#include "coretypes.h"
#include "tm.h"
#include "tree.h"
-#include "rtl.h"
-#include "varray.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "tree-inline.h"
-#include "diagnostic.h"
+#include "tree-pretty-print.h"
#include "langhooks.h"
-#include "langhooks-def.h"
#include "tree-flow.h"
#include "cgraph.h"
#include "timevar.h"
-#include "except.h"
#include "hashtab.h"
#include "flags.h"
-#include "real.h"
#include "function.h"
#include "output.h"
-#include "expr.h"
#include "ggc.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "target.h"
-#include "optabs.h"
#include "pointer-set.h"
#include "splay-tree.h"
#include "vec.h"
#include "gimple.h"
#include "tree-pass.h"
+#include "langhooks-def.h" /* FIXME: for lhd_set_decl_assembler_name. */
+#include "expr.h" /* FIXME: for can_move_by_pieces
+ and STACK_CHECK_MAX_VAR_SIZE. */
enum gimplify_omp_var_data
{
enum omp_region_type
{
ORT_WORKSHARE = 0,
- ORT_TASK = 1,
ORT_PARALLEL = 2,
- ORT_COMBINED_PARALLEL = 3
+ ORT_COMBINED_PARALLEL = 3,
+ ORT_TASK = 4,
+ ORT_UNTIED_TASK = 5
};
struct gimplify_omp_ctx
{
while (handled_component_p (x))
x = TREE_OPERAND (x, 0);
+ if (TREE_CODE (x) == MEM_REF
+ && TREE_CODE (TREE_OPERAND (x, 0)) == ADDR_EXPR)
+ x = TREE_OPERAND (TREE_OPERAND (x, 0), 0);
if (TREE_CODE (x) != VAR_DECL
&& TREE_CODE (x) != PARM_DECL
&& TREE_CODE (x) != RESULT_DECL)
- return ;
+ return;
TREE_ADDRESSABLE (x) = 1;
}
if (!operand_equal_p (t1, t2, 0))
return 0;
+#ifdef ENABLE_CHECKING
/* Only allow them to compare equal if they also hash equal; otherwise
results are nondeterminate, and we fail bootstrap comparison. */
gcc_assert (gimple_tree_hash (p1) == gimple_tree_hash (p2));
+#endif
return 1;
}
During gimplification, we need to manipulate statement sequences
before the def/use vectors have been constructed. */
-static void
+void
gimplify_seq_add_stmt (gimple_seq *seq_p, gimple gs)
{
gimple_stmt_iterator si;
c->privatized_types = pointer_set_create ();
c->location = input_location;
c->region_type = region_type;
- if (region_type != ORT_TASK)
+ if ((region_type & ORT_TASK) == 0)
c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
else
c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
static void omp_add_variable (struct gimplify_omp_ctx *, tree, unsigned int);
static bool omp_notice_variable (struct gimplify_omp_ctx *, tree, bool);
-/* A subroutine of append_to_statement_list{,_force}. T is not NULL. */
-
-static void
-append_to_statement_list_1 (tree t, tree *list_p)
-{
- tree list = *list_p;
- tree_stmt_iterator i;
-
- if (!list)
- {
- if (t && TREE_CODE (t) == STATEMENT_LIST)
- {
- *list_p = t;
- return;
- }
- *list_p = list = alloc_stmt_list ();
- }
-
- i = tsi_last (list);
- tsi_link_after (&i, t, TSI_CONTINUE_LINKING);
-}
-
-/* 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)
-{
- 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)
-{
- if (t != NULL_TREE)
- append_to_statement_list_1 (t, list_p);
-}
-
/* Both gimplify the statement T and append it to *SEQ_P. This function
behaves exactly as gimplify_stmt, but you don't have to pass T as a
reference. */
/* Return true if T is a CALL_EXPR or an expression that can be
- assignmed to a temporary. Note that this predicate should only be
+ assigned to a temporary. Note that this predicate should only be
used during gimplification. See the rationale for this in
gimplify_modify_expr. */
mod = build2 (INIT_EXPR, TREE_TYPE (t), t, unshare_expr (val));
- if (EXPR_HAS_LOCATION (val))
- SET_EXPR_LOCATION (mod, EXPR_LOCATION (val));
- else
- SET_EXPR_LOCATION (mod, input_location);
+ SET_EXPR_LOCATION (mod, EXPR_LOC_OR_HERE (val));
/* gimplify_modify_expr might want to reduce this further. */
gimplify_and_add (mod, pre_p);
gcc_assert (!block || TREE_CODE (block) == BLOCK);
if (!block || !debug_info)
{
- TREE_CHAIN (last) = gimple_bind_vars (scope);
+ DECL_CHAIN (last) = gimple_bind_vars (scope);
gimple_bind_set_vars (scope, temps);
}
else
void
gimple_add_tmp_var (tree tmp)
{
- gcc_assert (!TREE_CHAIN (tmp) && !DECL_SEEN_IN_BIND_EXPR_P (tmp));
+ gcc_assert (!DECL_CHAIN (tmp) && !DECL_SEEN_IN_BIND_EXPR_P (tmp));
/* Later processing assumes that the object size is constant, which might
not be true at this point. Force the use of a constant upper bound in
if (gimplify_ctxp)
{
- TREE_CHAIN (tmp) = gimplify_ctxp->temps;
+ DECL_CHAIN (tmp) = gimplify_ctxp->temps;
gimplify_ctxp->temps = tmp;
/* Mark temporaries local within the nearest enclosing parallel. */
annotate_one_with_location (gs, location);
}
}
-
-
-/* Similar to copy_tree_r() but do not copy SAVE_EXPR or TARGET_EXPR nodes.
+\f
+/* This page contains routines to unshare tree nodes, i.e. to duplicate tree
+ nodes that are referenced more than once in GENERIC functions. This is
+ necessary because gimplification (translation into GIMPLE) is performed
+ by modifying tree nodes in-place, so gimplication of a shared node in a
+ first context could generate an invalid GIMPLE form in a second context.
+
+ This is achieved with a simple mark/copy/unmark algorithm that walks the
+ GENERIC representation top-down, marks nodes with TREE_VISITED the first
+ time it encounters them, duplicates them if they already have TREE_VISITED
+ set, and finally removes the TREE_VISITED marks it has set.
+
+ The algorithm works only at the function level, i.e. it generates a GENERIC
+ representation of a function with no nodes shared within the function when
+ passed a GENERIC function (except for nodes that are allowed to be shared).
+
+ At the global level, it is also necessary to unshare tree nodes that are
+ referenced in more than one function, for the same aforementioned reason.
+ This requires some cooperation from the front-end. There are 2 strategies:
+
+ 1. Manual unsharing. The front-end needs to call unshare_expr on every
+ expression that might end up being shared across functions.
+
+ 2. Deep unsharing. This is an extension of regular unsharing. Instead
+ of calling unshare_expr on expressions that might be shared across
+ functions, the front-end pre-marks them with TREE_VISITED. This will
+ ensure that they are unshared on the first reference within functions
+ when the regular unsharing algorithm runs. The counterpart is that
+ this algorithm must look deeper than for manual unsharing, which is
+ specified by LANG_HOOKS_DEEP_UNSHARING.
+
+ If there are only few specific cases of node sharing across functions, it is
+ probably easier for a front-end to unshare the expressions manually. On the
+ contrary, if the expressions generated at the global level are as widespread
+ as expressions generated within functions, deep unsharing is very likely the
+ way to go. */
+
+/* Similar to copy_tree_r but do not copy SAVE_EXPR or TARGET_EXPR nodes.
These nodes model computations that should only be done once. If we
were to unshare something like SAVE_EXPR(i++), the gimplification
process would create wrong code. */
static tree
mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data)
{
- enum tree_code code = TREE_CODE (*tp);
- /* Don't unshare types, decls, constants and SAVE_EXPR nodes. */
- 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 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
+ tree t = *tp;
+ enum tree_code code = TREE_CODE (t);
+
+ /* Do not copy SAVE_EXPR, TARGET_EXPR or BIND_EXPR nodes themselves, but
+ copy their subtrees if we can make sure to do it only once. */
+ if (code == SAVE_EXPR || code == TARGET_EXPR || code == BIND_EXPR)
{
- gcc_assert (code != BIND_EXPR);
- copy_tree_r (tp, walk_subtrees, data);
+ if (data && !pointer_set_insert ((struct pointer_set_t *)data, t))
+ ;
+ else
+ *walk_subtrees = 0;
}
+ /* Stop at types, decls, constants like copy_tree_r. */
+ else if (TREE_CODE_CLASS (code) == tcc_type
+ || TREE_CODE_CLASS (code) == tcc_declaration
+ || TREE_CODE_CLASS (code) == tcc_constant
+ /* We can't do anything sensible with a BLOCK used as an
+ expression, but we also can't just die when we see it
+ because of non-expression uses. So we avert our eyes
+ and cross our fingers. Silly Java. */
+ || code == BLOCK)
+ *walk_subtrees = 0;
+
+ /* Cope with the statement expression extension. */
+ else if (code == STATEMENT_LIST)
+ ;
+
+ /* Leave the bulk of the work to copy_tree_r itself. */
+ else
+ copy_tree_r (tp, walk_subtrees, NULL);
+
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.
-
- This unshares the same trees as copy_tree_r with the exception of
- SAVE_EXPR nodes. These nodes model computations that should only be
- done once. If we were to unshare something like SAVE_EXPR(i++), the
- gimplification process would create wrong code. */
+ then *TP is deep copied by calling mostly_copy_tree_r. */
static tree
-copy_if_shared_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED)
+copy_if_shared_r (tree *tp, int *walk_subtrees, void *data)
{
tree t = *tp;
enum tree_code code = TREE_CODE (t);
any deeper. */
else if (TREE_VISITED (t))
{
- walk_tree (tp, mostly_copy_tree_r, NULL, NULL);
+ walk_tree (tp, mostly_copy_tree_r, data, NULL);
*walk_subtrees = 0;
}
- /* Otherwise, mark the tree as visited and keep looking. */
+ /* Otherwise, mark the node as visited and keep looking. */
else
TREE_VISITED (t) = 1;
return NULL_TREE;
}
-static tree
-unmark_visited_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED)
-{
- if (TREE_VISITED (*tp))
- TREE_VISITED (*tp) = 0;
- else
- *walk_subtrees = 0;
+/* Unshare most of the shared trees rooted at *TP. */
- return NULL_TREE;
+static inline void
+copy_if_shared (tree *tp)
+{
+ /* If the language requires deep unsharing, we need a pointer set to make
+ sure we don't repeatedly unshare subtrees of unshareable nodes. */
+ struct pointer_set_t *visited
+ = lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
+ walk_tree (tp, copy_if_shared_r, visited, NULL);
+ if (visited)
+ pointer_set_destroy (visited);
}
/* Unshare all the trees in BODY_P, a pointer into the body of FNDECL, and the
{
struct cgraph_node *cgn = cgraph_node (fndecl);
- walk_tree (body_p, copy_if_shared_r, NULL, NULL);
+ copy_if_shared (body_p);
+
if (body_p == &DECL_SAVED_TREE (fndecl))
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
unshare_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
}
+/* Callback for walk_tree to unmark the visited trees rooted at *TP.
+ Subtrees are walked until the first unvisited node is encountered. */
+
+static tree
+unmark_visited_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+{
+ tree t = *tp;
+
+ /* If this node has been visited, unmark it and keep looking. */
+ if (TREE_VISITED (t))
+ TREE_VISITED (t) = 0;
+
+ /* Otherwise, don't look any deeper. */
+ else
+ *walk_subtrees = 0;
+
+ return NULL_TREE;
+}
+
+/* Unmark the visited trees rooted at *TP. */
+
+static inline void
+unmark_visited (tree *tp)
+{
+ walk_tree (tp, unmark_visited_r, NULL, NULL);
+}
+
/* Likewise, but mark all trees as not visited. */
static void
{
struct cgraph_node *cgn = cgraph_node (fndecl);
- walk_tree (body_p, unmark_visited_r, NULL, NULL);
+ unmark_visited (body_p);
+
if (body_p == &DECL_SAVED_TREE (fndecl))
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
unvisit_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
tree temp = voidify_wrapper_expr (bind_expr, NULL);
/* Mark variables seen in this bind expr. */
- for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t))
+ for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
{
if (TREE_CODE (t) == VAR_DECL)
{
/* Preliminarily mark non-addressed complex variables as eligible
for promotion to gimple registers. We'll transform their uses
- as we find them.
- We exclude complex types if not optimizing because they can be
- subject to partial stores in GNU C by means of the __real__ and
- __imag__ operators and we cannot promote them to total stores
- (see gimplify_modify_expr_complex_part). */
- if (optimize
- && (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE
- || TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
+ as we find them. */
+ if ((TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
&& !TREE_THIS_VOLATILE (t)
&& (TREE_CODE (t) == VAR_DECL && !DECL_HARD_REGISTER (t))
&& !needs_to_live_in_memory (t))
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
- || aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
- result = result_decl;
+ if (!result_decl)
+ result = NULL_TREE;
+ else if (aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
+ {
+ if (TREE_CODE (DECL_SIZE (result_decl)) != INTEGER_CST)
+ {
+ if (!TYPE_SIZES_GIMPLIFIED (TREE_TYPE (result_decl)))
+ gimplify_type_sizes (TREE_TYPE (result_decl), pre_p);
+ /* Note that we don't use gimplify_vla_decl because the RESULT_DECL
+ should be effectively allocated by the caller, i.e. all calls to
+ this function must be subject to the Return Slot Optimization. */
+ gimplify_one_sizepos (&DECL_SIZE (result_decl), pre_p);
+ gimplify_one_sizepos (&DECL_SIZE_UNIT (result_decl), pre_p);
+ }
+ result = result_decl;
+ }
else if (gimplify_ctxp->return_temp)
result = gimplify_ctxp->return_temp;
else
t = built_in_decls[BUILT_IN_ALLOCA];
t = build_call_expr (t, 1, DECL_SIZE_UNIT (decl));
+ /* The call has been built for a variable-sized object. */
+ ALLOCA_FOR_VAR_P (t) = 1;
t = fold_convert (ptr_type, t);
t = build2 (MODIFY_EXPR, TREE_TYPE (addr), addr, t);
STACK_CHECK_MAX_VAR_SIZE) > 0))
gimplify_vla_decl (decl, seq_p);
+ /* Some front ends do not explicitly declare all anonymous
+ artificial variables. We compensate here by declaring the
+ variables, though it would be better if the front ends would
+ explicitly declare them. */
+ if (!DECL_SEEN_IN_BIND_EXPR_P (decl)
+ && DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
+ gimple_add_tmp_var (decl);
+
if (init && init != error_mark_node)
{
if (!TREE_STATIC (decl))
as they may contain a label address. */
walk_tree (&init, force_labels_r, NULL, NULL);
}
-
- /* Some front ends do not explicitly declare all anonymous
- artificial variables. We compensate here by declaring the
- variables, though it would be better if the front ends would
- explicitly declare them. */
- if (!DECL_SEEN_IN_BIND_EXPR_P (decl)
- && DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
- gimple_add_tmp_var (decl);
}
return GS_ALL_DONE;
void
sort_case_labels (VEC(tree,heap)* label_vec)
{
- size_t len = VEC_length (tree, label_vec);
- qsort (VEC_address (tree, label_vec), len, sizeof (tree),
- compare_case_labels);
+ VEC_qsort (tree, label_vec, compare_case_labels);
}
&& !TREE_STATIC (decl) && !DECL_EXTERNAL (decl)
&& decl_function_context (decl) == current_function_decl)
{
- gcc_assert (errorcount || sorrycount);
+ gcc_assert (seen_error ());
return GS_ERROR;
}
tree copy = copy_node (decl), block;
lang_hooks.dup_lang_specific_decl (copy);
- SET_DECL_RTL (copy, NULL_RTX);
+ SET_DECL_RTL (copy, 0);
TREE_USED (copy) = 1;
block = DECL_INITIAL (current_function_decl);
- TREE_CHAIN (copy) = BLOCK_VARS (block);
+ DECL_CHAIN (copy) = BLOCK_VARS (block);
BLOCK_VARS (block) = copy;
SET_DECL_VALUE_EXPR (copy, unshare_expr (value_expr));
DECL_HAS_VALUE_EXPR_P (copy) = 1;
{
tree *p;
VEC(tree,heap) *stack;
- enum gimplify_status ret = GS_OK, tret;
+ enum gimplify_status ret = GS_ALL_DONE, tret;
int i;
location_t loc = EXPR_LOCATION (*expr_p);
+ tree expr = *expr_p;
/* Create a stack of the subexpressions so later we can walk them in
order from inner to outer. */
if ((fallback & fb_rvalue) && TREE_CODE (*expr_p) == COMPONENT_REF)
{
canonicalize_component_ref (expr_p);
- ret = MIN (ret, GS_OK);
}
VEC_free (tree, heap, stack);
+ gcc_assert (*expr_p == expr || ret != GS_ALL_DONE);
+
return ret;
}
{
/* The CALL_EXPR in *EXPR_P is already in GIMPLE form, so all we
have to do is replicate it as a GIMPLE_CALL tuple. */
+ gimple_stmt_iterator gsi;
call = gimple_build_call_from_tree (*expr_p);
gimplify_seq_add_stmt (pre_p, call);
+ gsi = gsi_last (*pre_p);
+ fold_stmt (&gsi);
*expr_p = NULL_TREE;
}
while (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
{
/* Keep the original source location on the first 'if'. */
- location_t locus = EXPR_HAS_LOCATION (expr)
- ? EXPR_LOCATION (expr) : input_location;
+ location_t locus = EXPR_LOC_OR_HERE (expr);
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
/* Set the source location of the && on the second 'if'. */
if (EXPR_HAS_LOCATION (pred))
while (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{
/* Keep the original source location on the first 'if'. */
- location_t locus = EXPR_HAS_LOCATION (expr)
- ? EXPR_LOCATION (expr) : input_location;
+ location_t locus = EXPR_LOC_OR_HERE (expr);
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
/* Set the source location of the || on the second 'if'. */
if (EXPR_HAS_LOCATION (pred))
/* If there was nothing else in our arms, just forward the label(s). */
if (!then_se && !else_se)
return shortcut_cond_r (pred, true_label_p, false_label_p,
- EXPR_HAS_LOCATION (expr)
- ? EXPR_LOCATION (expr) : input_location);
+ EXPR_LOC_OR_HERE (expr));
/* If our last subexpression already has a terminal label, reuse it. */
if (else_se)
jump_over_else = block_may_fallthru (then_);
pred = shortcut_cond_r (pred, true_label_p, false_label_p,
- EXPR_HAS_LOCATION (expr)
- ? EXPR_LOCATION (expr) : input_location);
+ EXPR_LOC_OR_HERE (expr));
expr = NULL;
append_to_statement_list (pred, &expr);
= build3 (COND_EXPR, type, TREE_OPERAND (expr, 0), then_, else_);
tmp = create_tmp_var (type, "iftmp");
- result = build_fold_indirect_ref_loc (loc, tmp);
+ result = build_simple_mem_ref_loc (loc, tmp);
}
/* Build the new then clause, `tmp = then_;'. But don't build the
gimple_call_set_lhs (gs, t);
gimplify_seq_add_stmt (seq_p, gs);
- *expr_p = build1 (INDIRECT_REF, TREE_TYPE (to), t);
+ *expr_p = build_simple_mem_ref (t);
return GS_ALL_DONE;
}
/* 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
+ if ((INDIRECT_REF_P (t)
+ || TREE_CODE (t) == MEM_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;
/* If the constructor component is a call, determine if it can hide a
- potential overlap with the lhs through an INDIRECT_REF like above. */
+ potential overlap with the lhs through an INDIRECT_REF like above.
+ ??? Ugh - this is completely broken. In fact this whole analysis
+ doesn't look conservative. */
if (TREE_CODE (t) == CALL_EXPR)
{
tree type, fntype = TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (t)));
constructor_elt *ce;
VEC(constructor_elt,gc) *v = CONSTRUCTOR_ELTS (*expr_p);
- for (ix = 0; VEC_iterate (constructor_elt, v, ix, ce); ix++)
+ FOR_EACH_VEC_ELT (constructor_elt, v, ix, ce)
gimplify_init_ctor_preeval (&ce->value, pre_p, post_p, data);
return;
if (notify_temp_creation)
return GS_ERROR;
- walk_tree (&ctor, force_labels_r, NULL, NULL);
- TREE_OPERAND (*expr_p, 1) = tree_output_constant_def (ctor);
+ walk_tree (&ctor, force_labels_r, NULL, NULL);
+ ctor = tree_output_constant_def (ctor);
+ if (!useless_type_conversion_p (type, TREE_TYPE (ctor)))
+ ctor = build1 (VIEW_CONVERT_EXPR, type, ctor);
+ TREE_OPERAND (*expr_p, 1) = ctor;
/* This is no longer an assignment of a CONSTRUCTOR, but
we still may have processing to do on the LHS. So
}
}
- /* If the target is volatile and we have non-zero elements
- initialize the target from a temporary. */
+ /* If the target is volatile, we have non-zero elements and more than
+ one field to assign, initialize the target from a temporary. */
if (TREE_THIS_VOLATILE (object)
&& !TREE_ADDRESSABLE (type)
- && num_nonzero_elements > 0)
+ && num_nonzero_elements > 0
+ && VEC_length (constructor_elt, elts) > 1)
{
tree temp = create_tmp_var (TYPE_MAIN_VARIANT (type), NULL);
TREE_OPERAND (*expr_p, 0) = temp;
i = VEC_index (constructor_elt, elts, 1)->value;
if (r == NULL || i == NULL)
{
- tree zero = fold_convert (TREE_TYPE (type), integer_zero_node);
+ tree zero = build_zero_cst (TREE_TYPE (type));
if (r == NULL)
r = zero;
if (i == NULL)
/* 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++)
+ FOR_EACH_VEC_ELT (constructor_elt, elts, ix, ce)
{
enum gimplify_status tret;
tret = gimplify_expr (&ce->value, pre_p, post_p, is_gimple_val,
tree
gimple_fold_indirect_ref (tree t)
{
- tree type = TREE_TYPE (TREE_TYPE (t));
+ tree ptype = TREE_TYPE (t), type = TREE_TYPE (ptype);
tree sub = t;
tree subtype;
}
}
- /* ((foo*)&vectorfoo)[1] => BIT_FIELD_REF<vectorfoo,...> */
+ /* *(p + CST) -> ... */
if (TREE_CODE (sub) == POINTER_PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (sub, 1)) == INTEGER_CST)
{
- tree op00 = TREE_OPERAND (sub, 0);
- tree op01 = TREE_OPERAND (sub, 1);
- tree op00type;
+ tree addr = TREE_OPERAND (sub, 0);
+ tree off = TREE_OPERAND (sub, 1);
+ tree addrtype;
+
+ STRIP_NOPS (addr);
+ addrtype = TREE_TYPE (addr);
- STRIP_NOPS (op00);
- op00type = TREE_TYPE (op00);
- if (TREE_CODE (op00) == ADDR_EXPR
- && TREE_CODE (TREE_TYPE (op00type)) == VECTOR_TYPE
- && useless_type_conversion_p (type, TREE_TYPE (TREE_TYPE (op00type))))
+ /* ((foo*)&vectorfoo)[1] -> BIT_FIELD_REF<vectorfoo,...> */
+ if (TREE_CODE (addr) == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (addrtype)) == VECTOR_TYPE
+ && useless_type_conversion_p (type, TREE_TYPE (TREE_TYPE (addrtype))))
{
- HOST_WIDE_INT offset = tree_low_cst (op01, 0);
- tree part_width = TYPE_SIZE (type);
- unsigned HOST_WIDE_INT part_widthi
- = tree_low_cst (part_width, 0) / BITS_PER_UNIT;
- unsigned HOST_WIDE_INT indexi = offset * BITS_PER_UNIT;
- tree index = bitsize_int (indexi);
- if (offset / part_widthi
- <= TYPE_VECTOR_SUBPARTS (TREE_TYPE (op00type)))
- return fold_build3 (BIT_FIELD_REF, type, TREE_OPERAND (op00, 0),
- part_width, index);
+ HOST_WIDE_INT offset = tree_low_cst (off, 0);
+ tree part_width = TYPE_SIZE (type);
+ unsigned HOST_WIDE_INT part_widthi
+ = tree_low_cst (part_width, 0) / BITS_PER_UNIT;
+ unsigned HOST_WIDE_INT indexi = offset * BITS_PER_UNIT;
+ tree index = bitsize_int (indexi);
+ if (offset / part_widthi
+ <= TYPE_VECTOR_SUBPARTS (TREE_TYPE (addrtype)))
+ return fold_build3 (BIT_FIELD_REF, type, TREE_OPERAND (addr, 0),
+ part_width, index);
}
- }
- /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */
- if (TREE_CODE (sub) == POINTER_PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (sub, 1)) == INTEGER_CST)
- {
- tree op00 = TREE_OPERAND (sub, 0);
- tree op01 = TREE_OPERAND (sub, 1);
- tree op00type;
+ /* ((foo*)&complexfoo)[1] -> __imag__ complexfoo */
+ if (TREE_CODE (addr) == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (addrtype)) == COMPLEX_TYPE
+ && useless_type_conversion_p (type, TREE_TYPE (TREE_TYPE (addrtype))))
+ {
+ tree size = TYPE_SIZE_UNIT (type);
+ if (tree_int_cst_equal (size, off))
+ return fold_build1 (IMAGPART_EXPR, type, TREE_OPERAND (addr, 0));
+ }
- STRIP_NOPS (op00);
- op00type = TREE_TYPE (op00);
- if (TREE_CODE (op00) == ADDR_EXPR
- && TREE_CODE (TREE_TYPE (op00type)) == COMPLEX_TYPE
- && useless_type_conversion_p (type, TREE_TYPE (TREE_TYPE (op00type))))
- {
- tree size = TYPE_SIZE_UNIT (type);
- if (tree_int_cst_equal (size, op01))
- return fold_build1 (IMAGPART_EXPR, type, TREE_OPERAND (op00, 0));
- }
+ /* *(p + CST) -> MEM_REF <p, CST>. */
+ if (TREE_CODE (addr) != ADDR_EXPR
+ || DECL_P (TREE_OPERAND (addr, 0)))
+ return fold_build2 (MEM_REF, type,
+ addr,
+ build_int_cst_wide (ptype,
+ TREE_INT_CST_LOW (off),
+ TREE_INT_CST_HIGH (off)));
}
/* *(foo *)fooarrptr => (*fooarrptr)[0] */
gimple_seq *pre_p, gimple_seq *post_p,
bool want_value)
{
- enum gimplify_status ret = GS_OK;
+ enum gimplify_status ret = GS_UNHANDLED;
+ bool changed;
- while (ret != GS_UNHANDLED)
- switch (TREE_CODE (*from_p))
- {
- case VAR_DECL:
- /* If we're assigning from a read-only variable initialized with
- a constructor, do the direct assignment from the constructor,
- but only if neither source nor target are volatile since this
- latter assignment might end up being done on a per-field basis. */
- if (DECL_INITIAL (*from_p)
- && TREE_READONLY (*from_p)
- && !TREE_THIS_VOLATILE (*from_p)
- && !TREE_THIS_VOLATILE (*to_p)
- && TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
+ do
+ {
+ changed = false;
+ switch (TREE_CODE (*from_p))
+ {
+ case VAR_DECL:
+ /* If we're assigning from a read-only variable initialized with
+ a constructor, do the direct assignment from the constructor,
+ but only if neither source nor target are volatile since this
+ latter assignment might end up being done on a per-field basis. */
+ if (DECL_INITIAL (*from_p)
+ && TREE_READONLY (*from_p)
+ && !TREE_THIS_VOLATILE (*from_p)
+ && !TREE_THIS_VOLATILE (*to_p)
+ && TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
+ {
+ tree old_from = *from_p;
+ enum gimplify_status subret;
+
+ /* Move the constructor into the RHS. */
+ *from_p = unshare_expr (DECL_INITIAL (*from_p));
+
+ /* Let's see if gimplify_init_constructor will need to put
+ it in memory. */
+ subret = gimplify_init_constructor (expr_p, NULL, NULL,
+ false, true);
+ if (subret == GS_ERROR)
+ {
+ /* If so, revert the change. */
+ *from_p = old_from;
+ }
+ else
+ {
+ ret = GS_OK;
+ changed = true;
+ }
+ }
+ break;
+ case INDIRECT_REF:
{
- tree old_from = *from_p;
+ /* If we have code like
- /* Move the constructor into the RHS. */
- *from_p = unshare_expr (DECL_INITIAL (*from_p));
+ *(const A*)(A*)&x
- /* Let's see if gimplify_init_constructor will need to put
- it in memory. If so, revert the change. */
- ret = gimplify_init_constructor (expr_p, NULL, NULL, false, true);
- if (ret == GS_ERROR)
+ 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. */
+ bool volatile_p = TREE_THIS_VOLATILE (*from_p);
+ tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
+ if (t)
{
- *from_p = old_from;
- /* Fall through. */
+ if (TREE_THIS_VOLATILE (t) != volatile_p)
+ {
+ if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
+ t = build_simple_mem_ref_loc (EXPR_LOCATION (*from_p),
+ build_fold_addr_expr (t));
+ if (REFERENCE_CLASS_P (t))
+ TREE_THIS_VOLATILE (t) = volatile_p;
+ }
+ *from_p = t;
+ ret = GS_OK;
+ changed = true;
}
- else
+ break;
+ }
+
+ case TARGET_EXPR:
+ {
+ /* If we are initializing something from a TARGET_EXPR, strip the
+ TARGET_EXPR and initialize it directly, if possible. This can't
+ be done if the initializer is void, since that implies that the
+ temporary is set in some non-trivial way.
+
+ ??? 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 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);
+
+ if (init
+ && !VOID_TYPE_P (TREE_TYPE (init)))
{
+ *from_p = init;
ret = GS_OK;
- break;
+ changed = true;
}
}
- ret = GS_UNHANDLED;
- break;
- case INDIRECT_REF:
- {
- /* If we have code like
+ break;
- *(const A*)(A*)&x
+ case COMPOUND_EXPR:
+ /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
+ caught. */
+ gimplify_compound_expr (from_p, pre_p, true);
+ ret = GS_OK;
+ changed = true;
+ break;
- 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 = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
- if (t)
+ case CONSTRUCTOR:
+ /* If we already made some changes, let the front end have a
+ crack at this before we break it down. */
+ if (ret != GS_UNHANDLED)
+ break;
+ /* If we're initializing from a CONSTRUCTOR, break this into
+ individual MODIFY_EXPRs. */
+ return gimplify_init_constructor (expr_p, pre_p, post_p, want_value,
+ false);
+
+ case COND_EXPR:
+ /* 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)))
{
- *from_p = t;
- ret = GS_OK;
+ /* This code should mirror the code in gimplify_cond_expr. */
+ enum tree_code code = TREE_CODE (*expr_p);
+ tree cond = *from_p;
+ tree result = *to_p;
+
+ ret = gimplify_expr (&result, pre_p, post_p,
+ is_gimple_lvalue, fb_lvalue);
+ if (ret != GS_ERROR)
+ ret = GS_OK;
+
+ if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node)
+ TREE_OPERAND (cond, 1)
+ = build2 (code, void_type_node, result,
+ TREE_OPERAND (cond, 1));
+ if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
+ TREE_OPERAND (cond, 2)
+ = build2 (code, void_type_node, unshare_expr (result),
+ TREE_OPERAND (cond, 2));
+
+ TREE_TYPE (cond) = void_type_node;
+ recalculate_side_effects (cond);
+
+ if (want_value)
+ {
+ gimplify_and_add (cond, pre_p);
+ *expr_p = unshare_expr (result);
+ }
+ else
+ *expr_p = cond;
+ return ret;
}
- else
- ret = GS_UNHANDLED;
break;
- }
- case TARGET_EXPR:
- {
- /* If we are initializing something from a TARGET_EXPR, strip the
- TARGET_EXPR and initialize it directly, if possible. This can't
- be done if the initializer is void, since that implies that the
- temporary is set in some non-trivial way.
-
- ??? 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 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);
-
- if (init
- && !VOID_TYPE_P (TREE_TYPE (init)))
+ 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))
{
- *from_p = init;
- ret = GS_OK;
+ bool use_target;
+
+ if (!(rhs_predicate_for (*to_p))(*from_p))
+ /* If we need a temporary, *to_p isn't accurate. */
+ use_target = false;
+ else if (TREE_CODE (*to_p) == RESULT_DECL
+ && DECL_NAME (*to_p) == NULL_TREE
+ && needs_to_live_in_memory (*to_p))
+ /* It's OK to use the return slot directly unless it's an NRV. */
+ use_target = true;
+ else if (is_gimple_reg_type (TREE_TYPE (*to_p))
+ || (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
+ /* Don't force regs into memory. */
+ use_target = false;
+ else if (TREE_CODE (*expr_p) == INIT_EXPR)
+ /* It's OK to use the target directly if it's being
+ initialized. */
+ 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
+ use_target = true;
+
+ if (use_target)
+ {
+ CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
+ mark_addressable (*to_p);
+ }
}
- else
- ret = GS_UNHANDLED;
- }
- break;
+ break;
- case COMPOUND_EXPR:
- /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
- caught. */
- gimplify_compound_expr (from_p, pre_p, true);
- ret = GS_OK;
- break;
+ case WITH_SIZE_EXPR:
+ /* Likewise for calls that return an aggregate of non-constant size,
+ since we would not be able to generate a temporary at all. */
+ if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
+ {
+ *from_p = TREE_OPERAND (*from_p, 0);
+ /* We don't change ret in this case because the
+ WITH_SIZE_EXPR might have been added in
+ gimplify_modify_expr, so returning GS_OK would lead to an
+ infinite loop. */
+ changed = true;
+ }
+ break;
- case CONSTRUCTOR:
- /* If we're initializing from a CONSTRUCTOR, break this into
- individual MODIFY_EXPRs. */
- return gimplify_init_constructor (expr_p, pre_p, post_p, want_value,
- false);
-
- case COND_EXPR:
- /* 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)))
+ /* If we're initializing from a container, push the initialization
+ inside it. */
+ case CLEANUP_POINT_EXPR:
+ case BIND_EXPR:
+ case STATEMENT_LIST:
{
- /* This code should mirror the code in gimplify_cond_expr. */
- enum tree_code code = TREE_CODE (*expr_p);
- tree cond = *from_p;
- tree result = *to_p;
+ tree wrap = *from_p;
+ tree t;
- ret = gimplify_expr (&result, pre_p, post_p,
- is_gimple_lvalue, fb_lvalue);
+ ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
+ fb_lvalue);
if (ret != GS_ERROR)
ret = GS_OK;
- if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node)
- TREE_OPERAND (cond, 1)
- = build2 (code, void_type_node, result,
- TREE_OPERAND (cond, 1));
- if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
- TREE_OPERAND (cond, 2)
- = build2 (code, void_type_node, unshare_expr (result),
- TREE_OPERAND (cond, 2));
-
- TREE_TYPE (cond) = void_type_node;
- recalculate_side_effects (cond);
+ t = voidify_wrapper_expr (wrap, *expr_p);
+ gcc_assert (t == *expr_p);
if (want_value)
{
- gimplify_and_add (cond, pre_p);
- *expr_p = unshare_expr (result);
+ gimplify_and_add (wrap, pre_p);
+ *expr_p = unshare_expr (*to_p);
}
else
- *expr_p = cond;
- return ret;
+ *expr_p = wrap;
+ return GS_OK;
}
- 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))
+ case COMPOUND_LITERAL_EXPR:
{
- bool use_target;
-
- if (!(rhs_predicate_for (*to_p))(*from_p))
- /* If we need a temporary, *to_p isn't accurate. */
- use_target = false;
- else if (TREE_CODE (*to_p) == RESULT_DECL
- && DECL_NAME (*to_p) == NULL_TREE
- && needs_to_live_in_memory (*to_p))
- /* It's OK to use the return slot directly unless it's an NRV. */
- use_target = true;
- else if (is_gimple_reg_type (TREE_TYPE (*to_p))
- || (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
- /* Don't force regs into memory. */
- use_target = false;
- else if (TREE_CODE (*expr_p) == INIT_EXPR)
- /* It's OK to use the target directly if it's being
- initialized. */
- 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
- use_target = true;
-
- if (use_target)
+ tree complit = TREE_OPERAND (*expr_p, 1);
+ tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
+ tree decl = DECL_EXPR_DECL (decl_s);
+ tree init = DECL_INITIAL (decl);
+
+ /* struct T x = (struct T) { 0, 1, 2 } can be optimized
+ into struct T x = { 0, 1, 2 } if the address of the
+ compound literal has never been taken. */
+ if (!TREE_ADDRESSABLE (complit)
+ && !TREE_ADDRESSABLE (decl)
+ && init)
{
- CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
- mark_addressable (*to_p);
+ *expr_p = copy_node (*expr_p);
+ TREE_OPERAND (*expr_p, 1) = init;
+ return GS_OK;
}
}
- ret = GS_UNHANDLED;
- break;
-
- case WITH_SIZE_EXPR:
- /* Likewise for calls that return an aggregate of non-constant size,
- since we would not be able to generate a temporary at all. */
- if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
- {
- *from_p = TREE_OPERAND (*from_p, 0);
- ret = GS_OK;
- }
- else
- ret = GS_UNHANDLED;
- break;
-
- /* If we're initializing from a container, push the initialization
- inside it. */
- case CLEANUP_POINT_EXPR:
- case BIND_EXPR:
- case STATEMENT_LIST:
- {
- tree wrap = *from_p;
- tree t;
-
- ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
- fb_lvalue);
- if (ret != GS_ERROR)
- ret = GS_OK;
-
- t = voidify_wrapper_expr (wrap, *expr_p);
- gcc_assert (t == *expr_p);
-
- if (want_value)
- {
- gimplify_and_add (wrap, pre_p);
- *expr_p = unshare_expr (*to_p);
- }
- else
- *expr_p = wrap;
- return GS_OK;
- }
-
- case COMPOUND_LITERAL_EXPR:
- {
- tree complit = TREE_OPERAND (*expr_p, 1);
- tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
- tree decl = DECL_EXPR_DECL (decl_s);
- tree init = DECL_INITIAL (decl);
-
- /* struct T x = (struct T) { 0, 1, 2 } can be optimized
- into struct T x = { 0, 1, 2 } if the address of the
- compound literal has never been taken. */
- if (!TREE_ADDRESSABLE (complit)
- && !TREE_ADDRESSABLE (decl)
- && init)
- {
- *expr_p = copy_node (*expr_p);
- TREE_OPERAND (*expr_p, 1) = init;
- return GS_OK;
- }
+ default:
+ break;
}
-
- default:
- ret = GS_UNHANDLED;
- break;
- }
+ }
+ while (changed);
return ret;
}
/* Try to alleviate the effects of the gimplification creating artificial
temporaries (see for example is_gimple_reg_rhs) on the debug info. */
if (!gimplify_ctxp->into_ssa
- && DECL_P (*from_p)
+ && TREE_CODE (*from_p) == VAR_DECL
&& DECL_IGNORED_P (*from_p)
&& DECL_P (*to_p)
&& !DECL_IGNORED_P (*to_p))
SET_DECL_DEBUG_EXPR (*from_p, *to_p);
}
+ if (want_value && TREE_THIS_VOLATILE (*to_p))
+ *from_p = get_initialized_tmp_var (*from_p, pre_p, post_p);
+
if (TREE_CODE (*from_p) == CALL_EXPR)
{
/* Since the RHS is a CALL_EXPR, we need to create a GIMPLE_CALL
if (want_value)
{
- *expr_p = unshare_expr (*to_p);
+ *expr_p = TREE_THIS_VOLATILE (*to_p) ? *from_p : unshare_expr (*to_p);
return GS_OK;
}
else
static enum gimplify_status
gimplify_variable_sized_compare (tree *expr_p)
{
+ location_t loc = EXPR_LOCATION (*expr_p);
tree op0 = TREE_OPERAND (*expr_p, 0);
tree op1 = TREE_OPERAND (*expr_p, 1);
- tree t, arg, dest, src;
- location_t loc = EXPR_LOCATION (*expr_p);
+ tree t, arg, dest, src, expr;
arg = TYPE_SIZE_UNIT (TREE_TYPE (op0));
arg = unshare_expr (arg);
dest = build_fold_addr_expr_loc (loc, op0);
t = implicit_built_in_decls[BUILT_IN_MEMCMP];
t = build_call_expr_loc (loc, t, 3, dest, src, arg);
- *expr_p
+
+ expr
= build2 (TREE_CODE (*expr_p), TREE_TYPE (*expr_p), t, integer_zero_node);
+ SET_EXPR_LOCATION (expr, loc);
+ *expr_p = expr;
return GS_OK;
}
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
/* If the operand is a memory input, it should be an lvalue. */
if (!allows_reg && allows_mem)
{
+ tree inputv = TREE_VALUE (link);
+ STRIP_NOPS (inputv);
+ if (TREE_CODE (inputv) == PREDECREMENT_EXPR
+ || TREE_CODE (inputv) == PREINCREMENT_EXPR
+ || TREE_CODE (inputv) == POSTDECREMENT_EXPR
+ || TREE_CODE (inputv) == POSTINCREMENT_EXPR)
+ TREE_VALUE (link) = error_mark_node;
tret = gimplify_expr (&TREE_VALUE (link), pre_p, post_p,
is_gimple_lvalue, fb_lvalue | fb_mayfail);
mark_addressable (TREE_VALUE (link));
{
/* Note that gsi_insert_seq_before and gsi_remove do not
scan operands, unlike some other sequence mutators. */
- gsi_insert_seq_before_without_update (&iter,
- gimple_wce_cleanup (wce),
- GSI_SAME_STMT);
+ if (!gimple_wce_cleanup_eh_only (wce))
+ gsi_insert_seq_before_without_update (&iter,
+ gimple_wce_cleanup (wce),
+ GSI_SAME_STMT);
gsi_remove (&iter, true);
break;
}
/* Errors can result in improperly nested cleanups. Which results in
confusion when trying to resolve the GIMPLE_WITH_CLEANUP_EXPR. */
- if (errorcount || sorrycount)
+ if (seen_error ())
return;
if (gimple_conditional_context ())
case QUAL_UNION_TYPE:
{
tree field;
- for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
{
omp_firstprivatize_variable (ctx, DECL_FIELD_OFFSET (field));
splay_tree_insert (ctx->variables, (splay_tree_key)decl, flags);
}
+/* Notice a threadprivate variable DECL used in OpenMP context CTX.
+ This just prints out diagnostics about threadprivate variable uses
+ in untied tasks. If DECL2 is non-NULL, prevent this warning
+ on that variable. */
+
+static bool
+omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
+ tree decl2)
+{
+ splay_tree_node n;
+
+ if (ctx->region_type != ORT_UNTIED_TASK)
+ return false;
+ n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl);
+ if (n == NULL)
+ {
+ error ("threadprivate variable %qE used in untied task", DECL_NAME (decl));
+ error_at (ctx->location, "enclosing task");
+ splay_tree_insert (ctx->variables, (splay_tree_key)decl, 0);
+ }
+ if (decl2)
+ splay_tree_insert (ctx->variables, (splay_tree_key)decl2, 0);
+ return false;
+}
+
/* Record the fact that DECL was used within the OpenMP context CTX.
IN_CODE is true when real code uses DECL, and false when we should
merely emit default(none) errors. Return true if DECL is going to
if (is_global_var (decl))
{
if (DECL_THREAD_LOCAL_P (decl))
- return false;
+ return omp_notice_threadprivate_variable (ctx, decl, NULL_TREE);
if (DECL_HAS_VALUE_EXPR_P (decl))
{
tree value = get_base_address (DECL_VALUE_EXPR (decl));
if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value))
- return false;
+ return omp_notice_threadprivate_variable (ctx, decl, value);
}
}
{
case OMP_CLAUSE_DEFAULT_NONE:
error ("%qE not specified in enclosing parallel",
- DECL_NAME (decl));
- error_at (ctx->location, "enclosing parallel");
+ DECL_NAME (lang_hooks.decls.omp_report_decl (decl)));
+ if ((ctx->region_type & ORT_TASK) != 0)
+ error_at (ctx->location, "enclosing task");
+ else
+ error_at (ctx->location, "enclosing parallel");
/* FALLTHRU */
case OMP_CLAUSE_DEFAULT_SHARED:
flags |= GOVD_SHARED;
break;
case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
/* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED. */
- gcc_assert (ctx->region_type == ORT_TASK);
+ gcc_assert ((ctx->region_type & ORT_TASK) != 0);
if (ctx->outer_context)
omp_notice_variable (ctx->outer_context, decl, in_code);
for (octx = ctx->outer_context; octx; octx = octx->outer_context)
gimple_seq body = NULL;
struct gimplify_ctx gctx;
- gimplify_scan_omp_clauses (&OMP_TASK_CLAUSES (expr), pre_p, ORT_TASK);
+ gimplify_scan_omp_clauses (&OMP_TASK_CLAUSES (expr), pre_p,
+ find_omp_clause (OMP_TASK_CLAUSES (expr),
+ OMP_CLAUSE_UNTIED)
+ ? ORT_UNTIED_TASK : ORT_TASK);
push_gimplify_context (&gctx);
{
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
lhs_addr, lhs_var);
+ case TRUTH_NOT_EXPR:
saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
lhs_addr, lhs_var);
break;
GIMPLE_TEST_F points to a function that takes a tree T and
returns nonzero if T is in the GIMPLE form requested by the
- caller. The GIMPLE predicates are in tree-gimple.c.
+ caller. The GIMPLE predicates are in gimple.c.
FALLBACK tells the function what sort of a temporary we want if
gimplification cannot produce an expression that complies with
|| gimple_test_f == is_gimple_mem_rhs_or_call
|| gimple_test_f == is_gimple_reg_rhs
|| gimple_test_f == is_gimple_reg_rhs_or_call
- || gimple_test_f == is_gimple_asm_val)
+ || gimple_test_f == is_gimple_asm_val
+ || gimple_test_f == is_gimple_mem_ref_addr)
gcc_assert (fallback & fb_rvalue);
else if (gimple_test_f == is_gimple_min_lval
|| gimple_test_f == is_gimple_lvalue)
else if (ret != GS_UNHANDLED)
break;
- ret = GS_OK;
+ /* Make sure that all the cases set 'ret' appropriately. */
+ ret = GS_UNHANDLED;
switch (TREE_CODE (*expr_p))
{
/* First deal with the special cases. */
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
+ ret = GS_OK;
}
break;
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
+ ret = GS_OK;
}
break;
/* Just strip a conversion to void (or in void context) and
try again. */
*expr_p = TREE_OPERAND (*expr_p, 0);
+ ret = GS_OK;
break;
}
break;
case INDIRECT_REF:
- *expr_p = fold_indirect_ref_loc (input_location, *expr_p);
- if (*expr_p != save_expr)
+ {
+ bool volatilep = TREE_THIS_VOLATILE (*expr_p);
+ bool notrap = TREE_THIS_NOTRAP (*expr_p);
+ tree saved_ptr_type = TREE_TYPE (TREE_OPERAND (*expr_p, 0));
+
+ *expr_p = fold_indirect_ref_loc (input_location, *expr_p);
+ if (*expr_p != save_expr)
+ {
+ ret = GS_OK;
+ break;
+ }
+
+ ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ is_gimple_reg, fb_rvalue);
+ if (ret == GS_ERROR)
+ break;
+
+ recalculate_side_effects (*expr_p);
+ *expr_p = fold_build2_loc (input_location, MEM_REF,
+ TREE_TYPE (*expr_p),
+ TREE_OPERAND (*expr_p, 0),
+ build_int_cst (saved_ptr_type, 0));
+ TREE_THIS_VOLATILE (*expr_p) = volatilep;
+ TREE_THIS_NOTRAP (*expr_p) = notrap;
+ ret = GS_OK;
break;
- /* else fall through. */
- case ALIGN_INDIRECT_REF:
- case MISALIGNED_INDIRECT_REF:
+ }
+
+ /* We arrive here through the various re-gimplifcation paths. */
+ case MEM_REF:
+ /* First try re-folding the whole thing. */
+ tmp = fold_binary (MEM_REF, TREE_TYPE (*expr_p),
+ TREE_OPERAND (*expr_p, 0),
+ TREE_OPERAND (*expr_p, 1));
+ if (tmp)
+ {
+ *expr_p = tmp;
+ recalculate_side_effects (*expr_p);
+ ret = GS_OK;
+ break;
+ }
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
- is_gimple_reg, fb_rvalue);
+ is_gimple_mem_ref_addr, fb_rvalue);
+ if (ret == GS_ERROR)
+ break;
recalculate_side_effects (*expr_p);
+ ret = GS_ALL_DONE;
break;
/* Constants need not be gimplified. */
if (fallback & fb_lvalue)
ret = GS_ALL_DONE;
else
- *expr_p = DECL_INITIAL (*expr_p);
+ {
+ *expr_p = DECL_INITIAL (*expr_p);
+ ret = GS_OK;
+ }
break;
case DECL_EXPR:
}
gimplify_seq_add_stmt (pre_p,
gimple_build_goto (GOTO_DESTINATION (*expr_p)));
+ ret = GS_ALL_DONE;
break;
case PREDICT_EXPR:
if (fallback == fb_none)
{
unsigned HOST_WIDE_INT ix;
- constructor_elt *ce;
+ tree val;
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);
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (*expr_p), ix, val)
+ if (TREE_SIDE_EFFECTS (val))
+ append_to_statement_list (val, &temp);
*expr_p = temp;
- ret = GS_OK;
+ ret = temp ? GS_OK : GS_ALL_DONE;
}
/* C99 code may assign to an array in a constructed
structure or union, and this has undefined behavior only
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
+ ret = GS_OK;
}
else
ret = GS_ALL_DONE;
{
enum gimplify_status r0 = GS_ALL_DONE, r1 = GS_ALL_DONE;
- if (TMR_SYMBOL (*expr_p))
- r0 = gimplify_expr (&TMR_SYMBOL (*expr_p), pre_p,
- post_p, is_gimple_lvalue, fb_either);
- else if (TMR_BASE (*expr_p))
+ if (TMR_BASE (*expr_p))
r0 = gimplify_expr (&TMR_BASE (*expr_p), pre_p,
- post_p, is_gimple_val, fb_either);
+ post_p, is_gimple_mem_ref_addr, fb_either);
if (TMR_INDEX (*expr_p))
r1 = gimplify_expr (&TMR_INDEX (*expr_p), pre_p,
post_p, is_gimple_val, fb_rvalue);
+ if (TMR_INDEX2 (*expr_p))
+ r1 = gimplify_expr (&TMR_INDEX2 (*expr_p), pre_p,
+ post_p, is_gimple_val, fb_rvalue);
/* TMR_STEP and TMR_OFFSET are always integer constants. */
ret = MIN (r0, r1);
}
gimple_test_f, fallback);
gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_val, fb_rvalue);
+ ret = GS_ALL_DONE;
}
break;
ret = gimplify_omp_atomic (expr_p, pre_p);
break;
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ /* Classified as tcc_expression. */
+ goto expr_2;
+
+ case FMA_EXPR:
+ /* Classified as tcc_expression. */
+ goto expr_3;
+
case POINTER_PLUS_EXPR:
/* Convert ((type *)A)+offset into &A->field_of_type_and_offset.
The second is gimple immediate saving a need for extra statement.
TREE_TYPE (*expr_p))))
{
*expr_p = tmp;
+ ret = GS_OK;
break;
}
/* Convert (void *)&a + 4 into (void *)&a[1]. */
0)))))
{
*expr_p = fold_convert (TREE_TYPE (*expr_p), tmp);
+ ret = GS_OK;
break;
}
/* FALLTHRU */
break;
}
+ expr_3:
+ {
+ enum gimplify_status r0, r1, r2;
+
+ 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);
+ r2 = gimplify_expr (&TREE_OPERAND (*expr_p, 2), pre_p,
+ post_p, is_gimple_val, fb_rvalue);
+
+ ret = MIN (MIN (r0, r1), r2);
+ break;
+ }
+
case tcc_declaration:
case tcc_constant:
ret = GS_ALL_DONE;
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;
+ gcc_unreachable ();
}
recalculate_side_effects (*expr_p);
break;
}
- /* If we replaced *expr_p, gimplify again. */
- if (ret == GS_OK && (*expr_p == NULL || *expr_p == save_expr))
- ret = GS_ALL_DONE;
+ gcc_assert (*expr_p || ret != GS_OK);
}
while (ret == GS_OK);
that temporary. */
tmp = build_fold_addr_expr_loc (input_location, *expr_p);
gimplify_expr (&tmp, pre_p, post_p, is_gimple_reg, fb_rvalue);
- *expr_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (tmp)), tmp);
+ *expr_p = build_simple_mem_ref (tmp);
}
else if ((fallback & fb_rvalue) && is_gimple_reg_rhs_or_call (*expr_p))
{
/* Ensure VLA bounds aren't removed, for -O0 they should be variables
with assigned stack slots, for -O1+ -g they should be tracked
by VTA. */
- if (TYPE_DOMAIN (type)
+ if (!(TYPE_NAME (type)
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_IGNORED_P (TYPE_NAME (type)))
+ && TYPE_DOMAIN (type)
&& INTEGRAL_TYPE_P (TYPE_DOMAIN (type)))
{
t = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
- for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
{
gimplify_one_sizepos (&DECL_FIELD_OFFSET (field), list_p);
*expr_p = create_tmp_var (type, NULL);
tmp = build1 (NOP_EXPR, type, expr);
stmt = gimplify_assign (*expr_p, tmp, stmt_p);
- if (EXPR_HAS_LOCATION (expr))
- gimple_set_location (stmt, EXPR_LOCATION (expr));
- else
- gimple_set_location (stmt, input_location);
+ gimple_set_location (stmt, EXPR_LOC_OR_HERE (expr));
}
}
gimple_bind_set_body (outer_bind, parm_stmts);
for (parm = DECL_ARGUMENTS (current_function_decl);
- parm; parm = TREE_CHAIN (parm))
+ parm; parm = DECL_CHAIN (parm))
if (DECL_HAS_VALUE_EXPR_P (parm))
{
DECL_HAS_VALUE_EXPR_P (parm) = 0;
gcc_assert (gimplify_ctxp == NULL);
#ifdef ENABLE_TYPES_CHECKING
- if (!errorcount && !sorrycount)
+ if (!seen_error ())
verify_types_in_gimple_seq (gimple_bind_body (outer_bind));
#endif
return outer_bind;
}
+typedef char *char_p; /* For DEF_VEC_P. */
+DEF_VEC_P(char_p);
+DEF_VEC_ALLOC_P(char_p,heap);
+
+/* Return whether we should exclude FNDECL from instrumentation. */
+
+static bool
+flag_instrument_functions_exclude_p (tree fndecl)
+{
+ VEC(char_p,heap) *vec;
+
+ vec = (VEC(char_p,heap) *) flag_instrument_functions_exclude_functions;
+ if (VEC_length (char_p, vec) > 0)
+ {
+ const char *name;
+ int i;
+ char *s;
+
+ name = lang_hooks.decl_printable_name (fndecl, 0);
+ FOR_EACH_VEC_ELT (char_p, vec, i, s)
+ if (strstr (name, s) != NULL)
+ return true;
+ }
+
+ vec = (VEC(char_p,heap) *) flag_instrument_functions_exclude_files;
+ if (VEC_length (char_p, vec) > 0)
+ {
+ const char *name;
+ int i;
+ char *s;
+
+ name = DECL_SOURCE_FILE (fndecl);
+ FOR_EACH_VEC_ELT (char_p, vec, i, s)
+ if (strstr (name, s) != NULL)
+ return true;
+ }
+
+ return false;
+}
+
/* Entry point to the gimplification pass. FNDECL is the FUNCTION_DECL
node for the function we want to gimplify.
else
push_struct_function (fndecl);
- for (parm = DECL_ARGUMENTS (fndecl); parm ; parm = TREE_CHAIN (parm))
+ for (parm = DECL_ARGUMENTS (fndecl); parm ; parm = DECL_CHAIN (parm))
{
/* Preliminarily mark non-addressed complex variables as eligible
for promotion to gimple registers. We'll transform their uses
gimple new_bind;
gimple tf;
gimple_seq cleanup = NULL, body = NULL;
-
+ tree tmp_var;
+ gimple call;
+
+ x = implicit_built_in_decls[BUILT_IN_RETURN_ADDRESS];
+ call = gimple_build_call (x, 0);
+ tmp_var = create_tmp_var (ptr_type_node, "return_addr");
+ gimple_call_set_lhs (call, tmp_var);
+ gimplify_seq_add_stmt (&cleanup, call);
x = implicit_built_in_decls[BUILT_IN_PROFILE_FUNC_EXIT];
- gimplify_seq_add_stmt (&cleanup, gimple_build_call (x, 0));
+ call = gimple_build_call (x, 2,
+ build_fold_addr_expr (current_function_decl),
+ tmp_var);
+ gimplify_seq_add_stmt (&cleanup, call);
tf = gimple_build_try (seq, cleanup, GIMPLE_TRY_FINALLY);
+ x = implicit_built_in_decls[BUILT_IN_RETURN_ADDRESS];
+ call = gimple_build_call (x, 0);
+ tmp_var = create_tmp_var (ptr_type_node, "return_addr");
+ gimple_call_set_lhs (call, tmp_var);
+ gimplify_seq_add_stmt (&body, call);
x = implicit_built_in_decls[BUILT_IN_PROFILE_FUNC_ENTER];
- gimplify_seq_add_stmt (&body, gimple_build_call (x, 0));
+ call = gimple_build_call (x, 2,
+ build_fold_addr_expr (current_function_decl),
+ tmp_var);
+ gimplify_seq_add_stmt (&body, call);
gimplify_seq_add_stmt (&body, tf);
new_bind = gimple_build_bind (NULL, body, gimple_bind_block (bind));
/* Clear the block for BIND, since it is no longer directly inside
}
-/* 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
+/* Expands EXPR to list of gimple statements STMTS. GIMPLE_TEST_F specifies
+ the predicate that will hold for the result. If VAR is not NULL, make the
base variable of the final destination be VAR if suitable. */
tree
-force_gimple_operand (tree expr, gimple_seq *stmts, bool simple, tree var)
+force_gimple_operand_1 (tree expr, gimple_seq *stmts,
+ gimple_predicate gimple_test_f, tree var)
{
tree t;
enum gimplify_status ret;
- gimple_predicate gimple_test_f;
struct gimplify_ctx gctx;
*stmts = NULL;
- if (is_gimple_val (expr))
+ /* gimple_test_f might be more strict than is_gimple_val, make
+ sure we pass both. Just checking gimple_test_f doesn't work
+ because most gimple predicates do not work recursively. */
+ if (is_gimple_val (expr)
+ && (*gimple_test_f) (expr))
return expr;
- gimple_test_f = simple ? is_gimple_val : is_gimple_reg_rhs;
-
push_gimplify_context (&gctx);
gimplify_ctxp->into_ssa = gimple_in_ssa_p (cfun);
gimplify_ctxp->allow_rhs_cond_expr = true;
}
if (gimple_referenced_vars (cfun))
- for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
+ for (t = gimplify_ctxp->temps; t ; t = DECL_CHAIN (t))
add_referenced_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 at GSI. If BEFORE is true.
- the statements are appended before GSI, otherwise they are appended after
- it. M specifies the way GSI moves after insertion (GSI_SAME_STMT or
- GSI_CONTINUE_LINKING are the usual values). */
+/* 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, gimple_seq *stmts, bool simple, tree var)
+{
+ return force_gimple_operand_1 (expr, stmts,
+ simple ? is_gimple_val : is_gimple_reg_rhs,
+ var);
+}
+
+/* Invokes force_gimple_operand_1 for EXPR with parameters GIMPLE_TEST_F
+ and VAR. If some statements are produced, emits them at GSI.
+ If BEFORE is true. the statements are appended before GSI, otherwise
+ they are appended after it. M specifies the way GSI moves after
+ insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING are the usual values). */
tree
-force_gimple_operand_gsi (gimple_stmt_iterator *gsi, tree expr,
- bool simple_p, tree var, bool before,
- enum gsi_iterator_update m)
+force_gimple_operand_gsi_1 (gimple_stmt_iterator *gsi, tree expr,
+ gimple_predicate gimple_test_f,
+ tree var, bool before,
+ enum gsi_iterator_update m)
{
gimple_seq stmts;
- expr = force_gimple_operand (expr, &stmts, simple_p, var);
+ expr = force_gimple_operand_1 (expr, &stmts, gimple_test_f, var);
if (!gimple_seq_empty_p (stmts))
{
return expr;
}
+/* Invokes force_gimple_operand_1 for EXPR with parameter VAR.
+ 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 some statements are
+ produced, emits them at GSI. If BEFORE is true, the statements are
+ appended before GSI, otherwise they are appended after it. M specifies
+ the way GSI moves after insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING
+ are the usual values). */
+
+tree
+force_gimple_operand_gsi (gimple_stmt_iterator *gsi, tree expr,
+ bool simple_p, tree var, bool before,
+ enum gsi_iterator_update m)
+{
+ return force_gimple_operand_gsi_1 (gsi, expr,
+ simple_p
+ ? is_gimple_val : is_gimple_reg_rhs,
+ var, before, m);
+}
+
+
#include "gt-gimplify.h"