/* Tree lowering pass. This pass converts the GENERIC functions-as-trees
tree representation into the GIMPLE form.
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008
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>.
GOVD_REDUCTION = 64,
GOVD_LOCAL = 128,
GOVD_DEBUG_PRIVATE = 256,
+ GOVD_PRIVATE_OUTER_REF = 512,
GOVD_DATA_SHARE_CLASS = (GOVD_SHARED | GOVD_PRIVATE | GOVD_FIRSTPRIVATE
| GOVD_LASTPRIVATE | GOVD_REDUCTION | GOVD_LOCAL)
};
+enum omp_region_type
+{
+ ORT_WORKSHARE = 0,
+ ORT_TASK = 1,
+ ORT_PARALLEL = 2,
+ ORT_COMBINED_PARALLEL = 3
+};
+
struct gimplify_omp_ctx
{
struct gimplify_omp_ctx *outer_context;
struct pointer_set_t *privatized_types;
location_t location;
enum omp_clause_default_kind default_kind;
- bool is_parallel;
- bool is_combined_parallel;
+ enum omp_region_type region_type;
};
struct gimplify_ctx
int conditions;
bool save_stack;
bool into_ssa;
+ bool allow_rhs_cond_expr;
};
static struct gimplify_ctx *gimplify_ctxp;
/* Tear down a context for the gimplifier. If BODY is non-null, then
put the temporaries into the outer BIND_EXPR. Otherwise, put them
- in the unexpanded_var_list. */
+ in the local_decls. */
void
pop_gimplify_context (tree body)
/* Create a new omp construct that deals with variable remapping. */
static struct gimplify_omp_ctx *
-new_omp_context (bool is_parallel, bool is_combined_parallel)
+new_omp_context (enum omp_region_type region_type)
{
struct gimplify_omp_ctx *c;
c->variables = splay_tree_new (splay_tree_compare_decl_uid, 0, 0);
c->privatized_types = pointer_set_create ();
c->location = input_location;
- c->is_parallel = is_parallel;
- c->is_combined_parallel = is_combined_parallel;
- c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
+ c->region_type = region_type;
+ if (region_type != ORT_TASK)
+ c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
+ else
+ c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
return c;
}
{
tree *pdecl = (tree *) data;
+ /* We are only looking for pointers at the same level as the
+ original tree; we must not look through any indirections.
+ Returning anything other than NULL_TREE will cause the caller to
+ not find a base. */
+ if (REFERENCE_CLASS_P (*tp))
+ return *tp;
+
if (DECL_P (*tp) && POINTER_TYPE_P (TREE_TYPE (*tp)))
{
if (*pdecl)
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. */
+/* Find the single DECL of pointer type in the tree T, used directly
+ rather than via an indirection, and return it. If there are zero
+ or more than one such DECLs, return NULL. */
static tree
find_single_pointer_decl (tree t)
{
/* find_single_pointer_decl_1 returns a nonzero value, causing
walk_tree to return a nonzero value, to indicate that it
- found more than one pointer DECL. */
+ found more than one pointer DECL or that it found an
+ indirection. */
return NULL_TREE;
}
if (gimplify_omp_ctxp)
{
struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;
- while (ctx && !ctx->is_parallel)
+ while (ctx && ctx->region_type == ORT_WORKSHARE)
ctx = ctx->outer_context;
if (ctx)
omp_add_variable (ctx, tmp, GOVD_LOCAL | GOVD_SEEN);
{
/* Discard empty ranges. */
tree high = CASE_HIGH (elt);
- if (high && INT_CST_LT (high, low))
+ if (high && tree_int_cst_lt (high, low))
remove_element = TRUE;
}
else
gimplify_conversion (tree *expr_p)
{
tree tem;
- gcc_assert (TREE_CODE (*expr_p) == NOP_EXPR
- || TREE_CODE (*expr_p) == CONVERT_EXPR);
+ gcc_assert (CONVERT_EXPR_P (*expr_p));
/* Then strip away all but the outermost conversion. */
STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
/* If we still have a conversion at the toplevel,
then canonicalize some constructs. */
- if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
+ if (CONVERT_EXPR_P (*expr_p))
{
tree sub = TREE_OPERAND (*expr_p, 0);
/* If the function is "const" or "pure", then clear TREE_SIDE_EFFECTS on its
decl. This allows us to eliminate redundant or useless
calls to "const" functions. */
- if (TREE_CODE (*expr_p) == CALL_EXPR
- && (call_expr_flags (*expr_p) & (ECF_CONST | ECF_PURE)))
- TREE_SIDE_EFFECTS (*expr_p) = 0;
-
+ if (TREE_CODE (*expr_p) == CALL_EXPR)
+ {
+ int flags = call_expr_flags (*expr_p);
+ if (flags & (ECF_CONST | ECF_PURE)
+ /* An infinite loop is considered a side effect. */
+ && !(flags & (ECF_LOOPING_CONST_OR_PURE)))
+ TREE_SIDE_EFFECTS (*expr_p) = 0;
+ }
return ret;
}
}
}
+/* Given a conditional expression *EXPR_P without side effects, gimplify
+ its operands. New statements are inserted to PRE_P. */
+
+static enum gimplify_status
+gimplify_pure_cond_expr (tree *expr_p, tree *pre_p)
+{
+ tree expr = *expr_p, cond;
+ enum gimplify_status ret, tret;
+ enum tree_code code;
+
+ cond = gimple_boolify (COND_EXPR_COND (expr));
+
+ /* We need to handle && and || specially, as their gimplification
+ creates pure cond_expr, thus leading to an infinite cycle otherwise. */
+ code = TREE_CODE (cond);
+ if (code == TRUTH_ANDIF_EXPR)
+ TREE_SET_CODE (cond, TRUTH_AND_EXPR);
+ else if (code == TRUTH_ORIF_EXPR)
+ TREE_SET_CODE (cond, TRUTH_OR_EXPR);
+ ret = gimplify_expr (&cond, pre_p, NULL,
+ is_gimple_condexpr, fb_rvalue);
+ COND_EXPR_COND (*expr_p) = cond;
+
+ tret = gimplify_expr (&COND_EXPR_THEN (expr), pre_p, NULL,
+ is_gimple_val, fb_rvalue);
+ ret = MIN (ret, tret);
+ tret = gimplify_expr (&COND_EXPR_ELSE (expr), pre_p, NULL,
+ is_gimple_val, fb_rvalue);
+
+ return MIN (ret, tret);
+}
+
+/* Returns true if evaluating EXPR could trap.
+ EXPR is GENERIC, while tree_could_trap_p can be called
+ only on GIMPLE. */
+
+static bool
+generic_expr_could_trap_p (tree expr)
+{
+ unsigned i, n;
+
+ if (!expr || is_gimple_val (expr))
+ return false;
+
+ if (!EXPR_P (expr) || tree_could_trap_p (expr))
+ return true;
+
+ n = TREE_OPERAND_LENGTH (expr);
+ for (i = 0; i < n; i++)
+ if (generic_expr_could_trap_p (TREE_OPERAND (expr, i)))
+ return true;
+
+ return false;
+}
+
/* Convert the conditional expression pointed to by EXPR_P '(p) ? a : b;'
into
{
tree result;
- if ((fallback & fb_lvalue) == 0)
+ /* If an rvalue is ok or we do not require an lvalue, avoid creating
+ an addressable temporary. */
+ if (((fallback & fb_rvalue)
+ || !(fallback & fb_lvalue))
+ && !TREE_ADDRESSABLE (type))
{
+ if (gimplify_ctxp->allow_rhs_cond_expr
+ /* If either branch has side effects or could trap, it can't be
+ evaluated unconditionally. */
+ && !TREE_SIDE_EFFECTS (TREE_OPERAND (*expr_p, 1))
+ && !generic_expr_could_trap_p (TREE_OPERAND (*expr_p, 1))
+ && !TREE_SIDE_EFFECTS (TREE_OPERAND (*expr_p, 2))
+ && !generic_expr_could_trap_p (TREE_OPERAND (*expr_p, 2)))
+ return gimplify_pure_cond_expr (expr_p, pre_p);
+
result = tmp2 = tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
ret = GS_ALL_DONE;
}
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 = build3 (COND_EXPR, void_type_node, TREE_OPERAND (expr, 0),
{
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 value is constant, then there's nothing to pre-evaluate. */
+ if (TREE_CONSTANT (*expr_p))
+ {
+ /* Ensure it does not have side effects, it might contain a reference to
+ the object we're initializing. */
+ gcc_assert (!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)))
if (array_elt_type)
{
+ /* Do not use bitsizetype for ARRAY_REF indices. */
+ if (TYPE_DOMAIN (TREE_TYPE (object)))
+ purpose = fold_convert (TREE_TYPE (TYPE_DOMAIN (TREE_TYPE (object))),
+ purpose);
cref = build4 (ARRAY_REF, array_elt_type, unshare_expr (object),
purpose, NULL_TREE, NULL_TREE);
}
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. */
+ original MODIFY_EXPR, we just remove all of the constructor elements.
+
+ If NOTIFY_TEMP_CREATION is true, do not gimplify, just return
+ GS_ERROR if we would have to create a temporary when gimplifying
+ this constructor. Otherwise, return GS_OK.
+
+ If NOTIFY_TEMP_CREATION is false, just do the gimplification. */
static enum gimplify_status
gimplify_init_constructor (tree *expr_p, tree *pre_p,
- tree *post_p, bool want_value)
+ tree *post_p, bool want_value,
+ bool notify_temp_creation)
{
tree object;
tree ctor = GENERIC_TREE_OPERAND (*expr_p, 1);
if (TREE_CODE (ctor) != CONSTRUCTOR)
return GS_UNHANDLED;
- ret = gimplify_expr (&GENERIC_TREE_OPERAND (*expr_p, 0), pre_p, post_p,
- is_gimple_lvalue, fb_lvalue);
- if (ret == GS_ERROR)
- return ret;
+ if (!notify_temp_creation)
+ {
+ ret = gimplify_expr (&GENERIC_TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+ is_gimple_lvalue, fb_lvalue);
+ if (ret == GS_ERROR)
+ return ret;
+ }
object = GENERIC_TREE_OPERAND (*expr_p, 0);
elts = CONSTRUCTOR_ELTS (ctor);
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;
+ {
+ if (notify_temp_creation)
+ return GS_OK;
+ break;
+ }
/* Fetch information about the constructor to direct later processing.
We might want to make static versions of it in various cases, and
&& TREE_READONLY (object)
&& TREE_CODE (object) == VAR_DECL)
{
+ if (notify_temp_creation)
+ return GS_ERROR;
DECL_INITIAL (object) = ctor;
TREE_STATIC (object) = 1;
if (!DECL_NAME (object))
if (size > 0 && !can_move_by_pieces (size, align))
{
- tree new = create_tmp_var_raw (type, "C");
+ tree new;
+
+ if (notify_temp_creation)
+ return GS_ERROR;
+
+ new = create_tmp_var_raw (type, "C");
gimple_add_tmp_var (new);
TREE_STATIC (new) = 1;
}
}
+ if (notify_temp_creation)
+ return GS_OK;
+
/* If there are nonzero elements, pre-evaluate to capture elements
overlapping with the lhs into temporaries. We must do this before
clearing to fetch the values before they are zeroed-out. */
{
tree r, i;
+ if (notify_temp_creation)
+ return GS_OK;
+
/* Extract the real and imaginary parts out of the ctor. */
gcc_assert (VEC_length (constructor_elt, elts) == 2);
r = VEC_index (constructor_elt, elts, 0)->value;
unsigned HOST_WIDE_INT ix;
constructor_elt *ce;
+ if (notify_temp_creation)
+ return GS_OK;
+
/* Go ahead and simplify constant constructors to VECTOR_CST. */
if (TREE_CONSTANT (ctor))
{
break;
TREE_CONSTANT (ctor) = 0;
- TREE_INVARIANT (ctor) = 0;
}
/* Vector types use CONSTRUCTOR all the way through gimple
/* 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. */
+ possible. 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
+gimple_fold_indirect_ref (tree t)
{
tree type = TREE_TYPE (TREE_TYPE (t));
tree sub = t;
/* *&p => p */
if (useless_type_conversion_p (type, optype))
return op;
+
/* *(foo *)&fooarray => fooarray[0] */
- else if (TREE_CODE (optype) == ARRAY_TYPE
- && useless_type_conversion_p (type, TREE_TYPE (optype)))
+ if (TREE_CODE (optype) == ARRAY_TYPE
+ && useless_type_conversion_p (type, TREE_TYPE (optype)))
{
tree type_domain = TYPE_DOMAIN (optype);
tree min_val = size_zero_node;
tree type_domain;
tree min_val = size_zero_node;
tree osub = sub;
- sub = fold_indirect_ref_rhs (sub);
+ sub = gimple_fold_indirect_ref (sub);
if (! sub)
sub = build1 (INDIRECT_REF, TREE_TYPE (subtype), osub);
type_domain = TYPE_DOMAIN (TREE_TYPE (sub));
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. */
+/* 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
+gimple_fold_indirect_ref_rhs (tree t)
+{
+ return gimple_fold_indirect_ref (t);
+}
+
+/* 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. */
static enum gimplify_status
gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
while (ret != GS_UNHANDLED)
switch (TREE_CODE (*from_p))
{
+ case VAR_DECL:
+ /* If we're assigning from a constant constructor, move the
+ constructor expression to the RHS of the MODIFY_EXPR. */
+ if (DECL_INITIAL (*from_p)
+ && TYPE_READONLY (TREE_TYPE (*from_p))
+ && !TREE_THIS_VOLATILE (*from_p)
+ && TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
+ {
+ tree old_from = *from_p;
+
+ /* 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. If so, revert the change. */
+ ret = gimplify_init_constructor (expr_p, NULL, NULL, false, true);
+ if (ret == GS_ERROR)
+ {
+ *from_p = old_from;
+ /* Fall through. */
+ }
+ else
+ {
+ ret = GS_OK;
+ break;
+ }
+ }
+ ret = GS_UNHANDLED;
+ break;
case INDIRECT_REF:
{
/* If we have code like
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));
+ tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
if (t)
{
*from_p = t;
references somehow. */
tree init = TARGET_EXPR_INITIAL (*from_p);
- if (!VOID_TYPE_P (TREE_TYPE (init)))
+ if (init
+ && !VOID_TYPE_P (TREE_TYPE (init)))
{
*from_p = init;
ret = GS_OK;
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);
+ 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
tree result = *to_p;
ret = gimplify_expr (&result, pre_p, post_p,
- is_gimple_min_lval, fb_lvalue);
+ is_gimple_lvalue, fb_lvalue);
if (ret != GS_ERROR)
ret = GS_OK;
/* 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_GIMPLE_REG_P set. */
+ DECL_GIMPLE_REG_P set.
+
+ IMPORTANT NOTE: This promotion is performed by introducing a load of the
+ other, unmodified part of the complex object just before the total store.
+ As a consequence, if the object is still uninitialized, an undefined value
+ will be loaded into a register, which may result in a spurious exception
+ if the register is floating-point and the value happens to be a signaling
+ NaN for example. Then the fully-fledged complex operations lowering pass
+ followed by a DCE pass are necessary in order to fix things up. */
static enum gimplify_status
gimplify_modify_expr_complex_part (tree *expr_p, tree *pre_p, bool want_value)
|| TREE_CODE (*expr_p) == GIMPLE_MODIFY_STMT
|| TREE_CODE (*expr_p) == INIT_EXPR);
+ /* Insert pointer conversions required by the middle-end that are not
+ required by the frontend. This fixes middle-end type checking for
+ for example gcc.dg/redecl-6.c. */
+ if (POINTER_TYPE_P (TREE_TYPE (*to_p))
+ && lang_hooks.types_compatible_p (TREE_TYPE (*to_p), TREE_TYPE (*from_p)))
+ {
+ STRIP_USELESS_TYPE_CONVERSION (*from_p);
+ if (!useless_type_conversion_p (TREE_TYPE (*to_p), TREE_TYPE (*from_p)))
+ *from_p = fold_convert (TREE_TYPE (*to_p), *from_p);
+ }
+
/* 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,
want_value);
if (TREE_CODE (op0) == INDIRECT_REF)
goto do_indirect_ref;
- /* Make sure TREE_INVARIANT, TREE_CONSTANT, and TREE_SIDE_EFFECTS
- is set properly. */
+ /* Make sure TREE_CONSTANT and TREE_SIDE_EFFECTS are set properly. */
recompute_tree_invariant_for_addr_expr (expr);
mark_addressable (TREE_OPERAND (expr, 0));
else
return;
}
- else if (ctx->is_parallel)
+ else if (ctx->region_type != ORT_WORKSHARE)
omp_add_variable (ctx, decl, GOVD_FIRSTPRIVATE);
ctx = ctx->outer_context;
if (n == NULL)
{
enum omp_clause_default_kind default_kind, kind;
+ struct gimplify_omp_ctx *octx;
- if (!ctx->is_parallel)
+ if (ctx->region_type == ORT_WORKSHARE)
goto do_outer;
/* ??? Some compiler-generated variables (like SAVE_EXPRs) could be
case OMP_CLAUSE_DEFAULT_PRIVATE:
flags |= GOVD_PRIVATE;
break;
+ case OMP_CLAUSE_DEFAULT_FIRSTPRIVATE:
+ flags |= GOVD_FIRSTPRIVATE;
+ break;
+ case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+ /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED. */
+ gcc_assert (ctx->region_type == ORT_TASK);
+ if (ctx->outer_context)
+ omp_notice_variable (ctx->outer_context, decl, in_code);
+ for (octx = ctx->outer_context; octx; octx = octx->outer_context)
+ {
+ splay_tree_node n2;
+
+ n2 = splay_tree_lookup (octx->variables, (splay_tree_key) decl);
+ if (n2 && (n2->value & GOVD_DATA_SHARE_CLASS) != GOVD_SHARED)
+ {
+ flags |= GOVD_FIRSTPRIVATE;
+ break;
+ }
+ if ((octx->region_type & ORT_PARALLEL) != 0)
+ break;
+ }
+ if (flags & GOVD_FIRSTPRIVATE)
+ break;
+ if (octx == NULL
+ && (TREE_CODE (decl) == PARM_DECL
+ || (!is_global_var (decl)
+ && DECL_CONTEXT (decl) == current_function_decl)))
+ {
+ flags |= GOVD_FIRSTPRIVATE;
+ break;
+ }
+ flags |= GOVD_SHARED;
+ break;
default:
gcc_unreachable ();
}
+ if ((flags & GOVD_PRIVATE)
+ && lang_hooks.decls.omp_private_outer_ref (decl))
+ flags |= GOVD_PRIVATE_OUTER_REF;
+
omp_add_variable (ctx, decl, flags);
shared = (flags & GOVD_SHARED) != 0;
do_outer:
/* If the variable is private in the current context, then we don't
need to propagate anything to an outer context. */
- if (flags & GOVD_PRIVATE)
+ if ((flags & GOVD_PRIVATE) && !(flags & GOVD_PRIVATE_OUTER_REF))
return ret;
if (ctx->outer_context
&& omp_notice_variable (ctx->outer_context, decl, in_code))
}
else if ((n->value & GOVD_EXPLICIT) != 0
&& (ctx == gimplify_omp_ctxp
- || (ctx->is_combined_parallel
+ || (ctx->region_type == ORT_COMBINED_PARALLEL
&& gimplify_omp_ctxp->outer_context == ctx)))
{
if ((n->value & GOVD_FIRSTPRIVATE) != 0)
return true;
}
- if (ctx->is_parallel)
+ if (ctx->region_type != ORT_WORKSHARE)
return false;
else if (ctx->outer_context)
return omp_is_private (ctx->outer_context, decl);
if (n != NULL)
return (n->value & GOVD_SHARED) == 0;
}
- while (!ctx->is_parallel);
+ while (ctx->region_type == ORT_WORKSHARE);
return false;
}
and previous omp contexts. */
static void
-gimplify_scan_omp_clauses (tree *list_p, tree *pre_p, bool in_parallel,
- bool in_combined_parallel)
+gimplify_scan_omp_clauses (tree *list_p, tree *pre_p,
+ enum omp_region_type region_type)
{
struct gimplify_omp_ctx *ctx, *outer_ctx;
tree c;
- ctx = new_omp_context (in_parallel, in_combined_parallel);
+ ctx = new_omp_context (region_type);
outer_ctx = ctx->outer_context;
while ((c = *list_p) != NULL)
{
case OMP_CLAUSE_PRIVATE:
flags = GOVD_PRIVATE | GOVD_EXPLICIT;
- notice_outer = false;
+ if (lang_hooks.decls.omp_private_outer_ref (OMP_CLAUSE_DECL (c)))
+ {
+ flags |= GOVD_PRIVATE_OUTER_REF;
+ OMP_CLAUSE_PRIVATE_OUTER_REF (c) = 1;
+ }
+ else
+ notice_outer = false;
goto do_add;
case OMP_CLAUSE_SHARED:
flags = GOVD_SHARED | GOVD_EXPLICIT;
pop_gimplify_context (OMP_CLAUSE_REDUCTION_MERGE (c));
gimplify_omp_ctxp = outer_ctx;
}
+ else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE
+ && OMP_CLAUSE_LASTPRIVATE_STMT (c))
+ {
+ gimplify_omp_ctxp = ctx;
+ push_gimplify_context ();
+ if (TREE_CODE (OMP_CLAUSE_LASTPRIVATE_STMT (c)) != BIND_EXPR)
+ {
+ tree bind = build3 (BIND_EXPR, void_type_node, NULL,
+ NULL, NULL);
+ TREE_SIDE_EFFECTS (bind) = 1;
+ BIND_EXPR_BODY (bind) = OMP_CLAUSE_LASTPRIVATE_STMT (c);
+ OMP_CLAUSE_LASTPRIVATE_STMT (c) = bind;
+ }
+ gimplify_stmt (&OMP_CLAUSE_LASTPRIVATE_STMT (c));
+ pop_gimplify_context (OMP_CLAUSE_LASTPRIVATE_STMT (c));
+ gimplify_omp_ctxp = outer_ctx;
+ }
if (notice_outer)
goto do_notice;
break;
if (outer_ctx)
omp_notice_variable (outer_ctx, decl, true);
if (check_non_private
- && !in_parallel
+ && region_type == ORT_WORKSHARE
&& omp_check_private (ctx, decl))
{
error ("%s variable %qs is private in outer context",
case OMP_CLAUSE_NOWAIT:
case OMP_CLAUSE_ORDERED:
+ case OMP_CLAUSE_UNTIED:
+ case OMP_CLAUSE_COLLAPSE:
break;
case OMP_CLAUSE_DEFAULT:
OMP_CLAUSE_CHAIN (clause) = *list_p;
if (private_debug)
OMP_CLAUSE_PRIVATE_DEBUG (clause) = 1;
+ else if (code == OMP_CLAUSE_PRIVATE && (flags & GOVD_PRIVATE_OUTER_REF))
+ OMP_CLAUSE_PRIVATE_OUTER_REF (clause) = 1;
*list_p = clause;
+ lang_hooks.decls.omp_finish_clause (clause);
return 0;
}
case OMP_CLAUSE_NOWAIT:
case OMP_CLAUSE_ORDERED:
case OMP_CLAUSE_DEFAULT:
+ case OMP_CLAUSE_UNTIED:
+ case OMP_CLAUSE_COLLAPSE:
break;
default:
{
tree expr = *expr_p;
- gimplify_scan_omp_clauses (&OMP_PARALLEL_CLAUSES (expr), pre_p, true,
- OMP_PARALLEL_COMBINED (expr));
+ gimplify_scan_omp_clauses (&OMP_PARALLEL_CLAUSES (expr), pre_p,
+ OMP_PARALLEL_COMBINED (expr)
+ ? ORT_COMBINED_PARALLEL
+ : ORT_PARALLEL);
push_gimplify_context ();
return GS_ALL_DONE;
}
+/* Gimplify the contents of an OMP_TASK statement. This involves
+ gimplification of the body, as well as scanning the body for used
+ variables. We need to do this scan now, because variable-sized
+ decls will be decomposed during gimplification. */
+
+static enum gimplify_status
+gimplify_omp_task (tree *expr_p, tree *pre_p)
+{
+ tree expr = *expr_p;
+
+ gimplify_scan_omp_clauses (&OMP_TASK_CLAUSES (expr), pre_p, ORT_TASK);
+
+ push_gimplify_context ();
+
+ gimplify_stmt (&OMP_TASK_BODY (expr));
+
+ if (TREE_CODE (OMP_TASK_BODY (expr)) == BIND_EXPR)
+ pop_gimplify_context (OMP_TASK_BODY (expr));
+ else
+ pop_gimplify_context (NULL_TREE);
+
+ gimplify_adjust_omp_clauses (&OMP_TASK_CLAUSES (expr));
+
+ return GS_ALL_DONE;
+}
+
/* Gimplify the gross structure of an OMP_FOR statement. */
static enum gimplify_status
gimplify_omp_for (tree *expr_p, tree *pre_p)
{
- tree for_stmt, decl, var, t;
+ tree for_stmt, decl, var, t, bodylist;
enum gimplify_status ret = GS_OK;
tree body, init_decl = NULL_TREE;
+ int i;
for_stmt = *expr_p;
- gimplify_scan_omp_clauses (&OMP_FOR_CLAUSES (for_stmt), pre_p, false, false);
+ gimplify_scan_omp_clauses (&OMP_FOR_CLAUSES (for_stmt), pre_p,
+ ORT_WORKSHARE);
- t = OMP_FOR_INIT (for_stmt);
- gcc_assert (TREE_CODE (t) == MODIFY_EXPR
- || TREE_CODE (t) == GIMPLE_MODIFY_STMT);
- decl = GENERIC_TREE_OPERAND (t, 0);
- gcc_assert (DECL_P (decl));
- gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (decl)));
+ /* If OMP_FOR is re-gimplified, ensure all variables in pre-body
+ are noticed. */
+ gimplify_stmt (&OMP_FOR_PRE_BODY (for_stmt));
- /* Make sure the iteration variable is private. */
- if (omp_is_private (gimplify_omp_ctxp, decl))
- omp_notice_variable (gimplify_omp_ctxp, decl, true);
- else
- omp_add_variable (gimplify_omp_ctxp, decl, GOVD_PRIVATE | GOVD_SEEN);
+ bodylist = alloc_stmt_list ();
- /* If DECL is not a gimple register, create a temporary variable to act as an
- iteration counter. This is valid, since DECL cannot be modified in the
- body of the loop. */
- if (!is_gimple_reg (decl))
+ gcc_assert (TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt))
+ == TREE_VEC_LENGTH (OMP_FOR_COND (for_stmt)));
+ gcc_assert (TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt))
+ == TREE_VEC_LENGTH (OMP_FOR_INCR (for_stmt)));
+ for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)); i++)
{
- var = create_tmp_var (TREE_TYPE (decl), get_name (decl));
- GENERIC_TREE_OPERAND (t, 0) = var;
+ t = TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i);
+ gcc_assert (TREE_CODE (t) == MODIFY_EXPR
+ || TREE_CODE (t) == GIMPLE_MODIFY_STMT);
+ decl = GENERIC_TREE_OPERAND (t, 0);
+ gcc_assert (DECL_P (decl));
+ gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (decl))
+ || POINTER_TYPE_P (TREE_TYPE (decl)));
- init_decl = build_gimple_modify_stmt (decl, var);
- omp_add_variable (gimplify_omp_ctxp, var, GOVD_PRIVATE | GOVD_SEEN);
- }
- else
- var = decl;
+ /* Make sure the iteration variable is private. */
+ if (omp_is_private (gimplify_omp_ctxp, decl))
+ omp_notice_variable (gimplify_omp_ctxp, decl, true);
+ else
+ omp_add_variable (gimplify_omp_ctxp, decl, GOVD_PRIVATE | GOVD_SEEN);
- ret |= gimplify_expr (&GENERIC_TREE_OPERAND (t, 1),
- &OMP_FOR_PRE_BODY (for_stmt),
- NULL, is_gimple_val, fb_rvalue);
+ /* If DECL is not a gimple register, create a temporary variable to act
+ as an iteration counter. This is valid, since DECL cannot be
+ modified in the body of the loop. */
+ if (!is_gimple_reg (decl))
+ {
+ var = create_tmp_var (TREE_TYPE (decl), get_name (decl));
+ GENERIC_TREE_OPERAND (t, 0) = var;
- tree_to_gimple_tuple (&OMP_FOR_INIT (for_stmt));
+ init_decl = build_gimple_modify_stmt (decl, var);
+ omp_add_variable (gimplify_omp_ctxp, var, GOVD_PRIVATE | GOVD_SEEN);
+ }
+ else
+ var = decl;
- t = OMP_FOR_COND (for_stmt);
- gcc_assert (COMPARISON_CLASS_P (t));
- gcc_assert (GENERIC_TREE_OPERAND (t, 0) == decl);
- TREE_OPERAND (t, 0) = var;
+ ret |= gimplify_expr (&GENERIC_TREE_OPERAND (t, 1),
+ &OMP_FOR_PRE_BODY (for_stmt),
+ NULL, is_gimple_val, fb_rvalue);
- ret |= gimplify_expr (&GENERIC_TREE_OPERAND (t, 1),
- &OMP_FOR_PRE_BODY (for_stmt),
- NULL, is_gimple_val, fb_rvalue);
+ tree_to_gimple_tuple (&TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i));
- tree_to_gimple_tuple (&OMP_FOR_INCR (for_stmt));
- t = OMP_FOR_INCR (for_stmt);
- switch (TREE_CODE (t))
- {
- case PREINCREMENT_EXPR:
- case POSTINCREMENT_EXPR:
- t = build_int_cst (TREE_TYPE (decl), 1);
- t = build2 (PLUS_EXPR, TREE_TYPE (decl), var, t);
- t = build_gimple_modify_stmt (var, t);
- OMP_FOR_INCR (for_stmt) = t;
- break;
+ t = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
+ gcc_assert (COMPARISON_CLASS_P (t));
+ gcc_assert (GENERIC_TREE_OPERAND (t, 0) == decl);
+ TREE_OPERAND (t, 0) = var;
- case PREDECREMENT_EXPR:
- case POSTDECREMENT_EXPR:
- t = build_int_cst (TREE_TYPE (decl), -1);
- t = build2 (PLUS_EXPR, TREE_TYPE (decl), var, t);
- t = build_gimple_modify_stmt (var, t);
- OMP_FOR_INCR (for_stmt) = t;
- break;
-
- case GIMPLE_MODIFY_STMT:
- gcc_assert (GIMPLE_STMT_OPERAND (t, 0) == decl);
- GIMPLE_STMT_OPERAND (t, 0) = var;
+ ret |= gimplify_expr (&GENERIC_TREE_OPERAND (t, 1),
+ &OMP_FOR_PRE_BODY (for_stmt),
+ NULL, is_gimple_val, fb_rvalue);
- t = GIMPLE_STMT_OPERAND (t, 1);
+ tree_to_gimple_tuple (&TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i));
+ t = TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i);
switch (TREE_CODE (t))
{
- case PLUS_EXPR:
- if (TREE_OPERAND (t, 1) == decl)
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ t = build_int_cst (TREE_TYPE (decl), 1);
+ t = build2 (PLUS_EXPR, TREE_TYPE (decl), var, t);
+ t = build_gimple_modify_stmt (var, t);
+ TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i) = t;
+ break;
+
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ t = build_int_cst (TREE_TYPE (decl), -1);
+ t = build2 (PLUS_EXPR, TREE_TYPE (decl), var, t);
+ t = build_gimple_modify_stmt (var, t);
+ TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i) = t;
+ break;
+
+ case GIMPLE_MODIFY_STMT:
+ gcc_assert (GIMPLE_STMT_OPERAND (t, 0) == decl);
+ GIMPLE_STMT_OPERAND (t, 0) = var;
+
+ t = GIMPLE_STMT_OPERAND (t, 1);
+ switch (TREE_CODE (t))
{
- TREE_OPERAND (t, 1) = TREE_OPERAND (t, 0);
+ case PLUS_EXPR:
+ if (TREE_OPERAND (t, 1) == decl)
+ {
+ TREE_OPERAND (t, 1) = TREE_OPERAND (t, 0);
+ TREE_OPERAND (t, 0) = var;
+ break;
+ }
+
+ /* Fallthru. */
+ case MINUS_EXPR:
+ case POINTER_PLUS_EXPR:
+ gcc_assert (TREE_OPERAND (t, 0) == decl);
TREE_OPERAND (t, 0) = var;
break;
+ default:
+ gcc_unreachable ();
}
- /* Fallthru. */
- case MINUS_EXPR:
- gcc_assert (TREE_OPERAND (t, 0) == decl);
- TREE_OPERAND (t, 0) = var;
+ ret |= gimplify_expr (&TREE_OPERAND (t, 1),
+ &OMP_FOR_PRE_BODY (for_stmt),
+ NULL, is_gimple_val, fb_rvalue);
break;
+
default:
gcc_unreachable ();
}
- ret |= gimplify_expr (&TREE_OPERAND (t, 1), &OMP_FOR_PRE_BODY (for_stmt),
- NULL, is_gimple_val, fb_rvalue);
- break;
+ if (init_decl)
+ append_to_statement_list (init_decl, &bodylist);
- default:
- gcc_unreachable ();
+ if (var != decl || TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)) > 1)
+ {
+ tree c;
+ for (c = OMP_FOR_CLAUSES (for_stmt); c ; c = OMP_CLAUSE_CHAIN (c))
+ if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE
+ && OMP_CLAUSE_DECL (c) == decl
+ && OMP_CLAUSE_LASTPRIVATE_STMT (c) == NULL)
+ {
+ t = TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i);
+ gcc_assert (TREE_CODE (t) == GIMPLE_MODIFY_STMT);
+ gcc_assert (GIMPLE_STMT_OPERAND (t, 0) == var);
+ t = GIMPLE_STMT_OPERAND (t, 1);
+ gcc_assert (TREE_CODE (t) == PLUS_EXPR
+ || TREE_CODE (t) == MINUS_EXPR
+ || TREE_CODE (t) == POINTER_PLUS_EXPR);
+ gcc_assert (TREE_OPERAND (t, 0) == var);
+ t = build2 (TREE_CODE (t), TREE_TYPE (decl), decl,
+ TREE_OPERAND (t, 1));
+ OMP_CLAUSE_LASTPRIVATE_STMT (c)
+ = build_gimple_modify_stmt (decl, t);
+ }
+ }
}
body = OMP_FOR_BODY (for_stmt);
gimplify_to_stmt_list (&body);
- t = alloc_stmt_list ();
- if (init_decl)
- append_to_statement_list (init_decl, &t);
- append_to_statement_list (body, &t);
- OMP_FOR_BODY (for_stmt) = t;
+ append_to_statement_list (body, &bodylist);
+ OMP_FOR_BODY (for_stmt) = bodylist;
gimplify_adjust_omp_clauses (&OMP_FOR_CLAUSES (for_stmt));
return ret == GS_ALL_DONE ? GS_ALL_DONE : GS_ERROR;
{
tree stmt = *expr_p;
- gimplify_scan_omp_clauses (&OMP_CLAUSES (stmt), pre_p, false, false);
+ gimplify_scan_omp_clauses (&OMP_CLAUSES (stmt), pre_p, ORT_WORKSHARE);
gimplify_to_stmt_list (&OMP_BODY (stmt));
gimplify_adjust_omp_clauses (&OMP_CLAUSES (stmt));
/* Also include casts to other type variants. The C front end is fond
of adding these for e.g. volatile variables. This is like
STRIP_TYPE_NOPS but includes the main variant lookup. */
- while ((TREE_CODE (expr) == NOP_EXPR
- || TREE_CODE (expr) == CONVERT_EXPR
+ while ((CONVERT_EXPR_P (expr)
|| TREE_CODE (expr) == NON_LVALUE_EXPR)
&& TREE_OPERAND (expr, 0) != error_mark_node
&& (TYPE_MAIN_VARIANT (TREE_TYPE (expr))
== TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (expr, 0)))))
expr = TREE_OPERAND (expr, 0);
- if (TREE_CODE (expr) == INDIRECT_REF && TREE_OPERAND (expr, 0) == addr)
- return true;
+ if (TREE_CODE (expr) == INDIRECT_REF)
+ {
+ expr = TREE_OPERAND (expr, 0);
+ while (expr != addr
+ && (CONVERT_EXPR_P (expr)
+ || TREE_CODE (expr) == NON_LVALUE_EXPR)
+ && TREE_CODE (expr) == TREE_CODE (addr)
+ && TYPE_MAIN_VARIANT (TREE_TYPE (expr))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (addr)))
+ {
+ expr = TREE_OPERAND (expr, 0);
+ addr = TREE_OPERAND (addr, 0);
+ }
+ if (expr == addr)
+ return true;
+ return (TREE_CODE (addr) == ADDR_EXPR
+ && TREE_CODE (expr) == ADDR_EXPR
+ && TREE_OPERAND (addr, 0) == TREE_OPERAND (expr, 0));
+ }
if (TREE_CODE (addr) == ADDR_EXPR && expr == TREE_OPERAND (addr, 0))
return true;
return false;
break;
case TRUTH_NOT_EXPR:
- TREE_OPERAND (*expr_p, 0)
- = gimple_boolify (TREE_OPERAND (*expr_p, 0));
+ if (TREE_CODE (TREE_TYPE (*expr_p)) != BOOLEAN_TYPE)
+ {
+ tree type = TREE_TYPE (*expr_p);
+ *expr_p = fold_convert (type, gimple_boolify (*expr_p));
+ ret = GS_OK;
+ break;
+ }
+
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_val, fb_rvalue);
recalculate_side_effects (*expr_p);
ret = gimplify_va_arg_expr (expr_p, pre_p, post_p);
break;
- case CONVERT_EXPR:
- case NOP_EXPR:
+ CASE_CONVERT:
if (IS_EMPTY_STMT (*expr_p))
{
ret = GS_ALL_DONE;
NULL, is_gimple_val, fb_rvalue);
break;
+ /* Predictions are always gimplified. */
+ case PREDICT_EXPR:
+ goto out;
+
case LABEL_EXPR:
ret = GS_ALL_DONE;
gcc_assert (decl_function_context (LABEL_EXPR_LABEL (*expr_p))
ret = gimplify_omp_parallel (expr_p, pre_p);
break;
+ case OMP_TASK:
+ ret = gimplify_omp_task (expr_p, pre_p);
+ break;
+
case OMP_FOR:
ret = gimplify_omp_for (expr_p, pre_p);
break;
case OMP_RETURN:
case OMP_CONTINUE:
- case OMP_ATOMIC_LOAD:
- case OMP_ATOMIC_STORE:
-
+ case OMP_ATOMIC_STORE:
+ case OMP_SECTIONS_SWITCH:
ret = GS_ALL_DONE;
break;
+ case OMP_ATOMIC_LOAD:
+ if (gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, NULL,
+ is_gimple_val, fb_rvalue) != GS_ALL_DONE)
+ ret = GS_ERROR;
+ else
+ ret = GS_ALL_DONE;
+ break;
+
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.
unshare_body (body_p, fndecl);
unvisit_body (body_p, fndecl);
- /* Make sure input_location isn't set to something wierd. */
+ /* Make sure input_location isn't set to something weird. */
input_location = DECL_SOURCE_LOCATION (fndecl);
/* Resolve callee-copies. This has to be done before processing
ret = DECL_RESULT (fndecl);
if ((TREE_CODE (TREE_TYPE (ret)) == COMPLEX_TYPE
- || TREE_CODE (TREE_TYPE (ret)) == VECTOR_TYPE)
+ || TREE_CODE (TREE_TYPE (ret)) == VECTOR_TYPE)
&& !needs_to_live_in_memory (ret))
DECL_GIMPLE_REG_P (ret) = 1;
DECL_SAVED_TREE (fndecl) = bind;
}
- cfun->gimplified = true;
current_function_decl = oldfn;
pop_cfun ();
}
push_gimplify_context ();
gimplify_ctxp->into_ssa = gimple_in_ssa_p (cfun);
+ gimplify_ctxp->allow_rhs_cond_expr = true;
if (var)
expr = build_gimple_modify_stmt (var, expr);