/* Tree lowering pass. This pass converts the GENERIC functions-as-trees
tree representation into the GIMPLE form.
- Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005 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>.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
-#include "errors.h"
#include "varray.h"
#include "tree-gimple.h"
#include "tree-inline.h"
#include "output.h"
#include "expr.h"
#include "ggc.h"
+#include "toplev.h"
#include "target.h"
static struct gimplify_ctx
{
tree current_bind_expr;
- bool save_stack;
tree temps;
tree conditional_cleanups;
- int conditions;
tree exit_label;
tree return_temp;
- varray_type case_labels;
+ VEC(tree,heap) *case_labels;
/* The formal temporary table. Should this be persistent? */
htab_t temp_htab;
+ int conditions;
+ bool save_stack;
+ bool into_ssa;
} *gimplify_ctxp;
} elt_t;
/* Forward declarations. */
-static enum gimplify_status gimplify_modify_expr_rhs (tree *, tree *, tree *,
- tree *, tree *, bool);
static enum gimplify_status gimplify_compound_expr (tree *, tree *, bool);
+#ifdef ENABLE_CHECKING
+static bool cpt_same_type (tree a, tree b);
+#endif
/* Return a hash value for a formal temporary table entry. */
/* Only allow them to compare equal if they also hash equal; otherwise
results are nondeterminate, and we fail bootstrap comparison. */
- if (gimple_tree_hash (p1) != gimple_tree_hash (p2))
- abort ();
+ gcc_assert (gimple_tree_hash (p1) == gimple_tree_hash (p2));
return 1;
}
void
push_gimplify_context (void)
{
- if (gimplify_ctxp)
- abort ();
+ gcc_assert (!gimplify_ctxp);
gimplify_ctxp
= (struct gimplify_ctx *) xcalloc (1, sizeof (struct gimplify_ctx));
- gimplify_ctxp->temp_htab
- = htab_create (1000, gimple_tree_hash, gimple_tree_eq, free);
+ if (optimize)
+ gimplify_ctxp->temp_htab
+ = htab_create (1000, gimple_tree_hash, gimple_tree_eq, free);
+ else
+ gimplify_ctxp->temp_htab = NULL;
}
/* Tear down a context for the gimplifier. If BODY is non-null, then
void
pop_gimplify_context (tree body)
{
- if (!gimplify_ctxp || gimplify_ctxp->current_bind_expr)
- abort ();
+ tree t;
+
+ gcc_assert (gimplify_ctxp && !gimplify_ctxp->current_bind_expr);
+
+ for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
+ DECL_GIMPLE_FORMAL_TEMP_P (t) = 0;
if (body)
declare_tmp_vars (gimplify_ctxp->temps, body);
record_vars (gimplify_ctxp->temps);
#if 0
- if (!quiet_flag)
+ if (!quiet_flag && optimize)
fprintf (stderr, " collisions: %f ",
htab_collisions (gimplify_ctxp->temp_htab));
#endif
- htab_delete (gimplify_ctxp->temp_htab);
+ if (optimize)
+ htab_delete (gimplify_ctxp->temp_htab);
free (gimplify_ctxp);
gimplify_ctxp = NULL;
}
-void
+static void
gimple_push_bind_expr (tree bind)
{
TREE_CHAIN (bind) = gimplify_ctxp->current_bind_expr;
gimplify_ctxp->current_bind_expr = bind;
}
-void
+static void
gimple_pop_bind_expr (void)
{
gimplify_ctxp->current_bind_expr
static void
gimple_push_condition (void)
{
+#ifdef ENABLE_CHECKING
+ if (gimplify_ctxp->conditions == 0)
+ gcc_assert (!gimplify_ctxp->conditional_cleanups);
+#endif
++(gimplify_ctxp->conditions);
}
{
int conds = --(gimplify_ctxp->conditions);
+ gcc_assert (conds >= 0);
if (conds == 0)
{
append_to_statement_list (gimplify_ctxp->conditional_cleanups, pre_p);
gimplify_ctxp->conditional_cleanups = NULL_TREE;
}
- else if (conds < 0)
- abort ();
}
-/* A subroutine of append_to_statement_list{,_force}. */
+/* A subroutine of append_to_statement_list{,_force}. T is not NULL. */
static void
-append_to_statement_list_1 (tree t, tree *list_p, bool side_effects)
+append_to_statement_list_1 (tree t, tree *list_p)
{
tree list = *list_p;
tree_stmt_iterator i;
- if (!side_effects)
- return;
-
if (!list)
{
if (t && TREE_CODE (t) == STATEMENT_LIST)
tsi_link_after (&i, t, TSI_CONTINUE_LINKING);
}
-/* Add T to the end of the list container pointed by LIST_P.
+/* Add T to the end of the list container pointed to by LIST_P.
If T is an expression with no effects, it is ignored. */
void
append_to_statement_list (tree t, tree *list_p)
{
- append_to_statement_list_1 (t, list_p, t ? TREE_SIDE_EFFECTS (t) : false);
+ if (t && TREE_SIDE_EFFECTS (t))
+ append_to_statement_list_1 (t, list_p);
}
/* Similar, but the statement is always added, regardless of side effects. */
void
append_to_statement_list_force (tree t, tree *list_p)
{
- append_to_statement_list_1 (t, list_p, t != NULL);
+ if (t != NULL_TREE)
+ append_to_statement_list_1 (t, list_p);
}
/* Both gimplify the statement T and append it to LIST_P. */
tree lab = build_decl (LABEL_DECL, NULL_TREE, void_type_node);
DECL_ARTIFICIAL (lab) = 1;
+ DECL_IGNORED_P (lab) = 1;
DECL_CONTEXT (lab) = current_function_decl;
return lab;
}
+/* Subroutine for find_single_pointer_decl. */
+
+static tree
+find_single_pointer_decl_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+ void *data)
+{
+ tree *pdecl = (tree *) data;
+
+ if (DECL_P (*tp) && POINTER_TYPE_P (TREE_TYPE (*tp)))
+ {
+ if (*pdecl)
+ {
+ /* We already found a pointer decl; return anything other
+ than NULL_TREE to unwind from walk_tree signalling that
+ we have a duplicate. */
+ return *tp;
+ }
+ *pdecl = *tp;
+ }
+
+ return NULL_TREE;
+}
+
+/* Find the single DECL of pointer type in the tree T and return it.
+ If there are zero or more than one such DECLs, return NULL. */
+
+static tree
+find_single_pointer_decl (tree t)
+{
+ tree decl = NULL_TREE;
+
+ if (walk_tree (&t, find_single_pointer_decl_1, &decl, NULL))
+ {
+ /* find_single_pointer_decl_1 returns a non-zero value, causing
+ walk_tree to return a non-zero value, to indicate that it
+ found more than one pointer DECL. */
+ return NULL_TREE;
+ }
+
+ return decl;
+}
+
/* Create a new temporary name with PREFIX. Returns an identifier. */
static GTY(()) unsigned int tmp_var_id_num;
new_type = build_type_variant (type, 0, 0);
TYPE_ATTRIBUTES (new_type) = TYPE_ATTRIBUTES (type);
- tmp_var = build_decl (VAR_DECL, create_tmp_var_name (prefix), type);
+ tmp_var = build_decl (VAR_DECL, prefix ? create_tmp_var_name (prefix) : NULL,
+ type);
/* The variable was declared by the compiler. */
DECL_ARTIFICIAL (tmp_var) = 1;
{
tree tmp_var;
-#if defined ENABLE_CHECKING
/* We don't allow types that are addressable (meaning we can't make copies),
incomplete, or of variable size. */
- if (TREE_ADDRESSABLE (type)
- || !COMPLETE_TYPE_P (type)
- || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST)
- abort ();
-#endif
+ gcc_assert (!TREE_ADDRESSABLE (type)
+ && COMPLETE_TYPE_P (type)
+ && TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST);
tmp_var = create_tmp_var_raw (type, prefix);
gimple_add_tmp_var (tmp_var);
static inline tree
create_tmp_from_val (tree val)
{
- return create_tmp_var (TREE_TYPE (val), get_name (val));
+ 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
static tree
lookup_tmp_var (tree val, bool is_formal)
{
- if (!is_formal || TREE_SIDE_EFFECTS (val))
- return create_tmp_from_val (val);
+ tree ret;
+
+ /* If not optimizing, never really reuse a temporary. local-alloc
+ won't allocate any variable that is used in more than one basic
+ block, which means it will go into memory, causing much extra
+ work in reload and final and poorer code generation, outweighing
+ the extra memory allocation here. */
+ if (!optimize || !is_formal || TREE_SIDE_EFFECTS (val))
+ ret = create_tmp_from_val (val);
else
{
elt_t elt, *elt_p;
{
elt_p = xmalloc (sizeof (*elt_p));
elt_p->val = val;
- elt_p->temp = create_tmp_from_val (val);
- TREE_READONLY (elt_p->temp) = 1;
+ elt_p->temp = ret = create_tmp_from_val (val);
*slot = (void *) elt_p;
}
else
- elt_p = (elt_t *) *slot;
-
- return elt_p->temp;
+ {
+ elt_p = (elt_t *) *slot;
+ ret = elt_p->temp;
+ }
}
+
+ if (is_formal)
+ DECL_GIMPLE_FORMAL_TEMP_P (ret) = 1;
+
+ return ret;
}
/* Returns a formal temporary variable initialized with VAL. PRE_P is as
internal_get_tmp_var (tree val, tree *pre_p, tree *post_p, bool is_formal)
{
tree t, mod;
- char class;
- gimplify_expr (&val, pre_p, post_p, is_gimple_rhs, fb_rvalue);
+ gimplify_expr (&val, pre_p, post_p, is_gimple_formal_tmp_rhs, fb_rvalue);
t = lookup_tmp_var (val, is_formal);
+ if (is_formal)
+ {
+ tree u = find_single_pointer_decl (val);
+
+ if (u && TREE_CODE (u) == VAR_DECL && DECL_BASED_ON_RESTRICT_P (u))
+ u = DECL_GET_RESTRICT_BASE (u);
+ if (u && TYPE_RESTRICT (TREE_TYPE (u)))
+ {
+ if (DECL_BASED_ON_RESTRICT_P (t))
+ gcc_assert (u == DECL_GET_RESTRICT_BASE (t));
+ else
+ {
+ DECL_BASED_ON_RESTRICT_P (t) = 1;
+ SET_DECL_RESTRICT_BASE (t, u);
+ }
+ }
+ }
+
+ if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE)
+ DECL_COMPLEX_GIMPLE_REG_P (t) = 1;
+
mod = build (MODIFY_EXPR, TREE_TYPE (t), t, val);
- class = TREE_CODE_CLASS (TREE_CODE (val));
if (EXPR_HAS_LOCATION (val))
SET_EXPR_LOCUS (mod, EXPR_LOCUS (val));
else
/* gimplify_modify_expr might want to reduce this further. */
gimplify_and_add (mod, pre_p);
+
+ /* If we're gimplifying into ssa, gimplify_modify_expr will have
+ given our temporary an ssa name. Find and return it. */
+ if (gimplify_ctxp->into_ssa)
+ t = TREE_OPERAND (mod, 0);
+
return t;
}
return internal_get_tmp_var (val, pre_p, post_p, false);
}
-/* Returns true if T is a GIMPLE temporary variable, false otherwise. */
-
-bool
-is_gimple_tmp_var (tree t)
-{
- /* FIXME this could trigger for other local artificials, too. */
- return (TREE_CODE (t) == VAR_DECL && DECL_ARTIFICIAL (t)
- && !TREE_STATIC (t) && !DECL_EXTERNAL (t));
-}
-
/* Declares all the variables in VARS in SCOPE. */
void
while (TREE_CODE (scope) == COMPOUND_EXPR)
scope = TREE_OPERAND (scope, 0);
- if (TREE_CODE (scope) != BIND_EXPR)
- abort ();
+ gcc_assert (TREE_CODE (scope) == BIND_EXPR);
temps = nreverse (last);
TREE_CHAIN (last) = BIND_EXPR_VARS (scope);
void
gimple_add_tmp_var (tree tmp)
{
- if (TREE_CHAIN (tmp) || DECL_SEEN_IN_BIND_EXPR_P (tmp))
- abort ();
+ gcc_assert (!TREE_CHAIN (tmp) && !DECL_SEEN_IN_BIND_EXPR_P (tmp));
DECL_CONTEXT (tmp) = current_function_decl;
DECL_SEEN_IN_BIND_EXPR_P (tmp) = 1;
static void
annotate_one_with_locus (tree t, location_t locus)
{
- if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (t)))
- && ! EXPR_HAS_LOCATION (t)
- && should_carry_locus_p (t))
+ if (EXPR_P (t) && ! EXPR_HAS_LOCATION (t) && should_carry_locus_p (t))
SET_EXPR_LOCATION (t, locus);
}
{
tree t = tsi_stmt (i);
-#ifdef ENABLE_CHECKING
- /* Assuming we've already been gimplified, we shouldn't
- see nested chaining constructs anymore. */
- if (TREE_CODE (t) == STATEMENT_LIST
- || TREE_CODE (t) == COMPOUND_EXPR)
- abort ();
-#endif
+ /* Assuming we've already been gimplified, we shouldn't
+ see nested chaining constructs anymore. */
+ gcc_assert (TREE_CODE (t) != STATEMENT_LIST
+ && TREE_CODE (t) != COMPOUND_EXPR);
annotate_one_with_locus (t, locus);
}
{
enum tree_code code = TREE_CODE (*tp);
/* Don't unshare types, decls, constants and SAVE_EXPR nodes. */
- if (TREE_CODE_CLASS (code) == 't'
- || TREE_CODE_CLASS (code) == 'd'
- || TREE_CODE_CLASS (code) == 'c'
+ if (TREE_CODE_CLASS (code) == tcc_type
+ || TREE_CODE_CLASS (code) == tcc_declaration
+ || TREE_CODE_CLASS (code) == tcc_constant
|| code == SAVE_EXPR || code == TARGET_EXPR
/* We can't do anything sensible with a BLOCK used as an expression,
- but we also can't abort when we see it because of non-expression
+ but we also can't just die when we see it because of non-expression
uses. So just avert our eyes and cross our fingers. Silly Java. */
|| code == BLOCK)
*walk_subtrees = 0;
- else if (code == BIND_EXPR)
- abort ();
else
- copy_tree_r (tp, walk_subtrees, data);
-
- return NULL_TREE;
-}
-
-/* Mark all the _DECL nodes under *TP as volatile. FIXME: This must die
- after VA_ARG_EXPRs are properly lowered. */
-
-static tree
-mark_decls_volatile_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED)
-{
- if (SSA_VAR_P (*tp))
- TREE_THIS_VOLATILE (*tp) = 1;
+ {
+ gcc_assert (code != BIND_EXPR);
+ copy_tree_r (tp, walk_subtrees, 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 copy_tree_r.
types and the bounds of types. Mark them as visited so we properly
unmark their subtrees on the unmark pass. If we've already seen them,
don't look down further. */
- if (TREE_CODE_CLASS (code) == 't'
- || TREE_CODE_CLASS (code) == 'd'
- || TREE_CODE_CLASS (code) == 'c')
+ if (TREE_CODE_CLASS (code) == tcc_type
+ || TREE_CODE_CLASS (code) == tcc_declaration
+ || TREE_CODE_CLASS (code) == tcc_constant)
{
if (TREE_VISITED (t))
*walk_subtrees = 0;
/* Otherwise, mark the tree as visited and keep looking. */
else
- {
- TREE_VISITED (t) = 1;
- if (TREE_CODE (*tp) == VA_ARG_EXPR
- && targetm.calls.gimplify_va_arg_expr == NULL)
- {
- /* Mark any _DECL inside the operand as volatile to avoid
- the optimizers messing around with it. We have to do this
- early, otherwise we might mark a variable as volatile
- after we gimplify other statements that use the variable
- assuming it's not volatile. */
-
- /* FIXME once most targets define the above hook, this should
- go away (perhaps along with the #include "target.h"). */
- walk_tree (&TREE_OPERAND (*tp, 0), mark_decls_volatile_r,
- NULL, NULL);
- }
- }
+ TREE_VISITED (t) = 1;
return NULL_TREE;
}
/* Unshare T and all the trees reached from T via TREE_CHAIN. */
-void
+static void
unshare_all_trees (tree t)
{
walk_tree (&t, copy_if_shared_r, NULL, NULL);
return expr;
}
-/* A terser interface for building a representation of a exception
+/* A terser interface for building a representation of an exception
specification. */
tree
p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i);
}
else
- {
+ {
for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1))
{
TREE_SIDE_EFFECTS (*p) = 1;
/* Mark variables seen in this bind expr. */
for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t))
- DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
+ {
+ if (TREE_CODE (t) == VAR_DECL)
+ DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
+
+ /* Preliminarily mark non-addressed complex variables as eligible
+ for promotion to gimple registers. We'll transform their uses
+ as we find them. */
+ if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE
+ && !TREE_THIS_VOLATILE (t)
+ && (TREE_CODE (t) == VAR_DECL && !DECL_HARD_REGISTER (t))
+ && !needs_to_live_in_memory (t))
+ DECL_COMPLEX_GIMPLE_REG_P (t) = 1;
+ }
gimple_push_bind_expr (bind_expr);
gimplify_ctxp->save_stack = false;
tree ret_expr = TREE_OPERAND (stmt, 0);
tree result_decl, result;
- if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL)
+ if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL
+ || ret_expr == error_mark_node)
return GS_ALL_DONE;
- if (ret_expr == error_mark_node)
- return GS_ERROR;
-
if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
result_decl = NULL_TREE;
else
{
result_decl = TREE_OPERAND (ret_expr, 0);
-#ifdef ENABLE_CHECKING
- if ((TREE_CODE (ret_expr) != MODIFY_EXPR
- && TREE_CODE (ret_expr) != INIT_EXPR)
- || TREE_CODE (result_decl) != RESULT_DECL)
- abort ();
-#endif
+ if (TREE_CODE (result_decl) == INDIRECT_REF)
+ /* See through a return by reference. */
+ result_decl = TREE_OPERAND (result_decl, 0);
+
+ gcc_assert ((TREE_CODE (ret_expr) == MODIFY_EXPR
+ || TREE_CODE (ret_expr) == INIT_EXPR)
+ && TREE_CODE (result_decl) == RESULT_DECL);
}
/* If aggregate_value_p is true, then we can return the bare RESULT_DECL.
Recall that aggregate_value_p is FALSE for any aggregate type that is
returned in registers. If we're returning values in registers, then
we don't want to extend the lifetime of the RESULT_DECL, particularly
- across another call. In addition, for those aggregates for which
- hard_function_value generates a PARALLEL, we'll abort during normal
+ across another call. In addition, for those aggregates for which
+ hard_function_value generates a PARALLEL, we'll die during normal
expansion of structure assignments; there's special code in expand_return
to handle this case that does not exist in expand_expr. */
if (!result_decl
if (TREE_TYPE (decl) == error_mark_node)
return GS_ERROR;
- else if (TREE_CODE (decl) == TYPE_DECL)
+ if ((TREE_CODE (decl) == TYPE_DECL
+ || TREE_CODE (decl) == VAR_DECL)
+ && !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
gimplify_type_sizes (TREE_TYPE (decl), stmt_p);
- else if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
+ if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
{
tree init = DECL_INITIAL (decl);
/* This is a variable-sized decl. Simplify its size and mark it
for deferred expansion. Note that mudflap depends on the format
of the emitted code: see mx_register_decls(). */
- tree t, args;
+ tree t, args, addr, ptr_type;
- gimplify_type_sizes (TREE_TYPE (decl), stmt_p);
gimplify_one_sizepos (&DECL_SIZE (decl), stmt_p);
gimplify_one_sizepos (&DECL_SIZE_UNIT (decl), stmt_p);
+ /* All occurrences of this decl in final gimplified code will be
+ replaced by indirection. Setting DECL_VALUE_EXPR does two
+ things: First, it lets the rest of the gimplifier know what
+ replacement to use. Second, it lets the debug info know
+ where to find the value. */
+ ptr_type = build_pointer_type (TREE_TYPE (decl));
+ addr = create_tmp_var (ptr_type, get_name (decl));
+ DECL_IGNORED_P (addr) = 0;
+ t = build_fold_indirect_ref (addr);
+ SET_DECL_VALUE_EXPR (decl, t);
+ DECL_HAS_VALUE_EXPR_P (decl) = 1;
+
args = tree_cons (NULL, DECL_SIZE_UNIT (decl), NULL);
- t = build_fold_addr_expr (decl);
- args = tree_cons (NULL, t, args);
- t = implicit_built_in_decls[BUILT_IN_STACK_ALLOC];
+ t = built_in_decls[BUILT_IN_ALLOCA];
t = build_function_call_expr (t, args);
+ t = fold_convert (ptr_type, t);
+ t = build2 (MODIFY_EXPR, void_type_node, addr, t);
gimplify_and_add (t, stmt_p);
- DECL_DEFER_OUTPUT (decl) = 1;
+
+ /* Indicate that we need to restore the stack level when the
+ enclosing BIND_EXPR is exited. */
+ gimplify_ctxp->save_stack = true;
}
if (init && init != error_mark_node)
return tree_int_cst_compare (CASE_LOW (case1), CASE_LOW (case2));
}
-/* Sort the case labels in LABEL_VEC in ascending order. */
+/* Sort the case labels in LABEL_VEC in place in ascending order. */
void
sort_case_labels (tree label_vec)
if (SWITCH_BODY (switch_expr))
{
- varray_type labels, saved_labels;
+ VEC(tree,heap) *labels, *saved_labels;
tree label_vec, default_case = NULL_TREE;
size_t i, len;
/* If someone can be bothered to fill in the labels, they can
be bothered to null out the body too. */
- if (SWITCH_LABELS (switch_expr))
- abort ();
+ gcc_assert (!SWITCH_LABELS (switch_expr));
saved_labels = gimplify_ctxp->case_labels;
- VARRAY_TREE_INIT (gimplify_ctxp->case_labels, 8, "case_labels");
+ gimplify_ctxp->case_labels = VEC_alloc (tree, heap, 8);
gimplify_to_stmt_list (&SWITCH_BODY (switch_expr));
labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels = saved_labels;
- len = VARRAY_ACTIVE_SIZE (labels);
+ len = VEC_length (tree, labels);
for (i = 0; i < len; ++i)
{
- tree t = VARRAY_TREE (labels, i);
+ tree t = VEC_index (tree, labels, i);
if (!CASE_LOW (t))
{
/* The default case must be the last label in the list. */
default_case = t;
- VARRAY_TREE (labels, i) = VARRAY_TREE (labels, len - 1);
+ VEC_replace (tree, labels, i, VEC_index (tree, labels, len - 1));
len--;
break;
}
*expr_p = SWITCH_BODY (switch_expr);
for (i = 0; i < len; ++i)
- TREE_VEC_ELT (label_vec, i) = VARRAY_TREE (labels, i);
+ TREE_VEC_ELT (label_vec, i) = VEC_index (tree, labels, i);
TREE_VEC_ELT (label_vec, len) = default_case;
+ VEC_free (tree, heap, labels);
+
sort_case_labels (label_vec);
SWITCH_BODY (switch_expr) = NULL;
}
- else if (!SWITCH_LABELS (switch_expr))
- abort ();
+ else
+ gcc_assert (SWITCH_LABELS (switch_expr));
return ret;
}
gimplify_case_label_expr (tree *expr_p)
{
tree expr = *expr_p;
- if (gimplify_ctxp->case_labels)
- VARRAY_PUSH_TREE (gimplify_ctxp->case_labels, expr);
- else
- abort ();
+
+ gcc_assert (gimplify_ctxp->case_labels);
+ VEC_safe_push (tree, heap, gimplify_ctxp->case_labels, expr);
*expr_p = build (LABEL_EXPR, void_type_node, CASE_LABEL (expr));
return GS_ALL_DONE;
}
-/* Gimplify a LABELED_BLOCK_EXPR into a LABEL_EXPR following
- a (possibly empty) body. */
-
-static enum gimplify_status
-gimplify_labeled_block_expr (tree *expr_p)
-{
- tree body = LABELED_BLOCK_BODY (*expr_p);
- tree label = LABELED_BLOCK_LABEL (*expr_p);
- tree t;
-
- DECL_CONTEXT (label) = current_function_decl;
- t = build (LABEL_EXPR, void_type_node, label);
- if (body != NULL_TREE)
- t = build (COMPOUND_EXPR, void_type_node, body, t);
- *expr_p = t;
-
- return GS_OK;
-}
-
-/* Gimplify a EXIT_BLOCK_EXPR into a GOTO_EXPR. */
-
-static enum gimplify_status
-gimplify_exit_block_expr (tree *expr_p)
-{
- tree labeled_block = TREE_OPERAND (*expr_p, 0);
- tree label;
-
- /* First operand must be a LABELED_BLOCK_EXPR, which should
- already be lowered (or partially lowered) when we get here. */
-#if defined ENABLE_CHECKING
- if (TREE_CODE (labeled_block) != LABELED_BLOCK_EXPR)
- abort ();
-#endif
-
- label = LABELED_BLOCK_LABEL (labeled_block);
- *expr_p = build1 (GOTO_EXPR, void_type_node, label);
-
- return GS_OK;
-}
-
/* Build a GOTO to the LABEL_DECL pointed to by LABEL_P, building it first
if necessary. */
tree expr = *expr_p;
tree type;
- if (TREE_CODE (expr) != COMPONENT_REF)
- abort ();
+ gcc_assert (TREE_CODE (expr) == COMPONENT_REF);
if (INTEGRAL_TYPE_P (TREE_TYPE (expr)))
type = TREE_TYPE (get_unwidened (expr, NULL_TREE));
}
/* If a NOP conversion is changing a pointer to array of foo to a pointer
- to foo, embed that change in the ADDR_EXPR by converting
+ to foo, embed that change in the ADDR_EXPR by converting
T array[U];
(T *)&array
==>
return;
/* The lower bound and element sizes must be constant. */
- if (TREE_CODE (TYPE_SIZE_UNIT (dctype)) != INTEGER_CST
+ if (!TYPE_SIZE_UNIT (dctype)
+ || TREE_CODE (TYPE_SIZE_UNIT (dctype)) != INTEGER_CST
|| !TYPE_DOMAIN (datype) || !TYPE_MIN_VALUE (TYPE_DOMAIN (datype))
|| TREE_CODE (TYPE_MIN_VALUE (TYPE_DOMAIN (datype))) != INTEGER_CST)
return;
TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (dctype),
- size_int (TYPE_ALIGN (dctype)
- / BITS_PER_UNIT)));
+ size_int (TYPE_ALIGN_UNIT (dctype))));
*expr_p = build1 (ADDR_EXPR, ctype, *expr_p);
}
static enum gimplify_status
gimplify_conversion (tree *expr_p)
-{
- /* If we still have a conversion at the toplevel, then strip
- away all but the outermost conversion. */
- if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
- {
- STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
+{
+ gcc_assert (TREE_CODE (*expr_p) == NOP_EXPR
+ || TREE_CODE (*expr_p) == CONVERT_EXPR);
+
+ /* Then strip away all but the outermost conversion. */
+ STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
- /* And remove the outermost conversion if it's useless. */
- if (tree_ssa_useless_type_conversion (*expr_p))
- *expr_p = TREE_OPERAND (*expr_p, 0);
- }
+ /* And remove the outermost conversion if it's useless. */
+ if (tree_ssa_useless_type_conversion (*expr_p))
+ *expr_p = TREE_OPERAND (*expr_p, 0);
/* If we still have a conversion at the toplevel,
then canonicalize some constructs. */
return GS_OK;
}
-/* Reduce MIN/MAX_EXPR to a COND_EXPR for further gimplification. */
-
-static enum gimplify_status
-gimplify_minimax_expr (tree *expr_p, tree *pre_p, tree *post_p)
-{
- tree op1 = TREE_OPERAND (*expr_p, 0);
- tree op2 = TREE_OPERAND (*expr_p, 1);
- enum tree_code code;
- enum gimplify_status r0, r1;
-
- if (TREE_CODE (*expr_p) == MIN_EXPR)
- code = LE_EXPR;
- else
- code = GE_EXPR;
-
- r0 = gimplify_expr (&op1, pre_p, post_p, is_gimple_val, fb_rvalue);
- r1 = gimplify_expr (&op2, pre_p, post_p, is_gimple_val, fb_rvalue);
-
- *expr_p = build (COND_EXPR, TREE_TYPE (*expr_p),
- build (code, boolean_type_node, op1, op2),
- op1, op2);
-
- if (r0 == GS_ERROR || r1 == GS_ERROR)
- return GS_ERROR;
- else
- return GS_OK;
-}
-
-/* Subroutine of gimplify_compound_lval.
- Converts an ARRAY_REF to the equivalent *(&array + offset) form. */
+/* Gimplify a VAR_DECL or PARM_DECL. Returns GS_OK if we expanded a
+ DECL_VALUE_EXPR, and it's worth re-examining things. */
static enum gimplify_status
-gimplify_array_ref_to_plus (tree *expr_p, tree *pre_p, tree *post_p)
+gimplify_var_or_parm_decl (tree *expr_p)
{
- tree array = TREE_OPERAND (*expr_p, 0);
- tree arrtype = TREE_TYPE (array);
- tree elttype = TREE_TYPE (arrtype);
- tree size = array_ref_element_size (*expr_p);
- tree ptrtype = build_pointer_type (elttype);
- enum tree_code add_code = PLUS_EXPR;
- tree idx = TREE_OPERAND (*expr_p, 1);
- tree minidx = unshare_expr (array_ref_low_bound (*expr_p));
- tree offset, addr, result;
- enum gimplify_status ret;
-
- /* If the array domain does not start at zero, apply the offset. */
- if (!integer_zerop (minidx))
+ tree decl = *expr_p;
+
+ /* ??? If this is a local variable, and it has not been seen in any
+ outer BIND_EXPR, then it's probably the result of a duplicate
+ declaration, for which we've already issued an error. It would
+ be really nice if the front end wouldn't leak these at all.
+ Currently the only known culprit is C++ destructors, as seen
+ in g++.old-deja/g++.jason/binding.C. */
+ if (TREE_CODE (decl) == VAR_DECL
+ && !DECL_SEEN_IN_BIND_EXPR_P (decl)
+ && !TREE_STATIC (decl) && !DECL_EXTERNAL (decl)
+ && decl_function_context (decl) == current_function_decl)
{
- idx = convert (TREE_TYPE (minidx), idx);
- idx = fold (build (MINUS_EXPR, TREE_TYPE (minidx), idx, minidx));
+ gcc_assert (errorcount || sorrycount);
+ return GS_ERROR;
}
-
- /* If the index is negative -- a technically invalid situation now
- that we've biased the index back to zero -- then casting it to
- unsigned has ill effects. In particular, -1*4U/4U != -1.
- Represent this as a subtraction of a positive rather than addition
- of a negative. This will prevent any conversion back to ARRAY_REF
- from getting the wrong results from the division. */
- if (TREE_CODE (idx) == INTEGER_CST && tree_int_cst_sgn (idx) < 0)
+
+ /* If the decl is an alias for another expression, substitute it now. */
+ if (DECL_HAS_VALUE_EXPR_P (decl))
{
- idx = fold (build1 (NEGATE_EXPR, TREE_TYPE (idx), idx));
- add_code = MINUS_EXPR;
+ *expr_p = unshare_expr (DECL_VALUE_EXPR (decl));
+ return GS_OK;
}
- /* Pointer arithmetic must be done in sizetype. */
- idx = fold_convert (sizetype, idx);
-
- /* Convert the index to a byte offset. */
- offset = size_binop (MULT_EXPR, size, idx);
-
- ret = gimplify_expr (&array, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
- if (ret == GS_ERROR)
- return ret;
-
- addr = build_fold_addr_expr_with_type (array, ptrtype);
- result = fold (build (add_code, ptrtype, addr, offset));
- *expr_p = build1 (INDIRECT_REF, elttype, result);
-
- return GS_OK;
+ return GS_ALL_DONE;
}
+
/* Gimplify the COMPONENT_REF, ARRAY_REF, REALPART_EXPR or IMAGPART_EXPR
- node pointed by EXPR_P.
+ node pointed to by EXPR_P.
compound_lval
: min_lval '[' val ']'
tree *post_p, fallback_t fallback)
{
tree *p;
- varray_type stack;
+ VEC(tree,heap) *stack;
enum gimplify_status ret = GS_OK, tret;
int i;
-#if defined ENABLE_CHECKING
- if (TREE_CODE (*expr_p) != ARRAY_REF
- && TREE_CODE (*expr_p) != ARRAY_RANGE_REF
- && TREE_CODE (*expr_p) != COMPONENT_REF
- && TREE_CODE (*expr_p) != BIT_FIELD_REF
- && TREE_CODE (*expr_p) != REALPART_EXPR
- && TREE_CODE (*expr_p) != IMAGPART_EXPR)
- abort ();
-#endif
-
/* Create a stack of the subexpressions so later we can walk them in
order from inner to outer. */
- VARRAY_TREE_INIT (stack, 10, "stack");
+ stack = VEC_alloc (tree, heap, 10);
+
+ /* We can handle anything that get_inner_reference can deal with. */
+ for (p = expr_p; ; p = &TREE_OPERAND (*p, 0))
+ {
+ restart:
+ /* Fold INDIRECT_REFs now to turn them into ARRAY_REFs. */
+ if (TREE_CODE (*p) == INDIRECT_REF)
+ *p = fold_indirect_ref (*p);
+
+ if (handled_component_p (*p))
+ ;
+ /* Expand DECL_VALUE_EXPR now. In some cases that may expose
+ additional COMPONENT_REFs. */
+ else if ((TREE_CODE (*p) == VAR_DECL || TREE_CODE (*p) == PARM_DECL)
+ && gimplify_var_or_parm_decl (p) == GS_OK)
+ goto restart;
+ else
+ break;
+
+ VEC_safe_push (tree, heap, stack, *p);
+ }
- /* We can either handle REALPART_EXPR, IMAGEPART_EXPR anything that
- handled_components can deal with. */
- for (p = expr_p;
- (handled_component_p (*p)
- || TREE_CODE (*p) == REALPART_EXPR || TREE_CODE (*p) == IMAGPART_EXPR);
- p = &TREE_OPERAND (*p, 0))
- VARRAY_PUSH_TREE (stack, *p);
+ gcc_assert (VEC_length (tree, stack));
/* Now STACK is a stack of pointers to all the refs we've walked through
and P points to the innermost expression.
So we do this in three steps. First we deal with the annotations
for any variables in the components, then we gimplify the base,
then we gimplify any indices, from left to right. */
- for (i = VARRAY_ACTIVE_SIZE (stack) - 1; i >= 0; i--)
+ for (i = VEC_length (tree, stack) - 1; i >= 0; i--)
{
- tree t = VARRAY_TREE (stack, i);
+ tree t = VEC_index (tree, stack, i);
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
{
{
TREE_OPERAND (t, 2) = low;
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
- is_gimple_tmp_var, fb_rvalue);
+ is_gimple_formal_tmp_reg, fb_rvalue);
ret = MIN (ret, tret);
}
}
{
tree elmt_type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (t, 0)));
tree elmt_size = unshare_expr (array_ref_element_size (t));
- tree factor = size_int (TYPE_ALIGN (elmt_type) / BITS_PER_UNIT);
+ tree factor = size_int (TYPE_ALIGN_UNIT (elmt_type));
/* Divide the element size by the alignment of the element
type (above). */
{
TREE_OPERAND (t, 3) = elmt_size;
tret = gimplify_expr (&TREE_OPERAND (t, 3), pre_p, post_p,
- is_gimple_tmp_var, fb_rvalue);
+ is_gimple_formal_tmp_reg, fb_rvalue);
ret = MIN (ret, tret);
}
}
{
TREE_OPERAND (t, 2) = offset;
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
- is_gimple_tmp_var, fb_rvalue);
+ is_gimple_formal_tmp_reg, fb_rvalue);
ret = MIN (ret, tret);
}
}
}
}
- /* Step 2 is to gimplify the base expression. */
- tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval, fallback);
+ /* Step 2 is to gimplify the base expression. Make sure lvalue is set
+ so as to match the min_lval predicate. Failure to do so may result
+ in the creation of large aggregate temporaries. */
+ tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval,
+ fallback | fb_lvalue);
ret = MIN (ret, tret);
/* And finally, the indices and operands to BIT_FIELD_REF. During this
loop we also remove any useless conversions. */
- for (; VARRAY_ACTIVE_SIZE (stack) > 0; )
+ for (; VEC_length (tree, stack) > 0; )
{
- tree t = VARRAY_TOP_TREE (stack);
+ tree t = VEC_pop (tree, stack);
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
{
if (!is_gimple_min_invariant (TREE_OPERAND (t, 1)))
{
tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p,
- is_gimple_tmp_var, fb_rvalue);
+ is_gimple_formal_tmp_reg, fb_rvalue);
ret = MIN (ret, tret);
}
}
set which would have caused all the outer expressions in EXPR_P
leading to P to also have had TREE_SIDE_EFFECTS set. */
recalculate_side_effects (t);
- VARRAY_POP (stack);
}
tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval, fallback);
ret = MIN (ret, GS_OK);
}
+ VEC_free (tree, heap, stack);
+
return ret;
}
-/* Gimplify the self modifying expression pointed by EXPR_P (++, --, +=, -=).
+/* Gimplify the self modifying expression pointed to by EXPR_P
+ (++, --, +=, -=).
PRE_P points to the list where side effects that must happen before
*EXPR_P should be stored.
code = TREE_CODE (*expr_p);
-#if defined ENABLE_CHECKING
- if (code != POSTINCREMENT_EXPR
- && code != POSTDECREMENT_EXPR
- && code != PREINCREMENT_EXPR
- && code != PREDECREMENT_EXPR)
- abort ();
-#endif
+ gcc_assert (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR
+ || code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR);
/* Prefix or postfix? */
if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
}
}
-/* Gimplify the CALL_EXPR node pointed by EXPR_P. PRE_P points to the
+/* If *EXPR_P has a variable sized type, wrap it in a WITH_SIZE_EXPR. */
+
+static void
+maybe_with_size_expr (tree *expr_p)
+{
+ tree expr = *expr_p;
+ tree type = TREE_TYPE (expr);
+ tree size;
+
+ /* If we've already wrapped this or the type is error_mark_node, we can't do
+ anything. */
+ if (TREE_CODE (expr) == WITH_SIZE_EXPR
+ || type == error_mark_node)
+ return;
+
+ /* If the size isn't known or is a constant, we have nothing to do. */
+ size = TYPE_SIZE_UNIT (type);
+ if (!size || TREE_CODE (size) == INTEGER_CST)
+ return;
+
+ /* Otherwise, make a WITH_SIZE_EXPR. */
+ size = unshare_expr (size);
+ size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (size, expr);
+ *expr_p = build2 (WITH_SIZE_EXPR, type, expr, size);
+}
+
+/* Subroutine of gimplify_call_expr: Gimplify a single argument. */
+
+static enum gimplify_status
+gimplify_arg (tree *expr_p, tree *pre_p)
+{
+ bool (*test) (tree);
+ fallback_t fb;
+
+ /* In general, we allow lvalues for function arguments to avoid
+ extra overhead of copying large aggregates out of even larger
+ aggregates into temporaries only to copy the temporaries to
+ the argument list. Make optimizers happy by pulling out to
+ temporaries those types that fit in registers. */
+ if (is_gimple_reg_type (TREE_TYPE (*expr_p)))
+ test = is_gimple_val, fb = fb_rvalue;
+ else
+ test = is_gimple_lvalue, fb = fb_either;
+
+ /* If this is a variable sized type, we must remember the size. */
+ maybe_with_size_expr (expr_p);
+
+ /* There is a sequence point before a function call. Side effects in
+ the argument list must occur before the actual call. So, when
+ gimplifying arguments, force gimplify_expr to use an internal
+ post queue which is then appended to the end of PRE_P. */
+ return gimplify_expr (expr_p, pre_p, NULL, test, fb);
+}
+
+/* Gimplify the CALL_EXPR node pointed to by EXPR_P. PRE_P points to the
list where side effects that must happen before *EXPR_P should be stored.
WANT_VALUE is true if the result of the call is desired. */
tree arglist;
enum gimplify_status ret;
-#if defined ENABLE_CHECKING
- if (TREE_CODE (*expr_p) != CALL_EXPR)
- abort ();
-#endif
+ gcc_assert (TREE_CODE (*expr_p) == CALL_EXPR);
- /* For reliable diagnostics during inlining, it is necessary that
+ /* For reliable diagnostics during inlining, it is necessary that
every call_expr be annotated with file and line. */
if (! EXPR_HAS_LOCATION (*expr_p))
SET_EXPR_LOCATION (*expr_p, input_location);
decl = get_callee_fndecl (*expr_p);
if (decl && DECL_BUILT_IN (decl))
{
- tree new;
-
- /* If it is allocation of stack, record the need to restore the memory
- when the enclosing bind_expr is exited. */
- if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_ALLOC)
- gimplify_ctxp->save_stack = true;
-
- /* If it is restore of the stack, reset it, since it means we are
- regimplifying the bind_expr. Note that we use the fact that
- for try_finally_expr, try part is processed first. */
- if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_RESTORE)
- gimplify_ctxp->save_stack = false;
-
- new = simplify_builtin (*expr_p, !want_value);
+ tree fndecl = get_callee_fndecl (*expr_p);
+ tree arglist = TREE_OPERAND (*expr_p, 1);
+ tree new = fold_builtin (fndecl, arglist, !want_value);
if (new && new != *expr_p)
{
*expr_p = new;
return GS_OK;
}
+
+ if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (decl) == BUILT_IN_VA_START)
+ {
+ if (!arglist || !TREE_CHAIN (arglist))
+ {
+ error ("too few arguments to function %<va_start%>");
+ *expr_p = build_empty_stmt ();
+ return GS_OK;
+ }
+
+ if (fold_builtin_next_arg (TREE_CHAIN (arglist)))
+ {
+ *expr_p = build_empty_stmt ();
+ return GS_OK;
+ }
+ /* Avoid gimplifying the second argument to va_start, which needs
+ to be the plain PARM_DECL. */
+ return gimplify_arg (&TREE_VALUE (TREE_OPERAND (*expr_p, 1)), pre_p);
+ }
}
/* There is a sequence point before the call, so any side effects in
arglist = TREE_CHAIN (arglist))
{
enum gimplify_status t;
- bool (*test) (tree);
- fallback_t fb;
-
- /* In general, we allow lvalues for function arguments to avoid
- extra overhead of copying large aggregates out of even larger
- aggregates into temporaries only to copy the temporaries to
- the argument list. Make optimizers happy by pulling out to
- temporaries those types that fit in registers. */
- if (is_gimple_reg_type (TREE_TYPE (TREE_VALUE (arglist))))
- test = is_gimple_val, fb = fb_rvalue;
- else
- test = is_gimple_lvalue, fb = fb_either;
- /* There is a sequence point before a function call. Side effects in
- the argument list must occur before the actual call. So, when
- gimplifying arguments, force gimplify_expr to use an internal
- post queue which is then appended to the end of PRE_P. */
- t = gimplify_expr (&TREE_VALUE (arglist), pre_p, NULL, test, fb);
+ t = gimplify_arg (&TREE_VALUE (arglist), pre_p);
if (t == GS_ERROR)
ret = GS_ERROR;
/* Try this again in case gimplification exposed something. */
if (ret != GS_ERROR && decl && DECL_BUILT_IN (decl))
{
- tree new = simplify_builtin (*expr_p, !want_value);
+ tree fndecl = get_callee_fndecl (*expr_p);
+ tree arglist = TREE_OPERAND (*expr_p, 1);
+ tree new = fold_builtin (fndecl, arglist, !want_value);
if (new && new != *expr_p)
{
tree true_label, false_label, end_label, t;
tree *true_label_p;
tree *false_label_p;
- bool emit_end, emit_false;
+ bool emit_end, emit_false, jump_over_else;
bool then_se = then_ && TREE_SIDE_EFFECTS (then_);
bool else_se = else_ && TREE_SIDE_EFFECTS (else_);
{
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
then_ = shortcut_cond_expr (expr);
+ then_se = then_ && TREE_SIDE_EFFECTS (then_);
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, then_, NULL_TREE);
}
{
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
else_ = shortcut_cond_expr (expr);
+ else_se = else_ && TREE_SIDE_EFFECTS (else_);
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, NULL_TREE, else_);
}
emit_end = (end_label == NULL_TREE);
emit_false = (false_label == NULL_TREE);
+ /* We only emit the jump over the else clause if we have to--if the
+ then clause may fall through. Otherwise we can wind up with a
+ useless jump and a useless label at the end of gimplified code,
+ which will cause us to think that this conditional as a whole
+ falls through even if it doesn't. If we then inline a function
+ which ends with such a condition, that can cause us to issue an
+ inappropriate warning about control reaching the end of a
+ non-void function. */
+ jump_over_else = block_may_fallthru (then_);
+
pred = shortcut_cond_r (pred, true_label_p, false_label_p);
expr = NULL;
append_to_statement_list (then_, &expr);
if (else_se)
{
- t = build_and_jump (&end_label);
- append_to_statement_list (t, &expr);
+ if (jump_over_else)
+ {
+ t = build_and_jump (&end_label);
+ append_to_statement_list (t, &expr);
+ }
if (emit_false)
{
t = build1 (LABEL_EXPR, void_type_node, false_label);
if (TREE_CODE (type) == BOOLEAN_TYPE)
return expr;
- /* If this is the predicate of a COND_EXPR, it might not even be a
- truthvalue yet. */
- expr = lang_hooks.truthvalue_conversion (expr);
-
switch (TREE_CODE (expr))
{
case TRUTH_AND_EXPR:
/* These expressions always produce boolean results. */
TREE_TYPE (expr) = boolean_type_node;
return expr;
-
+
default:
/* Other expressions that get here must have boolean values, but
might need to be converted to the appropriate mode. */
}
}
-/* Convert the conditional expression pointed by EXPR_P '(p) ? a : b;'
+/* Convert the conditional expression pointed to by EXPR_P '(p) ? a : b;'
into
if (p) if (p)
TARGET is the tree for T1 above.
PRE_P points to the list where side effects that must happen before
- *EXPR_P should be stored. */
+ *EXPR_P should be stored.
+
+ POST_P points to the list where side effects that must happen after
+ *EXPR_P should be stored. */
static enum gimplify_status
-gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
+gimplify_cond_expr (tree *expr_p, tree *pre_p, tree *post_p, tree target,
+ fallback_t fallback)
{
tree expr = *expr_p;
- tree tmp, type;
+ tree tmp, tmp2, type;
enum gimplify_status ret;
type = TREE_TYPE (expr);
- if (!type)
- TREE_TYPE (expr) = void_type_node;
/* If this COND_EXPR has a value, copy the values into a temporary within
the arms. */
- else if (! VOID_TYPE_P (type))
+ if (! VOID_TYPE_P (type))
{
+ tree result;
+
if (target)
{
- tmp = target;
- ret = GS_OK;
+ ret = gimplify_expr (&target, pre_p, post_p,
+ is_gimple_min_lval, fb_lvalue);
+ if (ret != GS_ERROR)
+ ret = GS_OK;
+ result = tmp = target;
+ tmp2 = unshare_expr (target);
+ }
+ else if ((fallback & fb_lvalue) == 0)
+ {
+ result = tmp2 = tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
+ ret = GS_ALL_DONE;
}
else
{
- tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
+ tree type = build_pointer_type (TREE_TYPE (expr));
+
+ if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node)
+ TREE_OPERAND (expr, 1) =
+ build_fold_addr_expr (TREE_OPERAND (expr, 1));
+
+ if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
+ TREE_OPERAND (expr, 2) =
+ build_fold_addr_expr (TREE_OPERAND (expr, 2));
+
+ tmp2 = tmp = create_tmp_var (type, "iftmp");
+
+ expr = build (COND_EXPR, void_type_node, TREE_OPERAND (expr, 0),
+ TREE_OPERAND (expr, 1), TREE_OPERAND (expr, 2));
+
+ result = build_fold_indirect_ref (tmp);
ret = GS_ALL_DONE;
}
/* Build the else clause, 't1 = b;'. */
if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
TREE_OPERAND (expr, 2)
- = build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2));
+ = build (MODIFY_EXPR, void_type_node, tmp2, TREE_OPERAND (expr, 2));
TREE_TYPE (expr) = void_type_node;
recalculate_side_effects (expr);
- /* Move the COND_EXPR to the prequeue and use the temp in its place. */
+ /* Move the COND_EXPR to the prequeue. */
gimplify_and_add (expr, pre_p);
- *expr_p = tmp;
+ *expr_p = result;
return ret;
}
a call to __builtin_memcpy. */
static enum gimplify_status
-gimplify_modify_expr_to_memcpy (tree *expr_p, bool want_value)
+gimplify_modify_expr_to_memcpy (tree *expr_p, tree size, bool want_value)
{
tree args, t, to, to_ptr, from;
to = TREE_OPERAND (*expr_p, 0);
from = TREE_OPERAND (*expr_p, 1);
- t = TYPE_SIZE_UNIT (TREE_TYPE (from));
- t = unshare_expr (t);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, from);
- args = tree_cons (NULL, t, NULL);
+ args = tree_cons (NULL, size, NULL);
t = build_fold_addr_expr (from);
args = tree_cons (NULL, t, args);
a CONSTRUCTOR with an empty element list. */
static enum gimplify_status
-gimplify_modify_expr_to_memset (tree *expr_p, bool want_value)
+gimplify_modify_expr_to_memset (tree *expr_p, tree size, bool want_value)
{
tree args, t, to, to_ptr;
to = TREE_OPERAND (*expr_p, 0);
- t = TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (*expr_p, 1)));
- t = unshare_expr (t);
- t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
- args = tree_cons (NULL, t, NULL);
+ args = tree_cons (NULL, size, NULL);
args = tree_cons (NULL, integer_zero_node, args);
return GS_OK;
}
-/* A subroutine of gimplify_modify_expr. Break out elements of a
- CONSTRUCTOR used as an initializer into separate MODIFY_EXPRs.
-
- Note that we still need to clear any elements that don't have explicit
- initializers, so if not all elements are initialized we keep the
- original MODIFY_EXPR, we just remove all of the constructor elements. */
+/* A subroutine of gimplify_init_ctor_preeval. Called via walk_tree,
+ determine, cautiously, if a CONSTRUCTOR overlaps the lhs of an
+ assignment. Returns non-null if we detect a potential overlap. */
-static enum gimplify_status
-gimplify_init_constructor (tree *expr_p, tree *pre_p,
- tree *post_p, bool want_value)
+struct gimplify_init_ctor_preeval_data
{
- tree object = TREE_OPERAND (*expr_p, 0);
- tree ctor = TREE_OPERAND (*expr_p, 1);
- tree type = TREE_TYPE (ctor);
- enum gimplify_status ret;
- tree elt_list;
+ /* The base decl of the lhs object. May be NULL, in which case we
+ have to assume the lhs is indirect. */
+ tree lhs_base_decl;
- if (TREE_CODE (ctor) != CONSTRUCTOR)
- return GS_UNHANDLED;
+ /* The alias set of the lhs object. */
+ int lhs_alias_set;
+};
- elt_list = CONSTRUCTOR_ELTS (ctor);
+static tree
+gimplify_init_ctor_preeval_1 (tree *tp, int *walk_subtrees, void *xdata)
+{
+ struct gimplify_init_ctor_preeval_data *data
+ = (struct gimplify_init_ctor_preeval_data *) xdata;
+ tree t = *tp;
- ret = GS_ALL_DONE;
- switch (TREE_CODE (type))
- {
- case RECORD_TYPE:
- case UNION_TYPE:
- case QUAL_UNION_TYPE:
- case ARRAY_TYPE:
- {
- HOST_WIDE_INT i, num_elements, num_nonzero_elements;
- HOST_WIDE_INT num_nonconstant_elements;
- bool cleared;
+ /* If we find the base object, obviously we have overlap. */
+ if (data->lhs_base_decl == t)
+ return t;
- /* Aggregate types must lower constructors to initialization of
- individual elements. The exception is that a CONSTRUCTOR node
- with no elements indicates zero-initialization of the whole. */
- if (elt_list == NULL)
- {
- if (want_value)
- {
- *expr_p = object;
- return GS_OK;
- }
- else
- return GS_UNHANDLED;
- }
+ /* If the constructor component is indirect, determine if we have a
+ potential overlap with the lhs. The only bits of information we
+ have to go on at this point are addressability and alias sets. */
+ if (TREE_CODE (t) == INDIRECT_REF
+ && (!data->lhs_base_decl || TREE_ADDRESSABLE (data->lhs_base_decl))
+ && alias_sets_conflict_p (data->lhs_alias_set, get_alias_set (t)))
+ return t;
- categorize_ctor_elements (ctor, &num_nonzero_elements,
- &num_nonconstant_elements);
- num_elements = count_type_elements (TREE_TYPE (ctor));
+ if (IS_TYPE_OR_DECL_P (t))
+ *walk_subtrees = 0;
+ return NULL;
+}
- /* If a const aggregate variable is being initialized, then it
+/* A subroutine of gimplify_init_constructor. Pre-evaluate *EXPR_P,
+ force values that overlap with the lhs (as described by *DATA)
+ into temporaries. */
+
+static void
+gimplify_init_ctor_preeval (tree *expr_p, tree *pre_p, tree *post_p,
+ struct gimplify_init_ctor_preeval_data *data)
+{
+ enum gimplify_status one;
+
+ /* If the value is invariant, then there's nothing to pre-evaluate.
+ But ensure it doesn't have any side-effects since a SAVE_EXPR is
+ invariant but has side effects and might contain a reference to
+ the object we're initializing. */
+ if (TREE_INVARIANT (*expr_p) && !TREE_SIDE_EFFECTS (*expr_p))
+ return;
+
+ /* If the type has non-trivial constructors, we can't pre-evaluate. */
+ if (TREE_ADDRESSABLE (TREE_TYPE (*expr_p)))
+ return;
+
+ /* Recurse for nested constructors. */
+ if (TREE_CODE (*expr_p) == CONSTRUCTOR)
+ {
+ unsigned HOST_WIDE_INT ix;
+ constructor_elt *ce;
+ VEC(constructor_elt,gc) *v = CONSTRUCTOR_ELTS (*expr_p);
+
+ for (ix = 0; VEC_iterate (constructor_elt, v, ix, ce); ix++)
+ gimplify_init_ctor_preeval (&ce->value, pre_p, post_p, data);
+ return;
+ }
+
+ /* We can't preevaluate if the type contains a placeholder. */
+ if (type_contains_placeholder_p (TREE_TYPE (*expr_p)))
+ return;
+
+ /* Gimplify the constructor element to something appropriate for the rhs
+ of a MODIFY_EXPR. Given that we know the lhs is an aggregate, we know
+ the gimplifier will consider this a store to memory. Doing this
+ gimplification now means that we won't have to deal with complicated
+ language-specific trees, nor trees like SAVE_EXPR that can induce
+ exponential search behavior. */
+ one = gimplify_expr (expr_p, pre_p, post_p, is_gimple_mem_rhs, fb_rvalue);
+ if (one == GS_ERROR)
+ {
+ *expr_p = NULL;
+ return;
+ }
+
+ /* If we gimplified to a bare decl, we can be sure that it doesn't overlap
+ with the lhs, since "a = { .x=a }" doesn't make sense. This will
+ always be true for all scalars, since is_gimple_mem_rhs insists on a
+ temporary variable for them. */
+ if (DECL_P (*expr_p))
+ return;
+
+ /* If this is of variable size, we have no choice but to assume it doesn't
+ overlap since we can't make a temporary for it. */
+ if (!TREE_CONSTANT (TYPE_SIZE (TREE_TYPE (*expr_p))))
+ return;
+
+ /* Otherwise, we must search for overlap ... */
+ if (!walk_tree (expr_p, gimplify_init_ctor_preeval_1, data, NULL))
+ return;
+
+ /* ... and if found, force the value into a temporary. */
+ *expr_p = get_formal_tmp_var (*expr_p, pre_p);
+}
+
+/* A subroutine of gimplify_init_ctor_eval. Create a loop for
+ a RANGE_EXPR in a CONSTRUCTOR for an array.
+
+ var = lower;
+ loop_entry:
+ object[var] = value;
+ if (var == upper)
+ goto loop_exit;
+ var = var + 1;
+ goto loop_entry;
+ loop_exit:
+
+ We increment var _after_ the loop exit check because we might otherwise
+ fail if upper == TYPE_MAX_VALUE (type for upper).
+
+ Note that we never have to deal with SAVE_EXPRs here, because this has
+ already been taken care of for us, in gimplify_init_ctor_preeval(). */
+
+static void gimplify_init_ctor_eval (tree, VEC(constructor_elt,gc) *,
+ tree *, bool);
+
+static void
+gimplify_init_ctor_eval_range (tree object, tree lower, tree upper,
+ tree value, tree array_elt_type,
+ tree *pre_p, bool cleared)
+{
+ tree loop_entry_label, loop_exit_label;
+ tree var, var_type, cref;
+
+ loop_entry_label = create_artificial_label ();
+ loop_exit_label = create_artificial_label ();
+
+ /* Create and initialize the index variable. */
+ var_type = TREE_TYPE (upper);
+ var = create_tmp_var (var_type, NULL);
+ append_to_statement_list (build2 (MODIFY_EXPR, var_type, var, lower), pre_p);
+
+ /* Add the loop entry label. */
+ append_to_statement_list (build1 (LABEL_EXPR,
+ void_type_node,
+ loop_entry_label),
+ pre_p);
+
+ /* Build the reference. */
+ cref = build4 (ARRAY_REF, array_elt_type, unshare_expr (object),
+ var, NULL_TREE, NULL_TREE);
+
+ /* If we are a constructor, just call gimplify_init_ctor_eval to do
+ the store. Otherwise just assign value to the reference. */
+
+ if (TREE_CODE (value) == CONSTRUCTOR)
+ /* NB we might have to call ourself recursively through
+ gimplify_init_ctor_eval if the value is a constructor. */
+ gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
+ pre_p, cleared);
+ else
+ append_to_statement_list (build2 (MODIFY_EXPR, TREE_TYPE (cref),
+ cref, value),
+ pre_p);
+
+ /* We exit the loop when the index var is equal to the upper bound. */
+ gimplify_and_add (build3 (COND_EXPR, void_type_node,
+ build2 (EQ_EXPR, boolean_type_node,
+ var, upper),
+ build1 (GOTO_EXPR,
+ void_type_node,
+ loop_exit_label),
+ NULL_TREE),
+ pre_p);
+
+ /* Otherwise, increment the index var... */
+ append_to_statement_list (build2 (MODIFY_EXPR, var_type, var,
+ build2 (PLUS_EXPR, var_type, var,
+ fold_convert (var_type,
+ integer_one_node))),
+ pre_p);
+
+ /* ...and jump back to the loop entry. */
+ append_to_statement_list (build1 (GOTO_EXPR,
+ void_type_node,
+ loop_entry_label),
+ pre_p);
+
+ /* Add the loop exit label. */
+ append_to_statement_list (build1 (LABEL_EXPR,
+ void_type_node,
+ loop_exit_label),
+ pre_p);
+}
+
+/* Return true if FDECL is accessing a field that is zero sized. */
+
+static bool
+zero_sized_field_decl (tree fdecl)
+{
+ if (TREE_CODE (fdecl) == FIELD_DECL && DECL_SIZE (fdecl)
+ && integer_zerop (DECL_SIZE (fdecl)))
+ return true;
+ return false;
+}
+
+/* Return true if TYPE is zero sized. */
+
+static bool
+zero_sized_type (tree type)
+{
+ if (AGGREGATE_TYPE_P (type) && TYPE_SIZE (type)
+ && integer_zerop (TYPE_SIZE (type)))
+ return true;
+ return false;
+}
+
+/* A subroutine of gimplify_init_constructor. Generate individual
+ MODIFY_EXPRs for a CONSTRUCTOR. OBJECT is the LHS against which the
+ assignments should happen. ELTS is the CONSTRUCTOR_ELTS of the
+ CONSTRUCTOR. CLEARED is true if the entire LHS object has been
+ zeroed first. */
+
+static void
+gimplify_init_ctor_eval (tree object, VEC(constructor_elt,gc) *elts,
+ tree *pre_p, bool cleared)
+{
+ tree array_elt_type = NULL;
+ unsigned HOST_WIDE_INT ix;
+ tree purpose, value;
+
+ if (TREE_CODE (TREE_TYPE (object)) == ARRAY_TYPE)
+ array_elt_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
+
+ FOR_EACH_CONSTRUCTOR_ELT (elts, ix, purpose, value)
+ {
+ tree cref, init;
+
+ /* NULL values are created above for gimplification errors. */
+ if (value == NULL)
+ continue;
+
+ if (cleared && initializer_zerop (value))
+ continue;
+
+ /* ??? Here's to hoping the front end fills in all of the indices,
+ so we don't have to figure out what's missing ourselves. */
+ gcc_assert (purpose);
+
+ /* Skip zero-sized fields, unless value has side-effects. This can
+ happen with calls to functions returning a zero-sized type, which
+ we shouldn't discard. As a number of downstream passes don't
+ expect sets of zero-sized fields, we rely on the gimplification of
+ the MODIFY_EXPR we make below to drop the assignment statement. */
+ if (! TREE_SIDE_EFFECTS (value) && zero_sized_field_decl (purpose))
+ continue;
+
+ /* If we have a RANGE_EXPR, we have to build a loop to assign the
+ whole range. */
+ if (TREE_CODE (purpose) == RANGE_EXPR)
+ {
+ tree lower = TREE_OPERAND (purpose, 0);
+ tree upper = TREE_OPERAND (purpose, 1);
+
+ /* If the lower bound is equal to upper, just treat it as if
+ upper was the index. */
+ if (simple_cst_equal (lower, upper))
+ purpose = upper;
+ else
+ {
+ gimplify_init_ctor_eval_range (object, lower, upper, value,
+ array_elt_type, pre_p, cleared);
+ continue;
+ }
+ }
+
+ if (array_elt_type)
+ {
+ cref = build (ARRAY_REF, array_elt_type, unshare_expr (object),
+ purpose, NULL_TREE, NULL_TREE);
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (purpose) == FIELD_DECL);
+ cref = build (COMPONENT_REF, TREE_TYPE (purpose),
+ unshare_expr (object), purpose, NULL_TREE);
+ }
+
+ if (TREE_CODE (value) == CONSTRUCTOR
+ && TREE_CODE (TREE_TYPE (value)) != VECTOR_TYPE)
+ gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
+ pre_p, cleared);
+ else
+ {
+ init = build (MODIFY_EXPR, TREE_TYPE (cref), cref, value);
+ gimplify_and_add (init, pre_p);
+ }
+ }
+}
+
+/* A subroutine of gimplify_modify_expr. Break out elements of a
+ CONSTRUCTOR used as an initializer into separate MODIFY_EXPRs.
+
+ Note that we still need to clear any elements that don't have explicit
+ initializers, so if not all elements are initialized we keep the
+ original MODIFY_EXPR, we just remove all of the constructor elements. */
+
+static enum gimplify_status
+gimplify_init_constructor (tree *expr_p, tree *pre_p,
+ tree *post_p, bool want_value)
+{
+ tree object;
+ tree ctor = TREE_OPERAND (*expr_p, 1);
+ tree type = TREE_TYPE (ctor);
+ enum gimplify_status ret;
+ VEC(constructor_elt,gc) *elts;
+
+ if (TREE_CODE (ctor) != CONSTRUCTOR)
+ return GS_UNHANDLED;
+
+ ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ is_gimple_lvalue, fb_lvalue);
+ if (ret == GS_ERROR)
+ return ret;
+ object = TREE_OPERAND (*expr_p, 0);
+
+ elts = CONSTRUCTOR_ELTS (ctor);
+
+ ret = GS_ALL_DONE;
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ case ARRAY_TYPE:
+ {
+ struct gimplify_init_ctor_preeval_data preeval_data;
+ HOST_WIDE_INT num_type_elements, num_ctor_elements;
+ HOST_WIDE_INT num_nonzero_elements, num_nonconstant_elements;
+ bool cleared;
+
+ /* Aggregate types must lower constructors to initialization of
+ individual elements. The exception is that a CONSTRUCTOR node
+ with no elements indicates zero-initialization of the whole. */
+ if (VEC_empty (constructor_elt, elts))
+ break;
+
+ categorize_ctor_elements (ctor, &num_nonzero_elements,
+ &num_nonconstant_elements,
+ &num_ctor_elements, &cleared);
+
+ /* If a const aggregate variable is being initialized, then it
should never be a lose to promote the variable to be static. */
if (num_nonconstant_elements == 0
+ && num_nonzero_elements > 1
&& TREE_READONLY (object)
&& TREE_CODE (object) == VAR_DECL)
{
break;
}
+ /* If there are "lots" of initialized elements, even discounting
+ those that are not address constants (and thus *must* be
+ computed at runtime), then partition the constructor into
+ constant and non-constant parts. Block copy the constant
+ parts in, then generate code for the non-constant parts. */
+ /* TODO. There's code in cp/typeck.c to do this. */
+
+ num_type_elements = count_type_elements (type, true);
+
+ /* If count_type_elements could not determine number of type elements
+ for a constant-sized object, assume clearing is needed.
+ Don't do this for variable-sized objects, as store_constructor
+ will ignore the clearing of variable-sized objects. */
+ if (num_type_elements < 0 && int_size_in_bytes (type) >= 0)
+ cleared = true;
+ /* If there are "lots" of zeros, then block clear the object first. */
+ else if (num_type_elements - num_nonzero_elements > CLEAR_RATIO
+ && num_nonzero_elements < num_type_elements/4)
+ cleared = true;
+ /* ??? This bit ought not be needed. For any element not present
+ in the initializer, we should simply set them to zero. Except
+ we'd need to *find* the elements that are not present, and that
+ requires trickery to avoid quadratic compile-time behavior in
+ large cases or excessive memory use in small cases. */
+ else if (num_ctor_elements < num_type_elements)
+ cleared = true;
+
/* If there are "lots" of initialized elements, and all of them
are valid address constants, then the entire initializer can
- be dropped to memory, and then memcpy'd out. */
- if (num_nonconstant_elements == 0)
+ be dropped to memory, and then memcpy'd out. Don't do this
+ for sparse arrays, though, as it's more efficient to follow
+ the standard CONSTRUCTOR behavior of memset followed by
+ individual element initialization. */
+ if (num_nonconstant_elements == 0 && !cleared)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
unsigned int align;
if (size > 0 && !can_move_by_pieces (size, align))
{
tree new = create_tmp_var_raw (type, "C");
+
gimple_add_tmp_var (new);
TREE_STATIC (new) = 1;
TREE_READONLY (new) = 1;
walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
TREE_OPERAND (*expr_p, 1) = new;
- break;
- }
- }
-
- /* If there are "lots" of initialized elements, even discounting
- those that are not address constants (and thus *must* be
- computed at runtime), then partition the constructor into
- constant and non-constant parts. Block copy the constant
- parts in, then generate code for the non-constant parts. */
- /* TODO. There's code in cp/typeck.c to do this. */
-
- /* If there are "lots" of zeros, then block clear the object first. */
- cleared = false;
- if (num_elements - num_nonzero_elements > CLEAR_RATIO
- && num_nonzero_elements < num_elements/4)
- cleared = true;
- /* ??? This bit ought not be needed. For any element not present
- in the initializer, we should simply set them to zero. Except
- we'd need to *find* the elements that are not present, and that
- requires trickery to avoid quadratic compile-time behavior in
- large cases or excessive memory use in small cases. */
- else
- {
- HOST_WIDE_INT len = list_length (elt_list);
- if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree nelts = array_type_nelts (type);
- if (!host_integerp (nelts, 1)
- || tree_low_cst (nelts, 1) + 1 != len)
- cleared = 1;;
+ /* This is no longer an assignment of a CONSTRUCTOR, but
+ we still may have processing to do on the LHS. So
+ pretend we didn't do anything here to let that happen. */
+ return GS_UNHANDLED;
}
- else if (len != fields_length (type))
- cleared = 1;
}
if (cleared)
{
/* Zap the CONSTRUCTOR element list, which simplifies this case.
Note that we still have to gimplify, in order to handle the
- case of variable sized types. Make an unshared copy of
- OBJECT before that so we can match a PLACEHOLDER_EXPR to it
- later, if needed. */
- CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
- object = unshare_expr (TREE_OPERAND (*expr_p, 0));
+ case of variable sized types. Avoid shared tree structures. */
+ CONSTRUCTOR_ELTS (ctor) = NULL;
+ object = unshare_expr (object);
gimplify_stmt (expr_p);
append_to_statement_list (*expr_p, pre_p);
}
- for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
+ /* If we have not block cleared the object, or if there are nonzero
+ elements in the constructor, add assignments to the individual
+ scalar fields of the object. */
+ if (!cleared || num_nonzero_elements > 0)
{
- tree purpose, value, cref, init;
-
- purpose = TREE_PURPOSE (elt_list);
- value = TREE_VALUE (elt_list);
-
- if (cleared && initializer_zerop (value))
- continue;
-
- if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
-
- /* ??? Here's to hoping the front end fills in all of the
- indicies, so we don't have to figure out what's missing
- ourselves. */
- if (!purpose)
- abort ();
- /* ??? Need to handle this. */
- if (TREE_CODE (purpose) == RANGE_EXPR)
- abort ();
-
- cref = build (ARRAY_REF, t, unshare_expr (object), purpose,
- NULL_TREE, NULL_TREE);
- }
- else
- cref = build (COMPONENT_REF, TREE_TYPE (purpose),
- unshare_expr (object), purpose, NULL_TREE);
-
- init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
-
- /* Each member initialization is a full-expression. */
- gimplify_and_add (init, pre_p);
+ preeval_data.lhs_base_decl = get_base_address (object);
+ if (!DECL_P (preeval_data.lhs_base_decl))
+ preeval_data.lhs_base_decl = NULL;
+ preeval_data.lhs_alias_set = get_alias_set (object);
+
+ gimplify_init_ctor_preeval (&TREE_OPERAND (*expr_p, 1),
+ pre_p, post_p, &preeval_data);
+ gimplify_init_ctor_eval (object, elts, pre_p, cleared);
}
*expr_p = NULL_TREE;
tree r, i;
/* Extract the real and imaginary parts out of the ctor. */
- r = i = NULL_TREE;
- if (elt_list)
- {
- r = TREE_VALUE (elt_list);
- elt_list = TREE_CHAIN (elt_list);
- if (elt_list)
- {
- i = TREE_VALUE (elt_list);
- if (TREE_CHAIN (elt_list))
- abort ();
- }
- }
+ gcc_assert (VEC_length (constructor_elt, elts) == 2);
+ r = VEC_index (constructor_elt, elts, 0)->value;
+ i = VEC_index (constructor_elt, elts, 1)->value;
if (r == NULL || i == NULL)
{
tree zero = convert (TREE_TYPE (type), integer_zero_node);
ctor = build (COMPLEX_EXPR, type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
- is_gimple_rhs, fb_rvalue);
+ rhs_predicate_for (TREE_OPERAND (*expr_p, 0)),
+ fb_rvalue);
}
}
break;
case VECTOR_TYPE:
- /* Go ahead and simplify constant constructors to VECTOR_CST. */
- if (TREE_CONSTANT (ctor))
- TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
- else
- {
- /* Vector types use CONSTRUCTOR all the way through gimple
- compilation as a general initializer. */
- for (; elt_list; elt_list = TREE_CHAIN (elt_list))
- {
- enum gimplify_status tret;
- tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p,
- is_gimple_constructor_elt, fb_rvalue);
- if (tret == GS_ERROR)
- ret = GS_ERROR;
- }
- }
+ {
+ unsigned HOST_WIDE_INT ix;
+ constructor_elt *ce;
+
+ /* Go ahead and simplify constant constructors to VECTOR_CST. */
+ if (TREE_CONSTANT (ctor))
+ {
+ bool constant_p = true;
+ tree value;
+
+ /* Even when ctor is constant, it might contain non-*_CST
+ elements (e.g. { 1.0/0.0 - 1.0/0.0, 0.0 }) and those don't
+ belong into VECTOR_CST nodes. */
+ FOR_EACH_CONSTRUCTOR_VALUE (elts, ix, value)
+ if (!CONSTANT_CLASS_P (value))
+ {
+ constant_p = false;
+ break;
+ }
+
+ if (constant_p)
+ {
+ TREE_OPERAND (*expr_p, 1) = build_vector_from_ctor (type, elts);
+ break;
+ }
+ }
+
+ /* Vector types use CONSTRUCTOR all the way through gimple
+ compilation as a general initializer. */
+ for (ix = 0; VEC_iterate (constructor_elt, elts, ix, ce); ix++)
+ {
+ enum gimplify_status tret;
+ tret = gimplify_expr (&ce->value, pre_p, post_p,
+ is_gimple_val, fb_rvalue);
+ if (tret == GS_ERROR)
+ ret = GS_ERROR;
+ }
+ }
break;
default:
/* So how did we get a CONSTRUCTOR for a scalar type? */
- abort ();
+ gcc_unreachable ();
}
if (ret == GS_ERROR)
return GS_ALL_DONE;
}
+/* Given a pointer value OP0, return a simplified version of an
+ indirection through OP0, or NULL_TREE if no simplification is
+ possible. This may only be applied to a rhs of an expression.
+ Note that the resulting type may be different from the type pointed
+ to in the sense that it is still compatible from the langhooks
+ point of view. */
+
+static tree
+fold_indirect_ref_rhs (tree t)
+{
+ tree type = TREE_TYPE (TREE_TYPE (t));
+ tree sub = t;
+ tree subtype;
+
+ STRIP_NOPS (sub);
+ subtype = TREE_TYPE (sub);
+ if (!POINTER_TYPE_P (subtype))
+ return NULL_TREE;
+
+ if (TREE_CODE (sub) == ADDR_EXPR)
+ {
+ tree op = TREE_OPERAND (sub, 0);
+ tree optype = TREE_TYPE (op);
+ /* *&p => p */
+ if (lang_hooks.types_compatible_p (type, optype))
+ return op;
+ /* *(foo *)&fooarray => fooarray[0] */
+ else if (TREE_CODE (optype) == ARRAY_TYPE
+ && lang_hooks.types_compatible_p (type, TREE_TYPE (optype)))
+ {
+ tree type_domain = TYPE_DOMAIN (optype);
+ tree min_val = size_zero_node;
+ if (type_domain && TYPE_MIN_VALUE (type_domain))
+ min_val = TYPE_MIN_VALUE (type_domain);
+ return build4 (ARRAY_REF, type, op, min_val, NULL_TREE, NULL_TREE);
+ }
+ }
+
+ /* *(foo *)fooarrptr => (*fooarrptr)[0] */
+ if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE
+ && lang_hooks.types_compatible_p (type, TREE_TYPE (TREE_TYPE (subtype))))
+ {
+ tree type_domain;
+ tree min_val = size_zero_node;
+ tree osub = sub;
+ sub = fold_indirect_ref_rhs (sub);
+ if (! sub)
+ sub = build1 (INDIRECT_REF, TREE_TYPE (subtype), osub);
+ type_domain = TYPE_DOMAIN (TREE_TYPE (sub));
+ if (type_domain && TYPE_MIN_VALUE (type_domain))
+ min_val = TYPE_MIN_VALUE (type_domain);
+ return build4 (ARRAY_REF, type, sub, min_val, NULL_TREE, NULL_TREE);
+ }
+
+ return NULL_TREE;
+}
+
/* Subroutine of gimplify_modify_expr to do simplifications of MODIFY_EXPRs
based on the code of the RHS. We loop for as long as something changes. */
while (ret != GS_UNHANDLED)
switch (TREE_CODE (*from_p))
{
+ case INDIRECT_REF:
+ {
+ /* If we have code like
+
+ *(const A*)(A*)&x
+
+ where the type of "x" is a (possibly cv-qualified variant
+ of "A"), treat the entire expression as identical to "x".
+ This kind of code arises in C++ when an object is bound
+ to a const reference, and if "x" is a TARGET_EXPR we want
+ to take advantage of the optimization below. */
+ tree t = fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
+ if (t)
+ {
+ *from_p = t;
+ ret = GS_OK;
+ }
+ else
+ ret = GS_UNHANDLED;
+ break;
+ }
+
case TARGET_EXPR:
{
/* If we are initializing something from a TARGET_EXPR, strip the
??? What about code that pulls out the temp and uses it
elsewhere? I think that such code never uses the TARGET_EXPR as
- an initializer. If I'm wrong, we'll abort because the temp won't
+ an initializer. If I'm wrong, we'll die because the temp won't
have any RTL. In that case, I guess we'll need to replace
references somehow. */
tree init = TARGET_EXPR_INITIAL (*from_p);
return gimplify_init_constructor (expr_p, pre_p, post_p, want_value);
case COND_EXPR:
- /* If we're assigning from a ?: expression with ADDRESSABLE type, push
- the assignment down into the branches, since we can't generate a
- temporary of such a type. */
- if (TREE_ADDRESSABLE (TREE_TYPE (*from_p)))
+ /* If we're assigning to a non-register type, push the assignment
+ down into the branches. This is mandatory for ADDRESSABLE types,
+ since we cannot generate temporaries for such, but it saves a
+ copy in other cases as well. */
+ if (!is_gimple_reg_type (TREE_TYPE (*from_p)))
{
*expr_p = *from_p;
- return gimplify_cond_expr (expr_p, pre_p, *to_p);
+ return gimplify_cond_expr (expr_p, pre_p, post_p, *to_p,
+ fb_rvalue);
}
else
ret = GS_UNHANDLED;
break;
+ case CALL_EXPR:
+ /* For calls that return in memory, give *to_p as the CALL_EXPR's
+ return slot so that we don't generate a temporary. */
+ if (!CALL_EXPR_RETURN_SLOT_OPT (*from_p)
+ && aggregate_value_p (*from_p, *from_p))
+ {
+ bool use_target;
+
+ if (TREE_CODE (*to_p) == RESULT_DECL
+ && needs_to_live_in_memory (*to_p))
+ /* It's always OK to use the return slot directly. */
+ use_target = true;
+ else if (!is_gimple_non_addressable (*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
+ change before the called function returns; see c++/19317.
+ When optimizing, the return_slot pass marks more functions
+ as safe after we have escape info. */
+ use_target = false;
+ else if (TREE_CODE (*to_p) != PARM_DECL
+ && DECL_GIMPLE_FORMAL_TEMP_P (*to_p))
+ /* Don't use the original target if it's a formal temp; we
+ don't want to take their addresses. */
+ use_target = false;
+ else if (is_gimple_reg_type (TREE_TYPE (*to_p)))
+ /* Also don't force regs into memory. */
+ use_target = false;
+ else
+ use_target = true;
+
+ if (use_target)
+ {
+ CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
+ lang_hooks.mark_addressable (*to_p);
+ }
+ }
+
+ ret = GS_UNHANDLED;
+ break;
+
default:
ret = GS_UNHANDLED;
break;
return ret;
}
-/* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
+/* Promote partial stores to COMPLEX variables to total stores. *EXPR_P is
+ a MODIFY_EXPR with a lhs of a REAL/IMAGPART_EXPR of a variable with
+ DECL_COMPLEX_GIMPLE_REG_P set. */
+
+static enum gimplify_status
+gimplify_modify_expr_complex_part (tree *expr_p, tree *pre_p, bool want_value)
+{
+ enum tree_code code, ocode;
+ tree lhs, rhs, new_rhs, other, realpart, imagpart;
+
+ lhs = TREE_OPERAND (*expr_p, 0);
+ rhs = TREE_OPERAND (*expr_p, 1);
+ code = TREE_CODE (lhs);
+ lhs = TREE_OPERAND (lhs, 0);
+
+ ocode = code == REALPART_EXPR ? IMAGPART_EXPR : REALPART_EXPR;
+ other = build1 (ocode, TREE_TYPE (rhs), lhs);
+ other = get_formal_tmp_var (other, pre_p);
+
+ realpart = code == REALPART_EXPR ? rhs : other;
+ imagpart = code == REALPART_EXPR ? other : rhs;
+
+ if (TREE_CONSTANT (realpart) && TREE_CONSTANT (imagpart))
+ new_rhs = build_complex (TREE_TYPE (lhs), realpart, imagpart);
+ else
+ new_rhs = build2 (COMPLEX_EXPR, TREE_TYPE (lhs), realpart, imagpart);
+
+ TREE_OPERAND (*expr_p, 0) = lhs;
+ TREE_OPERAND (*expr_p, 1) = new_rhs;
+
+ if (want_value)
+ {
+ append_to_statement_list (*expr_p, pre_p);
+ *expr_p = rhs;
+ }
+
+ return GS_ALL_DONE;
+}
+
+/* Gimplify the MODIFY_EXPR node pointed to by EXPR_P.
modify_expr
: varname '=' rhs
tree *to_p = &TREE_OPERAND (*expr_p, 0);
enum gimplify_status ret = GS_UNHANDLED;
-#if defined ENABLE_CHECKING
- if (TREE_CODE (*expr_p) != MODIFY_EXPR && TREE_CODE (*expr_p) != INIT_EXPR)
- abort ();
-#endif
+ gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR
+ || TREE_CODE (*expr_p) == INIT_EXPR);
/* The distinction between MODIFY_EXPR and INIT_EXPR is no longer useful. */
if (TREE_CODE (*expr_p) == INIT_EXPR)
TREE_SET_CODE (*expr_p, MODIFY_EXPR);
+
+ /* For zero sized types only gimplify the left hand side and right hand side
+ as statements and throw away the assignment. */
+ if (zero_sized_type (TREE_TYPE (*from_p)))
+ {
+ gimplify_stmt (from_p);
+ gimplify_stmt (to_p);
+ append_to_statement_list (*from_p, pre_p);
+ append_to_statement_list (*to_p, pre_p);
+ *expr_p = NULL_TREE;
+ return GS_ALL_DONE;
+ }
/* See if any simplifications can be done based on what the RHS is. */
ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
if (ret != GS_UNHANDLED)
return ret;
- /* If the value being copied is of variable width, expose the length
- if the copy by converting the whole thing to a memcpy/memset.
- Note that we need to do this before gimplifying any of the operands
- so that we can resolve any PLACEHOLDER_EXPRs in the size.
- Also note that the RTL expander uses the size of the expression to
- be copied, not of the destination, so that is what we must here.
- The types on both sides of the MODIFY_EXPR should be the same,
- but they aren't always and there are problems with class-wide types
- in Ada where it's hard to make it "correct". */
- if (TREE_CODE (TREE_TYPE (*from_p)) != ERROR_MARK
- && TYPE_SIZE_UNIT (TREE_TYPE (*from_p))
- && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*from_p))) != INTEGER_CST)
- {
- if (TREE_CODE (*from_p) == CONSTRUCTOR)
- return gimplify_modify_expr_to_memset (expr_p, want_value);
- else
- return gimplify_modify_expr_to_memcpy (expr_p, want_value);
- }
+ /* If the value being copied is of variable width, compute the length
+ of the copy into a WITH_SIZE_EXPR. Note that we need to do this
+ before gimplifying any of the operands so that we can resolve any
+ PLACEHOLDER_EXPRs in the size. Also note that the RTL expander uses
+ the size of the expression to be copied, not of the destination, so
+ that is what we must here. */
+ maybe_with_size_expr (from_p);
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
- ret = gimplify_expr (from_p, pre_p, post_p, is_gimple_rhs, fb_rvalue);
+ ret = gimplify_expr (from_p, pre_p, post_p,
+ rhs_predicate_for (*to_p), fb_rvalue);
if (ret == GS_ERROR)
return ret;
if (ret != GS_UNHANDLED)
return ret;
- /* If the destination is already simple, nothing else needed. */
- if (is_gimple_tmp_var (*to_p))
- ret = GS_ALL_DONE;
- else
+ /* If we've got a variable sized assignment between two lvalues (i.e. does
+ not involve a call), then we can make things a bit more straightforward
+ by converting the assignment to memcpy or memset. */
+ if (TREE_CODE (*from_p) == WITH_SIZE_EXPR)
+ {
+ tree from = TREE_OPERAND (*from_p, 0);
+ tree size = TREE_OPERAND (*from_p, 1);
+
+ if (TREE_CODE (from) == CONSTRUCTOR)
+ return gimplify_modify_expr_to_memset (expr_p, size, want_value);
+ if (is_gimple_addressable (from))
+ {
+ *from_p = from;
+ return gimplify_modify_expr_to_memcpy (expr_p, size, want_value);
+ }
+ }
+
+ /* Transform partial stores to non-addressable complex variables into
+ total stores. This allows us to use real instead of virtual operands
+ for these variables, which improves optimization. */
+ if ((TREE_CODE (*to_p) == REALPART_EXPR
+ || TREE_CODE (*to_p) == IMAGPART_EXPR)
+ && is_gimple_reg (TREE_OPERAND (*to_p, 0)))
+ return gimplify_modify_expr_complex_part (expr_p, pre_p, want_value);
+
+ if (gimplify_ctxp->into_ssa && is_gimple_reg (*to_p))
{
- /* If the RHS of the MODIFY_EXPR may throw or make a nonlocal goto and
- the LHS is a user variable, then we need to introduce a temporary.
- ie temp = RHS; LHS = temp.
-
- This way the optimizers can determine that the user variable is
- only modified if evaluation of the RHS does not throw.
-
- FIXME this should be handled by the is_gimple_rhs predicate. */
-
- if (aggregate_value_p (TREE_TYPE (*from_p), NULL_TREE))
- /* Don't force a temp of a large aggregate type; the copy could be
- arbitrarily expensive. Instead we will generate a V_MAY_DEF for
- the assignment. */;
- else if (TREE_CODE (*from_p) == CALL_EXPR
- || (flag_non_call_exceptions && tree_could_trap_p (*from_p))
- /* If we're dealing with a renamable type, either source or dest
- must be a renamed variable. */
- || (is_gimple_reg_type (TREE_TYPE (*from_p))
- && !is_gimple_reg (*to_p)))
- gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
-
- ret = want_value ? GS_OK : GS_ALL_DONE;
+ /* If we've somehow already got an SSA_NAME on the LHS, then
+ we're probably modified it twice. Not good. */
+ gcc_assert (TREE_CODE (*to_p) != SSA_NAME);
+ *to_p = make_ssa_name (*to_p, *expr_p);
}
if (want_value)
{
append_to_statement_list (*expr_p, pre_p);
*expr_p = *to_p;
+ return GS_OK;
}
- return ret;
+ return GS_ALL_DONE;
}
/* Gimplify a comparison between two variable-sized objects. Do this
PRE_P points to the list where the side effects for all the
expressions in the sequence will be emitted.
-
+
WANT_VALUE is true when the result of the last COMPOUND_EXPR is used. */
/* ??? Should rearrange to share the pre-queue with all the indirect
- invocations of gimplify_expr. Would probably save on creations
+ invocations of gimplify_expr. Would probably save on creations
of statement_list nodes. */
static enum gimplify_status
enum gimplify_status ret = GS_ALL_DONE;
tree val;
-#if defined ENABLE_CHECKING
- if (TREE_CODE (*expr_p) != SAVE_EXPR)
- abort ();
-#endif
-
+ gcc_assert (TREE_CODE (*expr_p) == SAVE_EXPR);
val = TREE_OPERAND (*expr_p, 0);
- /* If the operand is already a GIMPLE temporary, just re-write the
- SAVE_EXPR node. */
- if (is_gimple_tmp_var (val))
- *expr_p = val;
- /* The operand may be a void-valued expression such as SAVE_EXPRs
- generated by the Java frontend for class initialization. It is
- being executed only for its side-effects. */
- else if (TREE_TYPE (val) == void_type_node)
+ /* If the SAVE_EXPR has not been resolved, then evaluate it once. */
+ if (!SAVE_EXPR_RESOLVED_P (*expr_p))
{
- tree body = TREE_OPERAND (*expr_p, 0);
- ret = gimplify_expr (& body, pre_p, post_p, is_gimple_stmt, fb_none);
- append_to_statement_list (body, pre_p);
- *expr_p = NULL;
+ /* The operand may be a void-valued expression such as SAVE_EXPRs
+ generated by the Java frontend for class initialization. It is
+ being executed only for its side-effects. */
+ if (TREE_TYPE (val) == void_type_node)
+ {
+ ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ is_gimple_stmt, fb_none);
+ append_to_statement_list (TREE_OPERAND (*expr_p, 0), pre_p);
+ val = NULL;
+ }
+ else
+ val = get_initialized_tmp_var (val, pre_p, post_p);
+
+ TREE_OPERAND (*expr_p, 0) = val;
+ SAVE_EXPR_RESOLVED_P (*expr_p) = 1;
}
- else
- *expr_p = TREE_OPERAND (*expr_p, 0)
- = get_initialized_tmp_var (val, pre_p, post_p);
+
+ *expr_p = val;
return ret;
}
-/* Re-write the ADDR_EXPR node pointed by EXPR_P
+/* Re-write the ADDR_EXPR node pointed to by EXPR_P
unary_expr
: ...
switch (TREE_CODE (op0))
{
case INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
+ do_indirect_ref:
/* Check if we are dealing with an expression of the form '&*ptr'.
While the front end folds away '&*ptr' into 'ptr', these
expressions may be generated internally by the compiler (e.g.,
builtins like __builtin_va_end). */
- *expr_p = TREE_OPERAND (op0, 0);
- ret = GS_OK;
- break;
-
- case ARRAY_REF:
- /* Fold &a[6] to (&a + 6). */
- ret = gimplify_array_ref_to_plus (&TREE_OPERAND (expr, 0),
- pre_p, post_p);
+ /* Caution: the silent array decomposition semantics we allow for
+ ADDR_EXPR means we can't always discard the pair. */
+ /* Gimplification of the ADDR_EXPR operand may drop
+ cv-qualification conversions, so make sure we add them if
+ needed. */
+ {
+ tree op00 = TREE_OPERAND (op0, 0);
+ tree t_expr = TREE_TYPE (expr);
+ tree t_op00 = TREE_TYPE (op00);
- /* This added an INDIRECT_REF. Fold it away. */
- *expr_p = TREE_OPERAND (TREE_OPERAND (expr, 0), 0);
+ if (!lang_hooks.types_compatible_p (t_expr, t_op00))
+ {
+#ifdef ENABLE_CHECKING
+ tree t_op0 = TREE_TYPE (op0);
+ gcc_assert (POINTER_TYPE_P (t_expr)
+ && cpt_same_type (TREE_CODE (t_op0) == ARRAY_TYPE
+ ? TREE_TYPE (t_op0) : t_op0,
+ TREE_TYPE (t_expr))
+ && POINTER_TYPE_P (t_op00)
+ && cpt_same_type (t_op0, TREE_TYPE (t_op00)));
+#endif
+ op00 = fold_convert (TREE_TYPE (expr), op00);
+ }
+ *expr_p = op00;
+ ret = GS_OK;
+ }
break;
case VIEW_CONVERT_EXPR:
??? The interactions of VIEW_CONVERT_EXPR and aliasing is not at
all clear. The impact of this transformation is even less clear. */
+
+ /* If the operand is a useless conversion, look through it. Doing so
+ guarantees that the ADDR_EXPR and its operand will remain of the
+ same type. */
+ if (tree_ssa_useless_type_conversion (TREE_OPERAND (op0, 0)))
+ op0 = TREE_OPERAND (op0, 0);
+
*expr_p = fold_convert (TREE_TYPE (expr),
build_fold_addr_expr (TREE_OPERAND (op0, 0)));
ret = GS_OK;
default:
/* We use fb_either here because the C frontend sometimes takes
- the address of a call that returns a struct. */
+ the address of a call that returns a struct; see
+ gcc.dg/c99-array-lval-1.c. The gimplifier will correctly make
+ the implied temporary explicit. */
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, post_p,
- is_gimple_addr_expr_arg, fb_either);
+ is_gimple_addressable, fb_either);
if (ret != GS_ERROR)
{
+ op0 = TREE_OPERAND (expr, 0);
+
+ /* For various reasons, the gimplification of the expression
+ may have made a new INDIRECT_REF. */
+ if (TREE_CODE (op0) == INDIRECT_REF)
+ goto do_indirect_ref;
+
/* Make sure TREE_INVARIANT, TREE_CONSTANT, and TREE_SIDE_EFFECTS
is set properly. */
recompute_tree_invarant_for_addr_expr (expr);
break;
}
- /* If the operand is gimplified into a _DECL, mark the address expression
- as TREE_INVARIANT. */
- if (DECL_P (TREE_OPERAND (expr, 0)))
- TREE_INVARIANT (expr) = 1;
-
return ret;
}
bool allows_mem, allows_reg, is_inout;
enum gimplify_status ret, tret;
- ASM_STRING (expr)
- = resolve_asm_operand_names (ASM_STRING (expr), ASM_OUTPUTS (expr),
- ASM_INPUTS (expr));
-
ret = GS_ALL_DONE;
for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = TREE_CHAIN (link))
{
+ size_t constraint_len;
oconstraints[i] = constraint
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ constraint_len = strlen (constraint);
+ if (constraint_len == 0)
+ continue;
parse_output_constraint (&constraint, i, 0, 0,
&allows_mem, &allows_reg, &is_inout);
operands. */
tree input;
char buf[10];
- size_t constraint_len = strlen (constraint);
/* Turn the in/out constraint into an output constraint. */
char *p = xstrdup (constraint);
p[0] = '=';
TREE_VALUE (TREE_PURPOSE (link)) = build_string (constraint_len, p);
- free (p);
/* And add a matching input constraint. */
if (allows_reg)
{
sprintf (buf, "%d", i);
- input = build_string (strlen (buf), buf);
+
+ /* If there are multiple alternatives in the constraint,
+ handle each of them individually. Those that allow register
+ will be replaced with operand number, the others will stay
+ unchanged. */
+ if (strchr (p, ',') != NULL)
+ {
+ size_t len = 0, buflen = strlen (buf);
+ char *beg, *end, *str, *dst;
+
+ for (beg = p + 1;;)
+ {
+ end = strchr (beg, ',');
+ if (end == NULL)
+ end = strchr (beg, '\0');
+ if ((size_t) (end - beg) < buflen)
+ len += buflen + 1;
+ else
+ len += end - beg + 1;
+ if (*end)
+ beg = end + 1;
+ else
+ break;
+ }
+
+ str = alloca (len);
+ for (beg = p + 1, dst = str;;)
+ {
+ const char *tem;
+ bool mem_p, reg_p, inout_p;
+
+ end = strchr (beg, ',');
+ if (end)
+ *end = '\0';
+ beg[-1] = '=';
+ tem = beg - 1;
+ parse_output_constraint (&tem, i, 0, 0,
+ &mem_p, ®_p, &inout_p);
+ if (dst != str)
+ *dst++ = ',';
+ if (reg_p)
+ {
+ memcpy (dst, buf, buflen);
+ dst += buflen;
+ }
+ else
+ {
+ if (end)
+ len = end - beg;
+ else
+ len = strlen (beg);
+ memcpy (dst, beg, len);
+ dst += len;
+ }
+ if (end)
+ beg = end + 1;
+ else
+ break;
+ }
+ *dst = '\0';
+ input = build_string (dst - str, str);
+ }
+ else
+ input = build_string (strlen (buf), buf);
}
else
input = build_string (constraint_len - 1, constraint + 1);
+
+ free (p);
+
input = build_tree_list (build_tree_list (NULL_TREE, input),
unshare_expr (TREE_VALUE (link)));
ASM_INPUTS (expr) = chainon (ASM_INPUTS (expr), input);
else
{
tret = gimplify_expr (&TREE_VALUE (link), pre_p, post_p,
- is_gimple_val, fb_rvalue);
+ is_gimple_asm_val, fb_rvalue);
if (tret == GS_ERROR)
ret = tret;
}
else
{
tree sl, tfe;
+ enum tree_code code;
+
+ if (CLEANUP_EH_ONLY (wce))
+ code = TRY_CATCH_EXPR;
+ else
+ code = TRY_FINALLY_EXPR;
sl = tsi_split_statement_list_after (&iter);
- tfe = build (TRY_FINALLY_EXPR, void_type_node, sl, NULL_TREE);
+ tfe = build (code, void_type_node, sl, NULL_TREE);
append_to_statement_list (TREE_OPERAND (wce, 0),
&TREE_OPERAND (tfe, 1));
*wce_p = tfe;
is the cleanup action required. */
static void
-gimple_push_cleanup (tree var, tree cleanup, tree *pre_p)
+gimple_push_cleanup (tree var, tree cleanup, bool eh_only, tree *pre_p)
{
tree wce;
else
{
wce = build (WITH_CLEANUP_EXPR, void_type_node, cleanup);
+ CLEANUP_EH_ONLY (wce) = eh_only;
append_to_statement_list (wce, pre_p);
}
ret = GS_OK;
if (TREE_CODE (init) == BIND_EXPR)
gimplify_bind_expr (&init, temp, pre_p);
- if (init != temp)
+ if (init != temp)
{
init = build (MODIFY_EXPR, void_type_node, temp, init);
ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt,
if (TARGET_EXPR_CLEANUP (targ))
{
gimplify_stmt (&TARGET_EXPR_CLEANUP (targ));
- gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ), pre_p);
+ gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ),
+ CLEANUP_EH_ONLY (targ), pre_p);
}
/* Only expand this once. */
TREE_OPERAND (targ, 3) = init;
TARGET_EXPR_INITIAL (targ) = NULL_TREE;
}
- else if (!DECL_SEEN_IN_BIND_EXPR_P (temp))
+ else
/* We should have expanded this before. */
- abort ();
+ gcc_assert (DECL_SEEN_IN_BIND_EXPR_P (temp));
*expr_p = temp;
return GS_OK;
}
-/* Gimplifies the expression tree pointed by EXPR_P. Return 0 if
+/* Gimplifies the expression tree pointed to by EXPR_P. Return 0 if
gimplification failed.
PRE_P points to the list where side effects that must happen before
case REALPART_EXPR:
case IMAGPART_EXPR:
case COMPONENT_REF:
+ case VIEW_CONVERT_EXPR:
ret = gimplify_compound_lval (expr_p, pre_p, post_p,
fallback ? fallback : fb_rvalue);
break;
case COND_EXPR:
- ret = gimplify_cond_expr (expr_p, pre_p, NULL_TREE);
+ ret = gimplify_cond_expr (expr_p, pre_p, post_p, NULL_TREE,
+ fallback);
+ /* C99 code may assign to an array in a structure value of a
+ conditional expression, and this has undefined behavior
+ only on execution, so create a temporary if an lvalue is
+ required. */
+ if (fallback == fb_lvalue)
+ {
+ *expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
+ lang_hooks.mark_addressable (*expr_p);
+ }
break;
case CALL_EXPR:
ret = gimplify_call_expr (expr_p, pre_p, fallback != fb_none);
+ /* C99 code may assign to an array in a structure returned
+ from a function, and this has undefined behavior only on
+ execution, so create a temporary if an lvalue is
+ required. */
+ if (fallback == fb_lvalue)
+ {
+ *expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
+ lang_hooks.mark_addressable (*expr_p);
+ }
break;
case TREE_LIST:
- abort ();
+ gcc_unreachable ();
case COMPOUND_EXPR:
ret = gimplify_compound_expr (expr_p, pre_p, fallback != fb_none);
ret = gimplify_va_arg_expr (expr_p, pre_p, post_p);
break;
- case VIEW_CONVERT_EXPR:
- if (VOID_TYPE_P (TREE_TYPE (*expr_p))
- || fallback == fb_none)
- {
- /* Just strip a conversion to void (or in void context) and
- try again. */
- *expr_p = TREE_OPERAND (*expr_p, 0);
- break;
- }
-
- /* If both types are BLKmode or if one type is of variable size,
- convert this into a pointer punning operation. This avoids
- copies of large data or making a variable-size temporary.
-
- ??? The interactions of VIEW_CONVERT_EXPR and aliasing is not at
- all clear. The impact of this transformation is even less
- clear. */
-
- if ((TYPE_MODE (TREE_TYPE (*expr_p)) == BLKmode
- && TYPE_MODE (TREE_TYPE (TREE_OPERAND (*expr_p, 0))) == BLKmode)
- || !TREE_CONSTANT (TYPE_SIZE (TREE_TYPE (*expr_p)))
- || !TREE_CONSTANT (TYPE_SIZE (TREE_TYPE
- (TREE_OPERAND (*expr_p,0)))))
- {
- tree restype = TREE_TYPE (*expr_p);
- *expr_p = build1 (INDIRECT_REF, TREE_TYPE (*expr_p),
- fold_convert (build_pointer_type (restype),
- build_fold_addr_expr
- (TREE_OPERAND (*expr_p, 0))));
- break;
- }
- goto unary;
-
case CONVERT_EXPR:
case NOP_EXPR:
if (IS_EMPTY_STMT (*expr_p))
case FIX_CEIL_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
- unary:
/* unary_expr: ... | '(' cast ')' val | ... */
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_val, fb_rvalue);
break;
case INDIRECT_REF:
+ *expr_p = fold_indirect_ref (*expr_p);
+ if (*expr_p != save_expr)
+ break;
+ /* else fall through. */
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_reg, fb_rvalue);
recalculate_side_effects (*expr_p);
break;
case CONST_DECL:
- *expr_p = DECL_INITIAL (*expr_p);
+ /* If we require an lvalue, such as for ADDR_EXPR, retain the
+ CONST_DECL node. Otherwise the decl is replaceable by its
+ value. */
+ /* ??? Should be == fb_lvalue, but ADDR_EXPR passes fb_either. */
+ if (fallback & fb_lvalue)
+ ret = GS_ALL_DONE;
+ else
+ *expr_p = DECL_INITIAL (*expr_p);
break;
case DECL_EXPR:
ret = gimplify_switch_expr (expr_p, pre_p);
break;
- case LABELED_BLOCK_EXPR:
- ret = gimplify_labeled_block_expr (expr_p);
- break;
-
- case EXIT_BLOCK_EXPR:
- ret = gimplify_exit_block_expr (expr_p);
- break;
-
case EXIT_EXPR:
ret = gimplify_exit_expr (expr_p);
break;
case LABEL_EXPR:
ret = GS_ALL_DONE;
-#ifdef ENABLE_CHECKING
- if (decl_function_context (LABEL_EXPR_LABEL (*expr_p)) != current_function_decl)
- abort ();
-#endif
+ gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
+ == current_function_decl);
break;
case CASE_LABEL_EXPR:
gimplify any element that has side-effects. */
if (fallback == fb_none)
{
- for (tmp = CONSTRUCTOR_ELTS (*expr_p); tmp;
- tmp = TREE_CHAIN (tmp))
- if (TREE_SIDE_EFFECTS (TREE_VALUE (tmp)))
- gimplify_expr (&TREE_VALUE (tmp), pre_p, post_p,
- gimple_test_f, fallback);
-
- *expr_p = NULL_TREE;
+ unsigned HOST_WIDE_INT ix;
+ constructor_elt *ce;
+ tree temp = NULL_TREE;
+ for (ix = 0;
+ VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (*expr_p),
+ ix, ce);
+ ix++)
+ if (TREE_SIDE_EFFECTS (ce->value))
+ append_to_statement_list (ce->value, &temp);
+
+ *expr_p = temp;
+ ret = GS_OK;
}
-
- ret = GS_ALL_DONE;
+ /* C99 code may assign to an array in a constructed
+ structure or union, and this has undefined behavior only
+ on execution, so create a temporary if an lvalue is
+ required. */
+ else if (fallback == fb_lvalue)
+ {
+ *expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
+ lang_hooks.mark_addressable (*expr_p);
+ }
+ else
+ ret = GS_ALL_DONE;
break;
/* The following are special cases that are not handled by the
case NON_LVALUE_EXPR:
/* This should have been stripped above. */
- abort ();
- break;
+ gcc_unreachable ();
case ASM_EXPR:
ret = gimplify_asm_expr (expr_p, pre_p, post_p);
}
break;
- case MIN_EXPR:
- case MAX_EXPR:
- ret = gimplify_minimax_expr (expr_p, pre_p, post_p);
- break;
-
case LABEL_DECL:
/* We get here when taking the address of a label. We mark
the label as "forced"; meaning it can never be removed and
ret = gimplify_statement_list (expr_p);
break;
+ case WITH_SIZE_EXPR:
+ {
+ gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
+ post_p == &internal_post ? NULL : post_p,
+ gimple_test_f, fallback);
+ gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
+ is_gimple_val, fb_rvalue);
+ }
+ break;
+
case VAR_DECL:
- /* ??? If this is a local variable, and it has not been seen in any
- outer BIND_EXPR, then it's probably the result of a duplicate
- declaration, for which we've already issued an error. It would
- be really nice if the front end wouldn't leak these at all.
- Currently the only known culprit is C++ destructors, as seen
- in g++.old-deja/g++.jason/binding.C. */
- tmp = *expr_p;
- if (!TREE_STATIC (tmp) && !DECL_EXTERNAL (tmp)
- && decl_function_context (tmp) == current_function_decl
- && !DECL_SEEN_IN_BIND_EXPR_P (tmp))
- {
-#ifdef ENABLE_CHECKING
- if (!errorcount && !sorrycount)
- abort ();
-#endif
- ret = GS_ERROR;
- }
- else
- ret = GS_ALL_DONE;
+ case PARM_DECL:
+ ret = gimplify_var_or_parm_decl (expr_p);
break;
case SSA_NAME:
break;
default:
- /* If this is a comparison of objects of aggregate type, handle
- it specially (by converting to a call to memcmp). It would be
- nice to only have to do this for variable-sized objects, but
- then we'd have to allow the same nest of reference nodes we
- allow for MODIFY_EXPR and that's too complex. */
- if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '<'
- && (AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (*expr_p, 1)))))
- ret = gimplify_variable_sized_compare (expr_p);
-
- /* If *EXPR_P does not need to be special-cased, handle it
- according to its class. */
- else if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '1')
- ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
- post_p, is_gimple_val, fb_rvalue);
- else if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '2'
- || TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '<'
- || TREE_CODE (*expr_p) == TRUTH_AND_EXPR
- || TREE_CODE (*expr_p) == TRUTH_OR_EXPR
- || TREE_CODE (*expr_p) == TRUTH_XOR_EXPR)
+ switch (TREE_CODE_CLASS (TREE_CODE (*expr_p)))
{
- enum gimplify_status r0, r1;
+ case tcc_comparison:
+ /* If this is a comparison of objects of aggregate type,
+ handle it specially (by converting to a call to
+ memcmp). It would be nice to only have to do this
+ for variable-sized objects, but then we'd have to
+ allow the same nest of reference nodes we allow for
+ MODIFY_EXPR and that's too complex. */
+ if (!AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (*expr_p, 1))))
+ goto expr_2;
+ ret = gimplify_variable_sized_compare (expr_p);
+ break;
- r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
- post_p, is_gimple_val, fb_rvalue);
- r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p,
- post_p, is_gimple_val, fb_rvalue);
+ /* If *EXPR_P does not need to be special-cased, handle it
+ according to its class. */
+ case tcc_unary:
+ ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
+ post_p, is_gimple_val, fb_rvalue);
+ break;
- ret = MIN (r0, r1);
- }
- else if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == 'd'
- || TREE_CODE_CLASS (TREE_CODE (*expr_p)) == 'c')
- {
+ case tcc_binary:
+ expr_2:
+ {
+ enum gimplify_status r0, r1;
+
+ r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
+ post_p, is_gimple_val, fb_rvalue);
+ r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p,
+ post_p, is_gimple_val, fb_rvalue);
+
+ ret = MIN (r0, r1);
+ break;
+ }
+
+ case tcc_declaration:
+ case tcc_constant:
ret = GS_ALL_DONE;
- break;
+ goto dont_recalculate;
+
+ default:
+ gcc_assert (TREE_CODE (*expr_p) == TRUTH_AND_EXPR
+ || TREE_CODE (*expr_p) == TRUTH_OR_EXPR
+ || TREE_CODE (*expr_p) == TRUTH_XOR_EXPR);
+ goto expr_2;
}
- else
- /* Fail if we don't know how to handle this tree code. */
- abort ();
recalculate_side_effects (*expr_p);
+ dont_recalculate:
break;
}
goto out;
}
-#ifdef ENABLE_CHECKING
/* This was only valid as a return value from the langhook, which
we handled. Make sure it doesn't escape from any other context. */
- if (ret == GS_UNHANDLED)
- abort ();
-#endif
+ gcc_assert (ret != GS_UNHANDLED);
if (fallback == fb_none && *expr_p && !is_gimple_stmt (*expr_p))
{
has side effects. Recurse through the operands to find it. */
enum tree_code code = TREE_CODE (*expr_p);
- if (code == COMPONENT_REF
- || code == REALPART_EXPR || code == IMAGPART_EXPR)
- gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
- gimple_test_f, fallback);
- else if (code == ARRAY_REF || code == ARRAY_RANGE_REF)
+ switch (code)
{
+ case COMPONENT_REF:
+ case REALPART_EXPR: case IMAGPART_EXPR:
+ gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ gimple_test_f, fallback);
+ break;
+
+ case ARRAY_REF: case ARRAY_RANGE_REF:
gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
gimple_test_f, fallback);
gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
- gimple_test_f, fallback);
+ gimple_test_f, fallback);
+ break;
+
+ default:
+ /* Anything else with side-effects must be converted to
+ a valid statement before we get here. */
+ gcc_unreachable ();
}
- else
- /* Anything else with side-effects
- must be converted to a valid statement before we get here. */
- abort ();
*expr_p = NULL;
}
{
/* Historically, the compiler has treated a bare
reference to a volatile lvalue as forcing a load. */
- tree tmp = create_tmp_var (TREE_TYPE (*expr_p), "vol");
- *expr_p = build (MODIFY_EXPR, TREE_TYPE (tmp), tmp, *expr_p);
+ tree type = TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p));
+ /* Normally, we do want to create a temporary for a
+ TREE_ADDRESSABLE type because such a type should not be
+ copied by bitwise-assignment. However, we make an
+ exception here, as all we are doing here is ensuring that
+ we read the bytes that make up the type. We use
+ create_tmp_var_raw because create_tmp_var will abort when
+ given a TREE_ADDRESSABLE type. */
+ tree tmp = create_tmp_var_raw (type, "vol");
+ gimple_add_tmp_var (tmp);
+ *expr_p = build (MODIFY_EXPR, type, tmp, *expr_p);
}
else
/* We can't do anything useful with a volatile reference to
postqueue; we need to copy the value out first, which means an
rvalue. */
if ((fallback & fb_lvalue) && !internal_post
- && is_gimple_addr_expr_arg (*expr_p))
+ && is_gimple_addressable (*expr_p))
{
/* An lvalue will do. Take the address of the expression, store it
in a temporary, and replace the expression with an INDIRECT_REF of
gimplify_expr (&tmp, pre_p, post_p, is_gimple_reg, fb_rvalue);
*expr_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (tmp)), tmp);
}
- else if ((fallback & fb_rvalue) && is_gimple_rhs (*expr_p))
+ else if ((fallback & fb_rvalue) && is_gimple_formal_tmp_rhs (*expr_p))
{
-#if defined ENABLE_CHECKING
- if (VOID_TYPE_P (TREE_TYPE (*expr_p)))
- abort ();
-#endif
+ gcc_assert (!VOID_TYPE_P (TREE_TYPE (*expr_p)));
/* An rvalue will do. Assign the gimplified expression into a new
temporary TMP and replace the original expression with TMP. */
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
else
*expr_p = get_formal_tmp_var (*expr_p, pre_p);
+
+ if (TREE_CODE (*expr_p) != SSA_NAME)
+ DECL_GIMPLE_FORMAL_TEMP_P (*expr_p) = 1;
}
- else if (fallback & fb_mayfail)
+ else
{
- /* If this is an asm statement, and the user asked for the impossible,
- don't abort. Fail and let gimplify_asm_expr issue an error. */
+#ifdef ENABLE_CHECKING
+ if (!(fallback & fb_mayfail))
+ {
+ fprintf (stderr, "gimplification failed:\n");
+ print_generic_expr (stderr, *expr_p, 0);
+ debug_tree (*expr_p);
+ internal_error ("gimplification failed");
+ }
+#endif
+ gcc_assert (fallback & fb_mayfail);
+ /* If this is an asm statement, and the user asked for the
+ impossible, don't die. Fail and let gimplify_asm_expr
+ issue an error. */
ret = GS_ERROR;
goto out;
}
- else
- {
- fprintf (stderr, "gimplification failed:\n");
- print_generic_expr (stderr, *expr_p, 0);
- debug_tree (*expr_p);
- abort ();
- }
-#if defined ENABLE_CHECKING
/* Make sure the temporary matches our predicate. */
- if (!(*gimple_test_f) (*expr_p))
- abort ();
-#endif
+ gcc_assert ((*gimple_test_f) (*expr_p));
if (internal_post)
{
void
gimplify_type_sizes (tree type, tree *list_p)
{
- tree field;
+ tree field, t;
+
+ if (type == NULL || type == error_mark_node)
+ return;
+
+ /* We first do the main variant, then copy into any other variants. */
+ type = TYPE_MAIN_VARIANT (type);
+
+ /* Avoid infinite recursion. */
+ if (TYPE_SIZES_GIMPLIFIED (type))
+ return;
+
+ TYPE_SIZES_GIMPLIFIED (type) = 1;
switch (TREE_CODE (type))
{
- case ERROR_MARK:
- return;
-
case INTEGER_TYPE:
case ENUMERAL_TYPE:
case BOOLEAN_TYPE:
case REAL_TYPE:
gimplify_one_sizepos (&TYPE_MIN_VALUE (type), list_p);
gimplify_one_sizepos (&TYPE_MAX_VALUE (type), list_p);
+
+ for (t = TYPE_NEXT_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
+ {
+ TYPE_MIN_VALUE (t) = TYPE_MIN_VALUE (type);
+ TYPE_MAX_VALUE (t) = TYPE_MAX_VALUE (type);
+ }
break;
case ARRAY_TYPE:
- /* These anonymous types don't have declarations, so handle them here. */
+ /* These types may not have declarations, so handle them here. */
+ gimplify_type_sizes (TREE_TYPE (type), list_p);
gimplify_type_sizes (TYPE_DOMAIN (type), list_p);
break;
case QUAL_UNION_TYPE:
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
- gimplify_one_sizepos (&DECL_FIELD_OFFSET (field), list_p);
+ {
+ gimplify_one_sizepos (&DECL_FIELD_OFFSET (field), list_p);
+ gimplify_type_sizes (TREE_TYPE (field), list_p);
+ }
+ break;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ gimplify_type_sizes (TREE_TYPE (type), list_p);
break;
default:
gimplify_one_sizepos (&TYPE_SIZE (type), list_p);
gimplify_one_sizepos (&TYPE_SIZE_UNIT (type), list_p);
+
+ for (t = TYPE_NEXT_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
+ {
+ TYPE_SIZE (t) = TYPE_SIZE (type);
+ TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (type);
+ TYPE_SIZES_GIMPLIFIED (t) = 1;
+ }
}
-/* Subroutine of the above to gimplify one size or position, *EXPR_P.
+/* 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, tree *stmt_p)
{
+ tree type, expr = *expr_p;
+
/* 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 gimplfier
+ 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_p == NULL_TREE || TREE_CONSTANT (*expr_p)
- || TREE_CODE (*expr_p) == VAR_DECL
- || CONTAINS_PLACEHOLDER_P (*expr_p))
+ if (expr == NULL_TREE || TREE_CONSTANT (expr)
+ || TREE_CODE (expr) == VAR_DECL
+ || CONTAINS_PLACEHOLDER_P (expr))
return;
+ type = TREE_TYPE (expr);
+ *expr_p = unshare_expr (expr);
+
gimplify_expr (expr_p, stmt_p, NULL, is_gimple_val, fb_rvalue);
+ expr = *expr_p;
+
+ /* Verify that we've an exact type match with the original expression.
+ In particular, we do not wish to drop a "sizetype" in favour of a
+ type of similar dimensions. We don't want to pollute the generic
+ type-stripping code with this knowledge because it doesn't matter
+ for the bulk of GENERIC/GIMPLE. It only matters that TYPE_SIZE_UNIT
+ and friends retain their "sizetype-ness". */
+ if (TREE_TYPE (expr) != type
+ && TREE_CODE (type) == INTEGER_TYPE
+ && TYPE_IS_SIZETYPE (type))
+ {
+ tree tmp;
+
+ *expr_p = create_tmp_var (type, NULL);
+ tmp = build1 (NOP_EXPR, type, expr);
+ tmp = build2 (MODIFY_EXPR, type, *expr_p, expr);
+ if (EXPR_HAS_LOCATION (expr))
+ SET_EXPR_LOCUS (tmp, EXPR_LOCUS (expr));
+ else
+ SET_EXPR_LOCATION (tmp, input_location);
+
+ gimplify_and_add (tmp, stmt_p);
+ }
}
\f
#ifdef ENABLE_CHECKING
otype = TREE_TYPE (t);
ptype = TREE_TYPE (TREE_OPERAND (t, 0));
dtype = TREE_TYPE (ptype);
- if (!cpt_same_type (otype, dtype))
- abort ();
+ gcc_assert (cpt_same_type (otype, dtype));
break;
case ADDR_EXPR:
a pointer to the array type. We must allow this in order to
properly represent assigning the address of an array in C into
pointer to the element type. */
- if (TREE_CODE (otype) == ARRAY_TYPE
- && POINTER_TYPE_P (ptype)
- && cpt_same_type (TREE_TYPE (otype), dtype))
- break;
- abort ();
+ gcc_assert (TREE_CODE (otype) == ARRAY_TYPE
+ && POINTER_TYPE_P (ptype)
+ && cpt_same_type (TREE_TYPE (otype), dtype));
+ break;
}
break;
}
#endif
-/* Gimplify the body of statements pointed by BODY_P. FNDECL is the
+/* Gimplify the body of statements pointed to by BODY_P. FNDECL is the
function decl containing BODY. */
void
-gimplify_body (tree *body_p, tree fndecl)
+gimplify_body (tree *body_p, tree fndecl, bool do_parms)
{
location_t saved_location = input_location;
- tree body;
+ tree body, parm_stmts;
timevar_push (TV_TREE_GIMPLIFY);
push_gimplify_context ();
/* Make sure input_location isn't set to something wierd. */
input_location = DECL_SOURCE_LOCATION (fndecl);
+ /* 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;
+
/* Gimplify the function's body. */
gimplify_stmt (body_p);
body = *body_p;
- /* Unshare again, in case gimplification was sloppy. */
- unshare_all_trees (body);
-
if (!body)
body = alloc_stmt_list ();
else if (TREE_CODE (body) == STATEMENT_LIST)
append_to_statement_list_force (body, &BIND_EXPR_BODY (b));
body = b;
}
+
+ /* If we had callee-copies statements, insert them at the beginning
+ of the function. */
+ if (parm_stmts)
+ {
+ append_to_statement_list_force (BIND_EXPR_BODY (body), &parm_stmts);
+ BIND_EXPR_BODY (body) = parm_stmts;
+ }
+
+ /* Unshare again, in case gimplification was sloppy. */
+ unshare_all_trees (body);
+
*body_p = body;
pop_gimplify_context (body);
void
gimplify_function_tree (tree fndecl)
{
- tree oldfn;
+ tree oldfn, parm, ret;
oldfn = current_function_decl;
current_function_decl = fndecl;
+ cfun = DECL_STRUCT_FUNCTION (fndecl);
+ if (cfun == NULL)
+ allocate_struct_function (fndecl);
- gimplify_body (&DECL_SAVED_TREE (fndecl), fndecl);
+ for (parm = DECL_ARGUMENTS (fndecl); parm ; parm = TREE_CHAIN (parm))
+ {
+ /* Preliminarily mark non-addressed complex variables as eligible
+ for promotion to gimple registers. We'll transform their uses
+ as we find them. */
+ if (TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
+ && !TREE_THIS_VOLATILE (parm)
+ && !needs_to_live_in_memory (parm))
+ DECL_COMPLEX_GIMPLE_REG_P (parm) = 1;
+ }
+
+ ret = DECL_RESULT (fndecl);
+ if (TREE_CODE (TREE_TYPE (ret)) == COMPLEX_TYPE
+ && !needs_to_live_in_memory (ret))
+ DECL_COMPLEX_GIMPLE_REG_P (ret) = 1;
+
+ gimplify_body (&DECL_SAVED_TREE (fndecl), fndecl, true);
/* 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
}
current_function_decl = oldfn;
+ cfun = oldfn ? DECL_STRUCT_FUNCTION (oldfn) : NULL;
+}
+
+\f
+/* Expands EXPR to list of gimple statements STMTS. If SIMPLE is true,
+ force the result to be either ssa_name or an invariant, otherwise
+ just force it to be a rhs expression. If VAR is not NULL, make the
+ base variable of the final destination be VAR if suitable. */
+
+tree
+force_gimple_operand (tree expr, tree *stmts, bool simple, tree var)
+{
+ tree t;
+ enum gimplify_status ret;
+ gimple_predicate gimple_test_f;
+
+ *stmts = NULL_TREE;
+
+ if (is_gimple_val (expr))
+ return expr;
+
+ gimple_test_f = simple ? is_gimple_val : is_gimple_reg_rhs;
+
+ push_gimplify_context ();
+ gimplify_ctxp->into_ssa = in_ssa_p;
+
+ if (var)
+ expr = build (MODIFY_EXPR, TREE_TYPE (var), var, expr);
+
+ ret = gimplify_expr (&expr, stmts, NULL,
+ gimple_test_f, fb_rvalue);
+ gcc_assert (ret != GS_ERROR);
+
+ if (referenced_vars)
+ {
+ for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
+ add_referenced_tmp_var (t);
+ }
+
+ pop_gimplify_context (NULL);
+
+ return expr;
+}
+
+/* Invokes force_gimple_operand for EXPR with parameters SIMPLE_P and VAR. If
+ some statements are produced, emits them before BSI. */
+
+tree
+force_gimple_operand_bsi (block_stmt_iterator *bsi, tree expr,
+ bool simple_p, tree var)
+{
+ tree stmts;
+
+ expr = force_gimple_operand (expr, &stmts, simple_p, var);
+ if (stmts)
+ bsi_insert_before (bsi, stmts, BSI_SAME_STMT);
+
+ return expr;
}
#include "gt-gimplify.h"