/* Tree lowering pass. This pass converts the GENERIC functions-as-trees
tree representation into the GIMPLE form.
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ 2012 Free Software Foundation, Inc.
Major work done by Sebastian Pop <s.pop@laposte.net>,
Diego Novillo <dnovillo@redhat.com> and Jason Merrill <jason@redhat.com>.
&& TREE_CODE (x) != RESULT_DECL)
return;
TREE_ADDRESSABLE (x) = 1;
+
+ /* Also mark the artificial SSA_NAME that points to the partition of X. */
+ if (TREE_CODE (x) == VAR_DECL
+ && !DECL_EXTERNAL (x)
+ && !TREE_STATIC (x)
+ && cfun->gimple_df != NULL
+ && cfun->gimple_df->decls_to_pointers != NULL)
+ {
+ void *namep
+ = pointer_map_contains (cfun->gimple_df->decls_to_pointers, x);
+ if (namep)
+ TREE_ADDRESSABLE (*(tree *)namep) = 1;
+ }
}
/* Return a hash value for a formal temporary table entry. */
before the def/use vectors have been constructed. */
void
-gimplify_seq_add_stmt (gimple_seq *seq_p, gimple gs)
+gimple_seq_add_stmt_without_update (gimple_seq *seq_p, gimple gs)
{
gimple_stmt_iterator si;
gsi_insert_after_without_update (&si, gs, GSI_NEW_STMT);
}
+/* Shorter alias name for the above function for use in gimplify.c
+ only. */
+
+static inline void
+gimplify_seq_add_stmt (gimple_seq *seq_p, gimple gs)
+{
+ gimple_seq_add_stmt_without_update (seq_p, gs);
+}
+
/* Append sequence SRC to the end of sequence *DST_P. If *DST_P is
NULL, a new sequence is allocated. This function is
similar to gimple_seq_add_seq, but does not scan the operands.
static inline tree
create_tmp_from_val (tree val)
{
- return create_tmp_var (TREE_TYPE (val), get_name (val));
+ /* Drop all qualifiers and address-space information from the value type. */
+ return create_tmp_var (TYPE_MAIN_VARIANT (TREE_TYPE (val)), get_name (val));
}
/* Create a temporary to hold the value of VAL. If IS_FORMAL, try to reuse
way to go. */
/* Similar to copy_tree_r but do not copy SAVE_EXPR or TARGET_EXPR nodes.
- These nodes model computations that should only be done once. If we
- were to unshare something like SAVE_EXPR(i++), the gimplification
- process would create wrong code. */
+ These nodes model computations that must be done once. If we were to
+ unshare something like SAVE_EXPR(i++), the gimplification process would
+ create wrong code. However, if DATA is non-null, it must hold a pointer
+ set that is used to unshare the subtrees of these nodes. */
static tree
mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data)
return NULL_TREE;
}
-/* Callback for walk_tree to unshare most of the shared trees rooted at
- *TP. If *TP has been visited already (i.e., TREE_VISITED (*TP) == 1),
- then *TP is deep copied by calling mostly_copy_tree_r. */
+/* Callback for walk_tree to unshare most of the shared trees rooted at *TP.
+ If *TP has been visited already, then *TP is deeply copied by calling
+ mostly_copy_tree_r. DATA is passed to mostly_copy_tree_r unmodified. */
static tree
copy_if_shared_r (tree *tp, int *walk_subtrees, void *data)
return NULL_TREE;
}
-/* Unshare most of the shared trees rooted at *TP. */
+/* Unshare most of the shared trees rooted at *TP. DATA is passed to the
+ copy_if_shared_r callback unmodified. */
static inline void
-copy_if_shared (tree *tp)
+copy_if_shared (tree *tp, void *data)
{
- /* If the language requires deep unsharing, we need a pointer set to make
- sure we don't repeatedly unshare subtrees of unshareable nodes. */
- struct pointer_set_t *visited
- = lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
- walk_tree (tp, copy_if_shared_r, visited, NULL);
- if (visited)
- pointer_set_destroy (visited);
+ walk_tree (tp, copy_if_shared_r, data, NULL);
}
-/* Unshare all the trees in BODY_P, a pointer into the body of FNDECL, and the
- bodies of any nested functions if we are unsharing the entire body of
- FNDECL. */
+/* Unshare all the trees in the body of FNDECL, as well as in the bodies of
+ any nested functions. */
static void
-unshare_body (tree *body_p, tree fndecl)
+unshare_body (tree fndecl)
{
struct cgraph_node *cgn = cgraph_get_node (fndecl);
+ /* If the language requires deep unsharing, we need a pointer set to make
+ sure we don't repeatedly unshare subtrees of unshareable nodes. */
+ struct pointer_set_t *visited
+ = lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
- copy_if_shared (body_p);
+ copy_if_shared (&DECL_SAVED_TREE (fndecl), visited);
+ copy_if_shared (&DECL_SIZE (DECL_RESULT (fndecl)), visited);
+ copy_if_shared (&DECL_SIZE_UNIT (DECL_RESULT (fndecl)), visited);
- if (cgn && body_p == &DECL_SAVED_TREE (fndecl))
+ if (visited)
+ pointer_set_destroy (visited);
+
+ if (cgn)
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
- unshare_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
+ unshare_body (cgn->decl);
}
/* Callback for walk_tree to unmark the visited trees rooted at *TP.
/* Likewise, but mark all trees as not visited. */
static void
-unvisit_body (tree *body_p, tree fndecl)
+unvisit_body (tree fndecl)
{
struct cgraph_node *cgn = cgraph_get_node (fndecl);
- unmark_visited (body_p);
+ unmark_visited (&DECL_SAVED_TREE (fndecl));
+ unmark_visited (&DECL_SIZE (DECL_RESULT (fndecl)));
+ unmark_visited (&DECL_SIZE_UNIT (DECL_RESULT (fndecl)));
- if (cgn && body_p == &DECL_SAVED_TREE (fndecl))
+ if (cgn)
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
- unvisit_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
+ unvisit_body (cgn->decl);
}
/* Unconditionally make an unshared copy of EXPR. This is used when using
break;
default:
+ /* Assume that any tree upon which voidify_wrapper_expr is
+ directly called is a wrapper, and that its body is op0. */
+ if (p == &wrapper)
+ {
+ TREE_SIDE_EFFECTS (*p) = 1;
+ TREE_TYPE (*p) = void_type_node;
+ p = &TREE_OPERAND (*p, 0);
+ break;
+ }
goto out;
}
}
&& !DECL_HAS_VALUE_EXPR_P (t)
/* Only care for variables that have to be in memory. Others
will be rewritten into SSA names, hence moved to the top-level. */
- && needs_to_live_in_memory (t))
+ && !is_gimple_reg (t))
{
tree clobber = build_constructor (TREE_TYPE (t), NULL);
TREE_THIS_VOLATILE (clobber) = 1;
&& !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
gimplify_type_sizes (TREE_TYPE (decl), seq_p);
+ /* ??? DECL_ORIGINAL_TYPE is streamed for LTO so it needs to be gimplified
+ in case its size expressions contain problematic nodes like CALL_EXPR. */
+ if (TREE_CODE (decl) == TYPE_DECL
+ && DECL_ORIGINAL_TYPE (decl)
+ && !TYPE_SIZES_GIMPLIFIED (DECL_ORIGINAL_TYPE (decl)))
+ gimplify_type_sizes (DECL_ORIGINAL_TYPE (decl), seq_p);
+
if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
{
tree init = DECL_INITIAL (decl);
CALL_EXPR_RETURN_SLOT_OPT (*expr_p)
= CALL_EXPR_RETURN_SLOT_OPT (call);
CALL_FROM_THUNK_P (*expr_p) = CALL_FROM_THUNK_P (call);
- CALL_CANNOT_INLINE_P (*expr_p) = CALL_CANNOT_INLINE_P (call);
SET_EXPR_LOCATION (*expr_p, EXPR_LOCATION (call));
TREE_BLOCK (*expr_p) = TREE_BLOCK (call);
if (!TREE_ADDRESSABLE (value)
&& !TREE_ADDRESSABLE (decl)
- && init)
+ && init
+ && TREE_CODE (init) == CONSTRUCTOR)
newval = optimize_compound_literals_in_ctor (init);
}
if (newval == value)
/* It's OK to use the target directly if it's being
initialized. */
use_target = true;
- else if (!is_gimple_non_addressable (*to_p))
+ else if (variably_modified_type_p (TREE_TYPE (*to_p), NULL_TREE))
+ /* Always use the target and thus RSO for variable-sized types.
+ GIMPLE cannot deal with a variable-sized assignment
+ embedded in a call statement. */
+ use_target = true;
+ else if (TREE_CODE (*to_p) != SSA_NAME
+ && (!is_gimple_variable (*to_p)
+ || needs_to_live_in_memory (*to_p)))
/* Don't use the original target if it's already addressable;
if its address escapes, and the called function uses the
NRV optimization, a conforming program could see *to_p
any cleanups collected outside the CLEANUP_POINT_EXPR. */
int old_conds = gimplify_ctxp->conditions;
gimple_seq old_cleanups = gimplify_ctxp->conditional_cleanups;
+ bool old_in_cleanup_point_expr = gimplify_ctxp->in_cleanup_point_expr;
gimplify_ctxp->conditions = 0;
gimplify_ctxp->conditional_cleanups = NULL;
+ gimplify_ctxp->in_cleanup_point_expr = true;
gimplify_stmt (&TREE_OPERAND (*expr_p, 0), &body_sequence);
gimplify_ctxp->conditions = old_conds;
gimplify_ctxp->conditional_cleanups = old_cleanups;
+ gimplify_ctxp->in_cleanup_point_expr = old_in_cleanup_point_expr;
for (iter = gsi_start (body_sequence); !gsi_end_p (iter); )
{
/* Add a clobber for the temporary going out of scope, like
gimplify_bind_expr. */
- if (needs_to_live_in_memory (temp))
+ if (gimplify_ctxp->in_cleanup_point_expr
+ && needs_to_live_in_memory (temp))
{
tree clobber = build_constructor (TREE_TYPE (temp), NULL);
TREE_THIS_VOLATILE (clobber) = true;
ret = GS_OK;
break;
}
- ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
- is_gimple_mem_ref_addr, fb_rvalue);
- if (ret == GS_ERROR)
- break;
+ /* Avoid re-gimplifying the address operand if it is already
+ in suitable form. Re-gimplifying would mark the address
+ operand addressable. Always gimplify when not in SSA form
+ as we still may have to gimplify decls with value-exprs. */
+ if (!gimplify_ctxp || !gimplify_ctxp->into_ssa
+ || !is_gimple_mem_ref_addr (TREE_OPERAND (*expr_p, 0)))
+ {
+ ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ is_gimple_mem_ref_addr, fb_rvalue);
+ if (ret == GS_ERROR)
+ break;
+ }
recalculate_side_effects (*expr_p);
ret = GS_ALL_DONE;
break;
- /* Constants need not be gimplified. */
+ /* Constants need not be gimplified. */
case INTEGER_CST:
case REAL_CST:
case FIXED_CST:
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 (expr == NULL_TREE || TREE_CONSTANT (expr)
- || TREE_CODE (expr) == VAR_DECL
- || CONTAINS_PLACEHOLDER_P (expr))
+ if (is_gimple_sizepos (expr))
return;
type = TREE_TYPE (expr);
}
}
-/* Gimplify the body of statements pointed to by BODY_P and return a
- GIMPLE_BIND containing the sequence of GIMPLE statements
- corresponding to BODY_P. FNDECL is the function decl containing
- *BODY_P. */
+/* 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. */
gimple
-gimplify_body (tree *body_p, tree fndecl, bool do_parms)
+gimplify_body (tree fndecl, bool do_parms)
{
location_t saved_location = input_location;
gimple_seq parm_stmts, seq;
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 (body_p, fndecl);
- unvisit_body (body_p, fndecl);
+ unshare_body (fndecl);
+ unvisit_body (fndecl);
cgn = cgraph_get_node (fndecl);
if (cgn && cgn->origin)
/* 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;
+ parm_stmts = do_parms ? gimplify_parameters () : NULL;
/* Gimplify the function's body. */
seq = NULL;
- gimplify_stmt (body_p, &seq);
+ gimplify_stmt (&DECL_SAVED_TREE (fndecl), &seq);
outer_bind = gimple_seq_first_stmt (seq);
if (!outer_bind)
{
else
outer_bind = gimple_build_bind (NULL_TREE, seq, NULL);
- *body_p = NULL_TREE;
+ DECL_SAVED_TREE (fndecl) = NULL_TREE;
/* If we had callee-copies statements, insert them at the beginning
of the function and clear DECL_VALUE_EXPR_P on the parameters. */
&& !needs_to_live_in_memory (ret))
DECL_GIMPLE_REG_P (ret) = 1;
- bind = gimplify_body (&DECL_SAVED_TREE (fndecl), fndecl, true);
+ bind = gimplify_body (fndecl, true);
/* The tree body of the function is no longer needed, replace it
with the new GIMPLE body. */