diff options
Diffstat (limited to 'gcc/omp-low.cc')
-rw-r--r-- | gcc/omp-low.cc | 14777 |
1 files changed, 14777 insertions, 0 deletions
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc new file mode 100644 index 0000000..f223742 --- /dev/null +++ b/gcc/omp-low.cc @@ -0,0 +1,14777 @@ +/* Lowering pass for OMP directives. Converts OMP directives into explicit + calls to the runtime library (libgomp), data marshalling to implement data + sharing and copying clauses, offloading to accelerators, and more. + + Contributed by Diego Novillo <dnovillo@redhat.com> + + Copyright (C) 2005-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "ssa.h" +#include "cgraph.h" +#include "pretty-print.h" +#include "diagnostic-core.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "internal-fn.h" +#include "gimple-fold.h" +#include "gimplify.h" +#include "gimple-iterator.h" +#include "gimplify-me.h" +#include "gimple-walk.h" +#include "tree-iterator.h" +#include "tree-inline.h" +#include "langhooks.h" +#include "tree-dfa.h" +#include "tree-ssa.h" +#include "splay-tree.h" +#include "omp-general.h" +#include "omp-low.h" +#include "gimple-low.h" +#include "alloc-pool.h" +#include "symbol-summary.h" +#include "tree-nested.h" +#include "context.h" +#include "gomp-constants.h" +#include "gimple-pretty-print.h" +#include "stringpool.h" +#include "attribs.h" +#include "omp-offload.h" + +/* Lowering of OMP parallel and workshare constructs proceeds in two + phases. The first phase scans the function looking for OMP statements + and then for variables that must be replaced to satisfy data sharing + clauses. The second phase expands code for the constructs, as well as + re-gimplifying things when variables have been replaced with complex + expressions. + + Final code generation is done by pass_expand_omp. The flowgraph is + scanned for regions which are then moved to a new + function, to be invoked by the thread library, or offloaded. */ + +/* Context structure. Used to store information about each parallel + directive in the code. */ + +struct omp_context +{ + /* This field must be at the beginning, as we do "inheritance": Some + callback functions for tree-inline.c (e.g., omp_copy_decl) + receive a copy_body_data pointer that is up-casted to an + omp_context pointer. */ + copy_body_data cb; + + /* The tree of contexts corresponding to the encountered constructs. */ + struct omp_context *outer; + gimple *stmt; + + /* Map variables to fields in a structure that allows communication + between sending and receiving threads. */ + splay_tree field_map; + tree record_type; + tree sender_decl; + tree receiver_decl; + + /* These are used just by task contexts, if task firstprivate fn is + needed. srecord_type is used to communicate from the thread + that encountered the task construct to task firstprivate fn, + record_type is allocated by GOMP_task, initialized by task firstprivate + fn and passed to the task body fn. */ + splay_tree sfield_map; + tree srecord_type; + + /* A chain of variables to add to the top-level block surrounding the + construct. In the case of a parallel, this is in the child function. */ + tree block_vars; + + /* Label to which GOMP_cancel{,llation_point} and explicit and implicit + barriers should jump to during omplower pass. */ + tree cancel_label; + + /* The sibling GIMPLE_OMP_FOR simd with _simt_ clause or NULL + otherwise. */ + gimple *simt_stmt; + + /* For task reductions registered in this context, a vector containing + the length of the private copies block (if constant, otherwise NULL) + and then offsets (if constant, otherwise NULL) for each entry. */ + vec<tree> task_reductions; + + /* A hash map from the reduction clauses to the registered array + elts. */ + hash_map<tree, unsigned> *task_reduction_map; + + /* And a hash map from the lastprivate(conditional:) variables to their + corresponding tracking loop iteration variables. */ + hash_map<tree, tree> *lastprivate_conditional_map; + + /* And a hash map from the allocate variables to their corresponding + allocators. */ + hash_map<tree, tree> *allocate_map; + + /* A tree_list of the reduction clauses in this context. This is + only used for checking the consistency of OpenACC reduction + clauses in scan_omp_for and is not guaranteed to contain a valid + value outside of this function. */ + tree local_reduction_clauses; + + /* A tree_list of the reduction clauses in outer contexts. This is + only used for checking the consistency of OpenACC reduction + clauses in scan_omp_for and is not guaranteed to contain a valid + value outside of this function. */ + tree outer_reduction_clauses; + + /* Nesting depth of this context. Used to beautify error messages re + invalid gotos. The outermost ctx is depth 1, with depth 0 being + reserved for the main body of the function. */ + int depth; + + /* True if this parallel directive is nested within another. */ + bool is_nested; + + /* True if this construct can be cancelled. */ + bool cancellable; + + /* True if lower_omp_1 should look up lastprivate conditional in parent + context. */ + bool combined_into_simd_safelen1; + + /* True if there is nested scan context with inclusive clause. */ + bool scan_inclusive; + + /* True if there is nested scan context with exclusive clause. */ + bool scan_exclusive; + + /* True in the second simd loop of for simd with inscan reductions. */ + bool for_simd_scan_phase; + + /* True if there is order(concurrent) clause on the construct. */ + bool order_concurrent; + + /* True if there is bind clause on the construct (i.e. a loop construct). */ + bool loop_p; + + /* Only used for omp target contexts. True if a teams construct is + strictly nested in it. */ + bool teams_nested_p; + + /* Only used for omp target contexts. True if an OpenMP construct other + than teams is strictly nested in it. */ + bool nonteams_nested_p; + + /* Candidates for adjusting OpenACC privatization level. */ + vec<tree> oacc_privatization_candidates; +}; + +static splay_tree all_contexts; +static int taskreg_nesting_level; +static int target_nesting_level; +static bitmap task_shared_vars; +static bitmap global_nonaddressable_vars; +static vec<omp_context *> taskreg_contexts; + +static void scan_omp (gimple_seq *, omp_context *); +static tree scan_omp_1_op (tree *, int *, void *); + +#define WALK_SUBSTMTS \ + case GIMPLE_BIND: \ + case GIMPLE_TRY: \ + case GIMPLE_CATCH: \ + case GIMPLE_EH_FILTER: \ + case GIMPLE_TRANSACTION: \ + /* The sub-statements for these should be walked. */ \ + *handled_ops_p = false; \ + break; + +/* Return whether CTX represents an OpenACC 'parallel' or 'serial' construct. + (This doesn't include OpenACC 'kernels' decomposed parts.) */ + +static bool +is_oacc_parallel_or_serial (omp_context *ctx) +{ + enum gimple_code outer_type = gimple_code (ctx->stmt); + return ((outer_type == GIMPLE_OMP_TARGET) + && ((gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_PARALLEL) + || (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_SERIAL))); +} + +/* Return whether CTX represents an OpenACC 'kernels' construct. + (This doesn't include OpenACC 'kernels' decomposed parts.) */ + +static bool +is_oacc_kernels (omp_context *ctx) +{ + enum gimple_code outer_type = gimple_code (ctx->stmt); + return ((outer_type == GIMPLE_OMP_TARGET) + && (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_KERNELS)); +} + +/* Return whether CTX represents an OpenACC 'kernels' decomposed part. */ + +static bool +is_oacc_kernels_decomposed_part (omp_context *ctx) +{ + enum gimple_code outer_type = gimple_code (ctx->stmt); + return ((outer_type == GIMPLE_OMP_TARGET) + && ((gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_PARALLELIZED) + || (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_GANG_SINGLE) + || (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_OACC_DATA_KERNELS))); +} + +/* Return true if STMT corresponds to an OpenMP target region. */ +static bool +is_omp_target (gimple *stmt) +{ + if (gimple_code (stmt) == GIMPLE_OMP_TARGET) + { + int kind = gimple_omp_target_kind (stmt); + return (kind == GF_OMP_TARGET_KIND_REGION + || kind == GF_OMP_TARGET_KIND_DATA + || kind == GF_OMP_TARGET_KIND_ENTER_DATA + || kind == GF_OMP_TARGET_KIND_EXIT_DATA); + } + return false; +} + +/* If DECL is the artificial dummy VAR_DECL created for non-static + data member privatization, return the underlying "this" parameter, + otherwise return NULL. */ + +tree +omp_member_access_dummy_var (tree decl) +{ + if (!VAR_P (decl) + || !DECL_ARTIFICIAL (decl) + || !DECL_IGNORED_P (decl) + || !DECL_HAS_VALUE_EXPR_P (decl) + || !lang_hooks.decls.omp_disregard_value_expr (decl, false)) + return NULL_TREE; + + tree v = DECL_VALUE_EXPR (decl); + if (TREE_CODE (v) != COMPONENT_REF) + return NULL_TREE; + + while (1) + switch (TREE_CODE (v)) + { + case COMPONENT_REF: + case MEM_REF: + case INDIRECT_REF: + CASE_CONVERT: + case POINTER_PLUS_EXPR: + v = TREE_OPERAND (v, 0); + continue; + case PARM_DECL: + if (DECL_CONTEXT (v) == current_function_decl + && DECL_ARTIFICIAL (v) + && TREE_CODE (TREE_TYPE (v)) == POINTER_TYPE) + return v; + return NULL_TREE; + default: + return NULL_TREE; + } +} + +/* Helper for unshare_and_remap, called through walk_tree. */ + +static tree +unshare_and_remap_1 (tree *tp, int *walk_subtrees, void *data) +{ + tree *pair = (tree *) data; + if (*tp == pair[0]) + { + *tp = unshare_expr (pair[1]); + *walk_subtrees = 0; + } + else if (IS_TYPE_OR_DECL_P (*tp)) + *walk_subtrees = 0; + return NULL_TREE; +} + +/* Return unshare_expr (X) with all occurrences of FROM + replaced with TO. */ + +static tree +unshare_and_remap (tree x, tree from, tree to) +{ + tree pair[2] = { from, to }; + x = unshare_expr (x); + walk_tree (&x, unshare_and_remap_1, pair, NULL); + return x; +} + +/* Convenience function for calling scan_omp_1_op on tree operands. */ + +static inline tree +scan_omp_op (tree *tp, omp_context *ctx) +{ + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.info = ctx; + wi.want_locations = true; + + return walk_tree (tp, scan_omp_1_op, &wi, NULL); +} + +static void lower_omp (gimple_seq *, omp_context *); +static tree lookup_decl_in_outer_ctx (tree, omp_context *); +static tree maybe_lookup_decl_in_outer_ctx (tree, omp_context *); + +/* Return true if CTX is for an omp parallel. */ + +static inline bool +is_parallel_ctx (omp_context *ctx) +{ + return gimple_code (ctx->stmt) == GIMPLE_OMP_PARALLEL; +} + + +/* Return true if CTX is for an omp task. */ + +static inline bool +is_task_ctx (omp_context *ctx) +{ + return gimple_code (ctx->stmt) == GIMPLE_OMP_TASK; +} + + +/* Return true if CTX is for an omp taskloop. */ + +static inline bool +is_taskloop_ctx (omp_context *ctx) +{ + return gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_TASKLOOP; +} + + +/* Return true if CTX is for a host omp teams. */ + +static inline bool +is_host_teams_ctx (omp_context *ctx) +{ + return gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && gimple_omp_teams_host (as_a <gomp_teams *> (ctx->stmt)); +} + +/* Return true if CTX is for an omp parallel or omp task or host omp teams + (the last one is strictly not a task region in OpenMP speak, but we + need to treat it similarly). */ + +static inline bool +is_taskreg_ctx (omp_context *ctx) +{ + return is_parallel_ctx (ctx) || is_task_ctx (ctx) || is_host_teams_ctx (ctx); +} + +/* Return true if EXPR is variable sized. */ + +static inline bool +is_variable_sized (const_tree expr) +{ + return !TREE_CONSTANT (TYPE_SIZE_UNIT (TREE_TYPE (expr))); +} + +/* Lookup variables. The "maybe" form + allows for the variable form to not have been entered, otherwise we + assert that the variable must have been entered. */ + +static inline tree +lookup_decl (tree var, omp_context *ctx) +{ + tree *n = ctx->cb.decl_map->get (var); + return *n; +} + +static inline tree +maybe_lookup_decl (const_tree var, omp_context *ctx) +{ + tree *n = ctx->cb.decl_map->get (const_cast<tree> (var)); + return n ? *n : NULL_TREE; +} + +static inline tree +lookup_field (tree var, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) var); + return (tree) n->value; +} + +static inline tree +lookup_sfield (splay_tree_key key, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->sfield_map + ? ctx->sfield_map : ctx->field_map, key); + return (tree) n->value; +} + +static inline tree +lookup_sfield (tree var, omp_context *ctx) +{ + return lookup_sfield ((splay_tree_key) var, ctx); +} + +static inline tree +maybe_lookup_field (splay_tree_key key, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->field_map, key); + return n ? (tree) n->value : NULL_TREE; +} + +static inline tree +maybe_lookup_field (tree var, omp_context *ctx) +{ + return maybe_lookup_field ((splay_tree_key) var, ctx); +} + +/* Return true if DECL should be copied by pointer. SHARED_CTX is + the parallel context if DECL is to be shared. */ + +static bool +use_pointer_for_field (tree decl, omp_context *shared_ctx) +{ + if (AGGREGATE_TYPE_P (TREE_TYPE (decl)) + || TYPE_ATOMIC (TREE_TYPE (decl))) + return true; + + /* We can only use copy-in/copy-out semantics for shared variables + when we know the value is not accessible from an outer scope. */ + if (shared_ctx) + { + gcc_assert (!is_gimple_omp_oacc (shared_ctx->stmt)); + + /* ??? Trivially accessible from anywhere. But why would we even + be passing an address in this case? Should we simply assert + this to be false, or should we have a cleanup pass that removes + these from the list of mappings? */ + if (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, shared_ctx))) + return true; + + /* For variables with DECL_HAS_VALUE_EXPR_P set, we cannot tell + without analyzing the expression whether or not its location + is accessible to anyone else. In the case of nested parallel + regions it certainly may be. */ + if (TREE_CODE (decl) != RESULT_DECL && DECL_HAS_VALUE_EXPR_P (decl)) + return true; + + /* Do not use copy-in/copy-out for variables that have their + address taken. */ + if (is_global_var (decl)) + { + /* For file scope vars, track whether we've seen them as + non-addressable initially and in that case, keep the same + answer for the duration of the pass, even when they are made + addressable later on e.g. through reduction expansion. Global + variables which weren't addressable before the pass will not + have their privatized copies address taken. See PR91216. */ + if (!TREE_ADDRESSABLE (decl)) + { + if (!global_nonaddressable_vars) + global_nonaddressable_vars = BITMAP_ALLOC (NULL); + bitmap_set_bit (global_nonaddressable_vars, DECL_UID (decl)); + } + else if (!global_nonaddressable_vars + || !bitmap_bit_p (global_nonaddressable_vars, + DECL_UID (decl))) + return true; + } + else if (TREE_ADDRESSABLE (decl)) + return true; + + /* lower_send_shared_vars only uses copy-in, but not copy-out + for these. */ + if (TREE_READONLY (decl) + || ((TREE_CODE (decl) == RESULT_DECL + || TREE_CODE (decl) == PARM_DECL) + && DECL_BY_REFERENCE (decl))) + return false; + + /* Disallow copy-in/out in nested parallel if + decl is shared in outer parallel, otherwise + each thread could store the shared variable + in its own copy-in location, making the + variable no longer really shared. */ + if (shared_ctx->is_nested) + { + omp_context *up; + + for (up = shared_ctx->outer; up; up = up->outer) + if ((is_taskreg_ctx (up) + || (gimple_code (up->stmt) == GIMPLE_OMP_TARGET + && is_gimple_omp_offloaded (up->stmt))) + && maybe_lookup_decl (decl, up)) + break; + + if (up) + { + tree c; + + if (gimple_code (up->stmt) == GIMPLE_OMP_TARGET) + { + for (c = gimple_omp_target_clauses (up->stmt); + c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_DECL (c) == decl) + break; + } + else + for (c = gimple_omp_taskreg_clauses (up->stmt); + c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED + && OMP_CLAUSE_DECL (c) == decl) + break; + + if (c) + goto maybe_mark_addressable_and_ret; + } + } + + /* For tasks avoid using copy-in/out. As tasks can be + deferred or executed in different thread, when GOMP_task + returns, the task hasn't necessarily terminated. */ + if (is_task_ctx (shared_ctx)) + { + tree outer; + maybe_mark_addressable_and_ret: + outer = maybe_lookup_decl_in_outer_ctx (decl, shared_ctx); + if (is_gimple_reg (outer) && !omp_member_access_dummy_var (outer)) + { + /* Taking address of OUTER in lower_send_shared_vars + might need regimplification of everything that uses the + variable. */ + if (!task_shared_vars) + task_shared_vars = BITMAP_ALLOC (NULL); + bitmap_set_bit (task_shared_vars, DECL_UID (outer)); + TREE_ADDRESSABLE (outer) = 1; + } + return true; + } + } + + return false; +} + +/* Construct a new automatic decl similar to VAR. */ + +static tree +omp_copy_decl_2 (tree var, tree name, tree type, omp_context *ctx) +{ + tree copy = copy_var_decl (var, name, type); + + DECL_CONTEXT (copy) = current_function_decl; + + if (ctx) + { + DECL_CHAIN (copy) = ctx->block_vars; + ctx->block_vars = copy; + } + else + record_vars (copy); + + /* If VAR is listed in task_shared_vars, it means it wasn't + originally addressable and is just because task needs to take + it's address. But we don't need to take address of privatizations + from that var. */ + if (TREE_ADDRESSABLE (var) + && ((task_shared_vars + && bitmap_bit_p (task_shared_vars, DECL_UID (var))) + || (global_nonaddressable_vars + && bitmap_bit_p (global_nonaddressable_vars, DECL_UID (var))))) + TREE_ADDRESSABLE (copy) = 0; + + return copy; +} + +static tree +omp_copy_decl_1 (tree var, omp_context *ctx) +{ + return omp_copy_decl_2 (var, DECL_NAME (var), TREE_TYPE (var), ctx); +} + +/* Build COMPONENT_REF and set TREE_THIS_VOLATILE and TREE_READONLY on it + as appropriate. */ +/* See also 'gcc/omp-oacc-neuter-broadcast.cc:oacc_build_component_ref'. */ + +static tree +omp_build_component_ref (tree obj, tree field) +{ + tree ret = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL); + if (TREE_THIS_VOLATILE (field)) + TREE_THIS_VOLATILE (ret) |= 1; + if (TREE_READONLY (field)) + TREE_READONLY (ret) |= 1; + return ret; +} + +/* Build tree nodes to access the field for VAR on the receiver side. */ + +static tree +build_receiver_ref (tree var, bool by_ref, omp_context *ctx) +{ + tree x, field = lookup_field (var, ctx); + + /* If the receiver record type was remapped in the child function, + remap the field into the new record type. */ + x = maybe_lookup_field (field, ctx); + if (x != NULL) + field = x; + + x = build_simple_mem_ref (ctx->receiver_decl); + TREE_THIS_NOTRAP (x) = 1; + x = omp_build_component_ref (x, field); + if (by_ref) + { + x = build_simple_mem_ref (x); + TREE_THIS_NOTRAP (x) = 1; + } + + return x; +} + +/* Build tree nodes to access VAR in the scope outer to CTX. In the case + of a parallel, this is a component reference; for workshare constructs + this is some variable. */ + +static tree +build_outer_var_ref (tree var, omp_context *ctx, + enum omp_clause_code code = OMP_CLAUSE_ERROR) +{ + tree x; + omp_context *outer = ctx->outer; + for (; outer; outer = outer->outer) + { + if (gimple_code (outer->stmt) == GIMPLE_OMP_TASKGROUP) + continue; + if (gimple_code (outer->stmt) == GIMPLE_OMP_SCOPE + && !maybe_lookup_decl (var, outer)) + continue; + break; + } + + if (is_global_var (maybe_lookup_decl_in_outer_ctx (var, ctx))) + x = var; + else if (is_variable_sized (var)) + { + x = TREE_OPERAND (DECL_VALUE_EXPR (var), 0); + x = build_outer_var_ref (x, ctx, code); + x = build_simple_mem_ref (x); + } + else if (is_taskreg_ctx (ctx)) + { + bool by_ref = use_pointer_for_field (var, NULL); + x = build_receiver_ref (var, by_ref, ctx); + } + else if ((gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD) + || ctx->loop_p + || (code == OMP_CLAUSE_PRIVATE + && (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + || gimple_code (ctx->stmt) == GIMPLE_OMP_SECTIONS + || gimple_code (ctx->stmt) == GIMPLE_OMP_SINGLE))) + { + /* #pragma omp simd isn't a worksharing construct, and can reference + even private vars in its linear etc. clauses. + Similarly for OMP_CLAUSE_PRIVATE with outer ref, that can refer + to private vars in all worksharing constructs. */ + x = NULL_TREE; + if (outer && is_taskreg_ctx (outer)) + x = lookup_decl (var, outer); + else if (outer) + x = maybe_lookup_decl_in_outer_ctx (var, ctx); + if (x == NULL_TREE) + x = var; + } + else if (code == OMP_CLAUSE_LASTPRIVATE && is_taskloop_ctx (ctx)) + { + gcc_assert (outer); + splay_tree_node n + = splay_tree_lookup (outer->field_map, + (splay_tree_key) &DECL_UID (var)); + if (n == NULL) + { + if (is_global_var (maybe_lookup_decl_in_outer_ctx (var, outer))) + x = var; + else + x = lookup_decl (var, outer); + } + else + { + tree field = (tree) n->value; + /* If the receiver record type was remapped in the child function, + remap the field into the new record type. */ + x = maybe_lookup_field (field, outer); + if (x != NULL) + field = x; + + x = build_simple_mem_ref (outer->receiver_decl); + x = omp_build_component_ref (x, field); + if (use_pointer_for_field (var, outer)) + x = build_simple_mem_ref (x); + } + } + else if (outer) + x = lookup_decl (var, outer); + else if (omp_privatize_by_reference (var)) + /* This can happen with orphaned constructs. If var is reference, it is + possible it is shared and as such valid. */ + x = var; + else if (omp_member_access_dummy_var (var)) + x = var; + else + gcc_unreachable (); + + if (x == var) + { + tree t = omp_member_access_dummy_var (var); + if (t) + { + x = DECL_VALUE_EXPR (var); + tree o = maybe_lookup_decl_in_outer_ctx (t, ctx); + if (o != t) + x = unshare_and_remap (x, t, o); + else + x = unshare_expr (x); + } + } + + if (omp_privatize_by_reference (var)) + x = build_simple_mem_ref (x); + + return x; +} + +/* Build tree nodes to access the field for VAR on the sender side. */ + +static tree +build_sender_ref (splay_tree_key key, omp_context *ctx) +{ + tree field = lookup_sfield (key, ctx); + return omp_build_component_ref (ctx->sender_decl, field); +} + +static tree +build_sender_ref (tree var, omp_context *ctx) +{ + return build_sender_ref ((splay_tree_key) var, ctx); +} + +/* Add a new field for VAR inside the structure CTX->SENDER_DECL. If + BASE_POINTERS_RESTRICT, declare the field with restrict. */ + +static void +install_var_field (tree var, bool by_ref, int mask, omp_context *ctx) +{ + tree field, type, sfield = NULL_TREE; + splay_tree_key key = (splay_tree_key) var; + + if ((mask & 16) != 0) + { + key = (splay_tree_key) &DECL_NAME (var); + gcc_checking_assert (key != (splay_tree_key) var); + } + if ((mask & 8) != 0) + { + key = (splay_tree_key) &DECL_UID (var); + gcc_checking_assert (key != (splay_tree_key) var); + } + gcc_assert ((mask & 1) == 0 + || !splay_tree_lookup (ctx->field_map, key)); + gcc_assert ((mask & 2) == 0 || !ctx->sfield_map + || !splay_tree_lookup (ctx->sfield_map, key)); + gcc_assert ((mask & 3) == 3 + || !is_gimple_omp_oacc (ctx->stmt)); + + type = TREE_TYPE (var); + if ((mask & 16) != 0) + type = lang_hooks.decls.omp_array_data (var, true); + + /* Prevent redeclaring the var in the split-off function with a restrict + pointer type. Note that we only clear type itself, restrict qualifiers in + the pointed-to type will be ignored by points-to analysis. */ + if (POINTER_TYPE_P (type) + && TYPE_RESTRICT (type)) + type = build_qualified_type (type, TYPE_QUALS (type) & ~TYPE_QUAL_RESTRICT); + + if (mask & 4) + { + gcc_assert (TREE_CODE (type) == ARRAY_TYPE); + type = build_pointer_type (build_pointer_type (type)); + } + else if (by_ref) + type = build_pointer_type (type); + else if ((mask & (32 | 3)) == 1 + && omp_privatize_by_reference (var)) + type = TREE_TYPE (type); + + field = build_decl (DECL_SOURCE_LOCATION (var), + FIELD_DECL, DECL_NAME (var), type); + + /* Remember what variable this field was created for. This does have a + side effect of making dwarf2out ignore this member, so for helpful + debugging we clear it later in delete_omp_context. */ + DECL_ABSTRACT_ORIGIN (field) = var; + if ((mask & 16) == 0 && type == TREE_TYPE (var)) + { + SET_DECL_ALIGN (field, DECL_ALIGN (var)); + DECL_USER_ALIGN (field) = DECL_USER_ALIGN (var); + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (var); + } + else + SET_DECL_ALIGN (field, TYPE_ALIGN (type)); + + if ((mask & 3) == 3) + { + insert_field_into_struct (ctx->record_type, field); + if (ctx->srecord_type) + { + sfield = build_decl (DECL_SOURCE_LOCATION (var), + FIELD_DECL, DECL_NAME (var), type); + DECL_ABSTRACT_ORIGIN (sfield) = var; + SET_DECL_ALIGN (sfield, DECL_ALIGN (field)); + DECL_USER_ALIGN (sfield) = DECL_USER_ALIGN (field); + TREE_THIS_VOLATILE (sfield) = TREE_THIS_VOLATILE (field); + insert_field_into_struct (ctx->srecord_type, sfield); + } + } + else + { + if (ctx->srecord_type == NULL_TREE) + { + tree t; + + ctx->srecord_type = lang_hooks.types.make_type (RECORD_TYPE); + ctx->sfield_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + for (t = TYPE_FIELDS (ctx->record_type); t ; t = TREE_CHAIN (t)) + { + sfield = build_decl (DECL_SOURCE_LOCATION (t), + FIELD_DECL, DECL_NAME (t), TREE_TYPE (t)); + DECL_ABSTRACT_ORIGIN (sfield) = DECL_ABSTRACT_ORIGIN (t); + insert_field_into_struct (ctx->srecord_type, sfield); + splay_tree_insert (ctx->sfield_map, + (splay_tree_key) DECL_ABSTRACT_ORIGIN (t), + (splay_tree_value) sfield); + } + } + sfield = field; + insert_field_into_struct ((mask & 1) ? ctx->record_type + : ctx->srecord_type, field); + } + + if (mask & 1) + splay_tree_insert (ctx->field_map, key, (splay_tree_value) field); + if ((mask & 2) && ctx->sfield_map) + splay_tree_insert (ctx->sfield_map, key, (splay_tree_value) sfield); +} + +static tree +install_var_local (tree var, omp_context *ctx) +{ + tree new_var = omp_copy_decl_1 (var, ctx); + insert_decl_map (&ctx->cb, var, new_var); + return new_var; +} + +/* Adjust the replacement for DECL in CTX for the new context. This means + copying the DECL_VALUE_EXPR, and fixing up the type. */ + +static void +fixup_remapped_decl (tree decl, omp_context *ctx, bool private_debug) +{ + tree new_decl, size; + + new_decl = lookup_decl (decl, ctx); + + TREE_TYPE (new_decl) = remap_type (TREE_TYPE (decl), &ctx->cb); + + if ((!TREE_CONSTANT (DECL_SIZE (new_decl)) || private_debug) + && DECL_HAS_VALUE_EXPR_P (decl)) + { + tree ve = DECL_VALUE_EXPR (decl); + walk_tree (&ve, copy_tree_body_r, &ctx->cb, NULL); + SET_DECL_VALUE_EXPR (new_decl, ve); + DECL_HAS_VALUE_EXPR_P (new_decl) = 1; + } + + if (!TREE_CONSTANT (DECL_SIZE (new_decl))) + { + size = remap_decl (DECL_SIZE (decl), &ctx->cb); + if (size == error_mark_node) + size = TYPE_SIZE (TREE_TYPE (new_decl)); + DECL_SIZE (new_decl) = size; + + size = remap_decl (DECL_SIZE_UNIT (decl), &ctx->cb); + if (size == error_mark_node) + size = TYPE_SIZE_UNIT (TREE_TYPE (new_decl)); + DECL_SIZE_UNIT (new_decl) = size; + } +} + +/* The callback for remap_decl. Search all containing contexts for a + mapping of the variable; this avoids having to duplicate the splay + tree ahead of time. We know a mapping doesn't already exist in the + given context. Create new mappings to implement default semantics. */ + +static tree +omp_copy_decl (tree var, copy_body_data *cb) +{ + omp_context *ctx = (omp_context *) cb; + tree new_var; + + if (TREE_CODE (var) == LABEL_DECL) + { + if (FORCED_LABEL (var) || DECL_NONLOCAL (var)) + return var; + new_var = create_artificial_label (DECL_SOURCE_LOCATION (var)); + DECL_CONTEXT (new_var) = current_function_decl; + insert_decl_map (&ctx->cb, var, new_var); + return new_var; + } + + while (!is_taskreg_ctx (ctx)) + { + ctx = ctx->outer; + if (ctx == NULL) + return var; + new_var = maybe_lookup_decl (var, ctx); + if (new_var) + return new_var; + } + + if (is_global_var (var) || decl_function_context (var) != ctx->cb.src_fn) + return var; + + return error_mark_node; +} + +/* Create a new context, with OUTER_CTX being the surrounding context. */ + +static omp_context * +new_omp_context (gimple *stmt, omp_context *outer_ctx) +{ + omp_context *ctx = XCNEW (omp_context); + + splay_tree_insert (all_contexts, (splay_tree_key) stmt, + (splay_tree_value) ctx); + ctx->stmt = stmt; + + if (outer_ctx) + { + ctx->outer = outer_ctx; + ctx->cb = outer_ctx->cb; + ctx->cb.block = NULL; + ctx->depth = outer_ctx->depth + 1; + } + else + { + ctx->cb.src_fn = current_function_decl; + ctx->cb.dst_fn = current_function_decl; + ctx->cb.src_node = cgraph_node::get (current_function_decl); + gcc_checking_assert (ctx->cb.src_node); + ctx->cb.dst_node = ctx->cb.src_node; + ctx->cb.src_cfun = cfun; + ctx->cb.copy_decl = omp_copy_decl; + ctx->cb.eh_lp_nr = 0; + ctx->cb.transform_call_graph_edges = CB_CGE_MOVE; + ctx->cb.adjust_array_error_bounds = true; + ctx->cb.dont_remap_vla_if_no_change = true; + ctx->depth = 1; + } + + ctx->cb.decl_map = new hash_map<tree, tree>; + + return ctx; +} + +static gimple_seq maybe_catch_exception (gimple_seq); + +/* Finalize task copyfn. */ + +static void +finalize_task_copyfn (gomp_task *task_stmt) +{ + struct function *child_cfun; + tree child_fn; + gimple_seq seq = NULL, new_seq; + gbind *bind; + + child_fn = gimple_omp_task_copy_fn (task_stmt); + if (child_fn == NULL_TREE) + return; + + child_cfun = DECL_STRUCT_FUNCTION (child_fn); + DECL_STRUCT_FUNCTION (child_fn)->curr_properties = cfun->curr_properties; + + push_cfun (child_cfun); + bind = gimplify_body (child_fn, false); + gimple_seq_add_stmt (&seq, bind); + new_seq = maybe_catch_exception (seq); + if (new_seq != seq) + { + bind = gimple_build_bind (NULL, new_seq, NULL); + seq = NULL; + gimple_seq_add_stmt (&seq, bind); + } + gimple_set_body (child_fn, seq); + pop_cfun (); + + /* Inform the callgraph about the new function. */ + cgraph_node *node = cgraph_node::get_create (child_fn); + node->parallelized_function = 1; + cgraph_node::add_new_function (child_fn, false); +} + +/* Destroy a omp_context data structures. Called through the splay tree + value delete callback. */ + +static void +delete_omp_context (splay_tree_value value) +{ + omp_context *ctx = (omp_context *) value; + + delete ctx->cb.decl_map; + + if (ctx->field_map) + splay_tree_delete (ctx->field_map); + if (ctx->sfield_map) + splay_tree_delete (ctx->sfield_map); + + /* We hijacked DECL_ABSTRACT_ORIGIN earlier. We need to clear it before + it produces corrupt debug information. */ + if (ctx->record_type) + { + tree t; + for (t = TYPE_FIELDS (ctx->record_type); t ; t = DECL_CHAIN (t)) + DECL_ABSTRACT_ORIGIN (t) = NULL; + } + if (ctx->srecord_type) + { + tree t; + for (t = TYPE_FIELDS (ctx->srecord_type); t ; t = DECL_CHAIN (t)) + DECL_ABSTRACT_ORIGIN (t) = NULL; + } + + if (is_task_ctx (ctx)) + finalize_task_copyfn (as_a <gomp_task *> (ctx->stmt)); + + if (ctx->task_reduction_map) + { + ctx->task_reductions.release (); + delete ctx->task_reduction_map; + } + + delete ctx->lastprivate_conditional_map; + delete ctx->allocate_map; + + XDELETE (ctx); +} + +/* Fix up RECEIVER_DECL with a type that has been remapped to the child + context. */ + +static void +fixup_child_record_type (omp_context *ctx) +{ + tree f, type = ctx->record_type; + + if (!ctx->receiver_decl) + return; + /* ??? It isn't sufficient to just call remap_type here, because + variably_modified_type_p doesn't work the way we expect for + record types. Testing each field for whether it needs remapping + and creating a new record by hand works, however. */ + for (f = TYPE_FIELDS (type); f ; f = DECL_CHAIN (f)) + if (variably_modified_type_p (TREE_TYPE (f), ctx->cb.src_fn)) + break; + if (f) + { + tree name, new_fields = NULL; + + type = lang_hooks.types.make_type (RECORD_TYPE); + name = DECL_NAME (TYPE_NAME (ctx->record_type)); + name = build_decl (DECL_SOURCE_LOCATION (ctx->receiver_decl), + TYPE_DECL, name, type); + TYPE_NAME (type) = name; + + for (f = TYPE_FIELDS (ctx->record_type); f ; f = DECL_CHAIN (f)) + { + tree new_f = copy_node (f); + DECL_CONTEXT (new_f) = type; + TREE_TYPE (new_f) = remap_type (TREE_TYPE (f), &ctx->cb); + DECL_CHAIN (new_f) = new_fields; + walk_tree (&DECL_SIZE (new_f), copy_tree_body_r, &ctx->cb, NULL); + walk_tree (&DECL_SIZE_UNIT (new_f), copy_tree_body_r, + &ctx->cb, NULL); + walk_tree (&DECL_FIELD_OFFSET (new_f), copy_tree_body_r, + &ctx->cb, NULL); + new_fields = new_f; + + /* Arrange to be able to look up the receiver field + given the sender field. */ + splay_tree_insert (ctx->field_map, (splay_tree_key) f, + (splay_tree_value) new_f); + } + TYPE_FIELDS (type) = nreverse (new_fields); + layout_type (type); + } + + /* In a target region we never modify any of the pointers in *.omp_data_i, + so attempt to help the optimizers. */ + if (is_gimple_omp_offloaded (ctx->stmt)) + type = build_qualified_type (type, TYPE_QUAL_CONST); + + TREE_TYPE (ctx->receiver_decl) + = build_qualified_type (build_reference_type (type), TYPE_QUAL_RESTRICT); +} + +/* Instantiate decls as necessary in CTX to satisfy the data sharing + specified by CLAUSES. */ + +static void +scan_sharing_clauses (tree clauses, omp_context *ctx) +{ + tree c, decl; + bool scan_array_reductions = false; + + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ALLOCATE + && (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) == NULL_TREE + /* omp_default_mem_alloc is 1 */ + || !integer_onep (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c)) + || OMP_CLAUSE_ALLOCATE_ALIGN (c) != NULL_TREE)) + { + if (ctx->allocate_map == NULL) + ctx->allocate_map = new hash_map<tree, tree>; + tree val = integer_zero_node; + if (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c)) + val = OMP_CLAUSE_ALLOCATE_ALLOCATOR (c); + if (OMP_CLAUSE_ALLOCATE_ALIGN (c)) + val = build_tree_list (val, OMP_CLAUSE_ALLOCATE_ALIGN (c)); + ctx->allocate_map->put (OMP_CLAUSE_DECL (c), val); + } + + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + bool by_ref; + + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + decl = OMP_CLAUSE_DECL (c); + if (OMP_CLAUSE_PRIVATE_OUTER_REF (c)) + goto do_private; + else if (!is_variable_sized (decl)) + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_SHARED: + decl = OMP_CLAUSE_DECL (c); + if (ctx->allocate_map && ctx->allocate_map->get (decl)) + ctx->allocate_map->remove (decl); + /* Ignore shared directives in teams construct inside of + target construct. */ + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && !is_host_teams_ctx (ctx)) + { + /* Global variables don't need to be copied, + the receiver side will use them directly. */ + tree odecl = maybe_lookup_decl_in_outer_ctx (decl, ctx); + if (is_global_var (odecl)) + break; + insert_decl_map (&ctx->cb, decl, odecl); + break; + } + gcc_assert (is_taskreg_ctx (ctx)); + gcc_assert (!COMPLETE_TYPE_P (TREE_TYPE (decl)) + || !is_variable_sized (decl)); + /* Global variables don't need to be copied, + the receiver side will use them directly. */ + if (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))) + break; + if (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + { + use_pointer_for_field (decl, ctx); + break; + } + by_ref = use_pointer_for_field (decl, NULL); + if ((! TREE_READONLY (decl) && !OMP_CLAUSE_SHARED_READONLY (c)) + || TREE_ADDRESSABLE (decl) + || by_ref + || omp_privatize_by_reference (decl)) + { + by_ref = use_pointer_for_field (decl, ctx); + install_var_field (decl, by_ref, 3, ctx); + install_var_local (decl, ctx); + break; + } + /* We don't need to copy const scalar vars back. */ + OMP_CLAUSE_SET_CODE (c, OMP_CLAUSE_FIRSTPRIVATE); + goto do_private; + + case OMP_CLAUSE_REDUCTION: + /* Collect 'reduction' clauses on OpenACC compute construct. */ + if (is_gimple_omp_oacc (ctx->stmt) + && is_gimple_omp_offloaded (ctx->stmt)) + { + /* No 'reduction' clauses on OpenACC 'kernels'. */ + gcc_checking_assert (!is_oacc_kernels (ctx)); + /* Likewise, on OpenACC 'kernels' decomposed parts. */ + gcc_checking_assert (!is_oacc_kernels_decomposed_part (ctx)); + + ctx->local_reduction_clauses + = tree_cons (NULL, c, ctx->local_reduction_clauses); + } + /* FALLTHRU */ + + case OMP_CLAUSE_IN_REDUCTION: + decl = OMP_CLAUSE_DECL (c); + if (ctx->allocate_map + && ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && (OMP_CLAUSE_REDUCTION_INSCAN (c) + || OMP_CLAUSE_REDUCTION_TASK (c))) + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION + || is_task_ctx (ctx))) + { + /* For now. */ + if (ctx->allocate_map->get (decl)) + ctx->allocate_map->remove (decl); + } + if (TREE_CODE (decl) == MEM_REF) + { + tree t = TREE_OPERAND (decl, 0); + if (TREE_CODE (t) == POINTER_PLUS_EXPR) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) == INDIRECT_REF + || TREE_CODE (t) == ADDR_EXPR) + t = TREE_OPERAND (t, 0); + if (is_omp_target (ctx->stmt)) + { + if (is_variable_sized (t)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (t)); + t = DECL_VALUE_EXPR (t); + gcc_assert (TREE_CODE (t) == INDIRECT_REF); + t = TREE_OPERAND (t, 0); + gcc_assert (DECL_P (t)); + } + tree at = t; + if (ctx->outer) + scan_omp_op (&at, ctx->outer); + tree nt = omp_copy_decl_1 (at, ctx->outer); + splay_tree_insert (ctx->field_map, + (splay_tree_key) &DECL_CONTEXT (t), + (splay_tree_value) nt); + if (at != t) + splay_tree_insert (ctx->field_map, + (splay_tree_key) &DECL_CONTEXT (at), + (splay_tree_value) nt); + break; + } + install_var_local (t, ctx); + if (is_taskreg_ctx (ctx) + && (!is_global_var (maybe_lookup_decl_in_outer_ctx (t, ctx)) + || (is_task_ctx (ctx) + && (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + || (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE + && (TREE_CODE (TREE_TYPE (TREE_TYPE (t))) + == POINTER_TYPE))))) + && !is_variable_sized (t) + && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION + || (!OMP_CLAUSE_REDUCTION_TASK (c) + && !is_task_ctx (ctx)))) + { + by_ref = use_pointer_for_field (t, NULL); + if (is_task_ctx (ctx) + && TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE + && TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == POINTER_TYPE) + { + install_var_field (t, false, 1, ctx); + install_var_field (t, by_ref, 2, ctx); + } + else + install_var_field (t, by_ref, 3, ctx); + } + break; + } + if (is_omp_target (ctx->stmt)) + { + tree at = decl; + if (ctx->outer) + scan_omp_op (&at, ctx->outer); + tree nt = omp_copy_decl_1 (at, ctx->outer); + splay_tree_insert (ctx->field_map, + (splay_tree_key) &DECL_CONTEXT (decl), + (splay_tree_value) nt); + if (at != decl) + splay_tree_insert (ctx->field_map, + (splay_tree_key) &DECL_CONTEXT (at), + (splay_tree_value) nt); + break; + } + if (is_task_ctx (ctx) + || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_TASK (c) + && is_parallel_ctx (ctx))) + { + /* Global variables don't need to be copied, + the receiver side will use them directly. */ + if (!is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))) + { + by_ref = use_pointer_for_field (decl, ctx); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) + install_var_field (decl, by_ref, 3, ctx); + } + install_var_local (decl, ctx); + break; + } + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_TASK (c)) + { + install_var_local (decl, ctx); + break; + } + goto do_private; + + case OMP_CLAUSE_LASTPRIVATE: + /* Let the corresponding firstprivate clause create + the variable. */ + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LINEAR: + decl = OMP_CLAUSE_DECL (c); + do_private: + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR) + && is_gimple_omp_offloaded (ctx->stmt)) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + by_ref = !omp_privatize_by_reference (decl); + install_var_field (decl, by_ref, 3, ctx); + } + else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + install_var_field (decl, true, 3, ctx); + else + install_var_field (decl, false, 3, ctx); + } + if (is_variable_sized (decl)) + { + if (is_task_ctx (ctx)) + { + if (ctx->allocate_map + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + /* For now. */ + if (ctx->allocate_map->get (decl)) + ctx->allocate_map->remove (decl); + } + install_var_field (decl, false, 1, ctx); + } + break; + } + else if (is_taskreg_ctx (ctx)) + { + bool global + = is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx)); + by_ref = use_pointer_for_field (decl, NULL); + + if (is_task_ctx (ctx) + && (global || by_ref || omp_privatize_by_reference (decl))) + { + if (ctx->allocate_map + && ctx->allocate_map->get (decl)) + install_var_field (decl, by_ref, 32 | 1, ctx); + else + install_var_field (decl, false, 1, ctx); + if (!global) + install_var_field (decl, by_ref, 2, ctx); + } + else if (!global) + install_var_field (decl, by_ref, 3, ctx); + } + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_USE_DEVICE_PTR: + case OMP_CLAUSE_USE_DEVICE_ADDR: + decl = OMP_CLAUSE_DECL (c); + + /* Fortran array descriptors. */ + if (lang_hooks.decls.omp_array_data (decl, true)) + install_var_field (decl, false, 19, ctx); + else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR + && !omp_privatize_by_reference (decl) + && !omp_is_allocatable_or_ptr (decl)) + || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + install_var_field (decl, true, 11, ctx); + else + install_var_field (decl, false, 11, ctx); + if (DECL_SIZE (decl) + && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + { + tree decl2 = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (decl2) == INDIRECT_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + install_var_local (decl2, ctx); + } + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_IS_DEVICE_PTR: + decl = OMP_CLAUSE_DECL (c); + goto do_private; + + case OMP_CLAUSE__LOOPTEMP_: + case OMP_CLAUSE__REDUCTEMP_: + gcc_assert (is_taskreg_ctx (ctx)); + decl = OMP_CLAUSE_DECL (c); + install_var_field (decl, false, 3, ctx); + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_COPYIN: + decl = OMP_CLAUSE_DECL (c); + by_ref = use_pointer_for_field (decl, NULL); + install_var_field (decl, by_ref, 3, ctx); + break; + + case OMP_CLAUSE_FINAL: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_NUM_TEAMS: + case OMP_CLAUSE_THREAD_LIMIT: + case OMP_CLAUSE_DEVICE: + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_DIST_SCHEDULE: + case OMP_CLAUSE_DEPEND: + case OMP_CLAUSE_PRIORITY: + case OMP_CLAUSE_GRAINSIZE: + case OMP_CLAUSE_NUM_TASKS: + case OMP_CLAUSE_NUM_GANGS: + case OMP_CLAUSE_NUM_WORKERS: + case OMP_CLAUSE_VECTOR_LENGTH: + case OMP_CLAUSE_DETACH: + case OMP_CLAUSE_FILTER: + if (ctx->outer) + scan_omp_op (&OMP_CLAUSE_OPERAND (c, 0), ctx->outer); + break; + + case OMP_CLAUSE_TO: + case OMP_CLAUSE_FROM: + case OMP_CLAUSE_MAP: + if (ctx->outer) + scan_omp_op (&OMP_CLAUSE_SIZE (c), ctx->outer); + decl = OMP_CLAUSE_DECL (c); + /* Global variables with "omp declare target" attribute + don't need to be copied, the receiver side will use them + directly. However, global variables with "omp declare target link" + attribute need to be copied. Or when ALWAYS modifier is used. */ + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && DECL_P (decl) + && ((OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER + && (OMP_CLAUSE_MAP_KIND (c) + != GOMP_MAP_FIRSTPRIVATE_REFERENCE) + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH) + || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_TO + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_FROM + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_TOFROM + && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_TO_PSET + && is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx)) + && varpool_node::get_create (decl)->offloadable + && !lookup_attribute ("omp declare target link", + DECL_ATTRIBUTES (decl))) + break; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER) + { + /* Ignore GOMP_MAP_POINTER kind for arrays in regions that are + not offloaded; there is nothing to map for those. */ + if (!is_gimple_omp_offloaded (ctx->stmt) + && !POINTER_TYPE_P (TREE_TYPE (decl)) + && !OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c)) + break; + } + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && DECL_P (decl) + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH) + && is_omp_target (ctx->stmt)) + { + /* If this is an offloaded region, an attach operation should + only exist when the pointer variable is mapped in a prior + clause. */ + if (is_gimple_omp_offloaded (ctx->stmt)) + gcc_assert + (maybe_lookup_decl (decl, ctx) + || (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx)) + && lookup_attribute ("omp declare target", + DECL_ATTRIBUTES (decl)))); + + /* By itself, attach/detach is generated as part of pointer + variable mapping and should not create new variables in the + offloaded region, however sender refs for it must be created + for its address to be passed to the runtime. */ + tree field + = build_decl (OMP_CLAUSE_LOCATION (c), + FIELD_DECL, NULL_TREE, ptr_type_node); + SET_DECL_ALIGN (field, TYPE_ALIGN (ptr_type_node)); + insert_field_into_struct (ctx->record_type, field); + /* To not clash with a map of the pointer variable itself, + attach/detach maps have their field looked up by the *clause* + tree expression, not the decl. */ + gcc_assert (!splay_tree_lookup (ctx->field_map, + (splay_tree_key) c)); + splay_tree_insert (ctx->field_map, (splay_tree_key) c, + (splay_tree_value) field); + break; + } + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER + || (OMP_CLAUSE_MAP_KIND (c) + == GOMP_MAP_FIRSTPRIVATE_REFERENCE))) + { + if (TREE_CODE (decl) == COMPONENT_REF + || (TREE_CODE (decl) == INDIRECT_REF + && TREE_CODE (TREE_OPERAND (decl, 0)) == COMPONENT_REF + && (TREE_CODE (TREE_TYPE (TREE_OPERAND (decl, 0))) + == REFERENCE_TYPE))) + break; + if (DECL_SIZE (decl) + && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + { + tree decl2 = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (decl2) == INDIRECT_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + install_var_local (decl2, ctx); + } + install_var_local (decl, ctx); + break; + } + if (DECL_P (decl)) + { + if (DECL_SIZE (decl) + && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + { + tree decl2 = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (decl2) == INDIRECT_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + install_var_field (decl2, true, 3, ctx); + install_var_local (decl2, ctx); + install_var_local (decl, ctx); + } + else + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER + && !OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c) + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + install_var_field (decl, true, 7, ctx); + else + install_var_field (decl, true, 3, ctx); + if (is_gimple_omp_offloaded (ctx->stmt) + && !(is_gimple_omp_oacc (ctx->stmt) + && OMP_CLAUSE_MAP_IN_REDUCTION (c))) + install_var_local (decl, ctx); + } + } + else + { + tree base = get_base_address (decl); + tree nc = OMP_CLAUSE_CHAIN (c); + if (DECL_P (base) + && nc != NULL_TREE + && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP + && OMP_CLAUSE_DECL (nc) == base + && OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_POINTER + && integer_zerop (OMP_CLAUSE_SIZE (nc))) + { + OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c) = 1; + OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (nc) = 1; + } + else + { + if (ctx->outer) + { + scan_omp_op (&OMP_CLAUSE_DECL (c), ctx->outer); + decl = OMP_CLAUSE_DECL (c); + } + gcc_assert (!splay_tree_lookup (ctx->field_map, + (splay_tree_key) decl)); + tree field + = build_decl (OMP_CLAUSE_LOCATION (c), + FIELD_DECL, NULL_TREE, ptr_type_node); + SET_DECL_ALIGN (field, TYPE_ALIGN (ptr_type_node)); + insert_field_into_struct (ctx->record_type, field); + splay_tree_insert (ctx->field_map, (splay_tree_key) decl, + (splay_tree_value) field); + } + } + break; + + case OMP_CLAUSE_ORDER: + ctx->order_concurrent = true; + break; + + case OMP_CLAUSE_BIND: + ctx->loop_p = true; + break; + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_COLLAPSE: + case OMP_CLAUSE_UNTIED: + case OMP_CLAUSE_MERGEABLE: + case OMP_CLAUSE_PROC_BIND: + case OMP_CLAUSE_SAFELEN: + case OMP_CLAUSE_SIMDLEN: + case OMP_CLAUSE_THREADS: + case OMP_CLAUSE_SIMD: + case OMP_CLAUSE_NOGROUP: + case OMP_CLAUSE_DEFAULTMAP: + case OMP_CLAUSE_ASYNC: + case OMP_CLAUSE_WAIT: + case OMP_CLAUSE_GANG: + case OMP_CLAUSE_WORKER: + case OMP_CLAUSE_VECTOR: + case OMP_CLAUSE_INDEPENDENT: + case OMP_CLAUSE_AUTO: + case OMP_CLAUSE_SEQ: + case OMP_CLAUSE_TILE: + case OMP_CLAUSE__SIMT_: + case OMP_CLAUSE_DEFAULT: + case OMP_CLAUSE_NONTEMPORAL: + case OMP_CLAUSE_IF_PRESENT: + case OMP_CLAUSE_FINALIZE: + case OMP_CLAUSE_TASK_REDUCTION: + case OMP_CLAUSE_ALLOCATE: + break; + + case OMP_CLAUSE_ALIGNED: + decl = OMP_CLAUSE_DECL (c); + if (is_global_var (decl) + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE__CONDTEMP_: + decl = OMP_CLAUSE_DECL (c); + if (is_parallel_ctx (ctx)) + { + install_var_field (decl, false, 3, ctx); + install_var_local (decl, ctx); + } + else if (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD + && !OMP_CLAUSE__CONDTEMP__ITER (c)) + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE__CACHE_: + case OMP_CLAUSE_NOHOST: + default: + gcc_unreachable (); + } + } + + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_LASTPRIVATE: + /* Let the corresponding firstprivate clause create + the variable. */ + if (OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c)) + scan_array_reductions = true; + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_LINEAR: + case OMP_CLAUSE_IS_DEVICE_PTR: + decl = OMP_CLAUSE_DECL (c); + if (is_variable_sized (decl)) + { + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR) + && is_gimple_omp_offloaded (ctx->stmt)) + { + tree decl2 = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (decl2) == INDIRECT_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + install_var_local (decl2, ctx); + fixup_remapped_decl (decl2, ctx, false); + } + install_var_local (decl, ctx); + } + fixup_remapped_decl (decl, ctx, + OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE + && OMP_CLAUSE_PRIVATE_DEBUG (c)); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c)) + scan_array_reductions = true; + break; + + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + decl = OMP_CLAUSE_DECL (c); + if (TREE_CODE (decl) != MEM_REF && !is_omp_target (ctx->stmt)) + { + if (is_variable_sized (decl)) + install_var_local (decl, ctx); + fixup_remapped_decl (decl, ctx, false); + } + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + scan_array_reductions = true; + break; + + case OMP_CLAUSE_TASK_REDUCTION: + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + scan_array_reductions = true; + break; + + case OMP_CLAUSE_SHARED: + /* Ignore shared directives in teams construct inside of + target construct. */ + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && !is_host_teams_ctx (ctx)) + break; + decl = OMP_CLAUSE_DECL (c); + if (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))) + break; + if (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + { + if (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, + ctx->outer))) + break; + bool by_ref = use_pointer_for_field (decl, ctx); + install_var_field (decl, by_ref, 11, ctx); + break; + } + fixup_remapped_decl (decl, ctx, false); + break; + + case OMP_CLAUSE_MAP: + if (!is_gimple_omp_offloaded (ctx->stmt)) + break; + decl = OMP_CLAUSE_DECL (c); + if (DECL_P (decl) + && ((OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER + && (OMP_CLAUSE_MAP_KIND (c) + != GOMP_MAP_FIRSTPRIVATE_REFERENCE)) + || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + && is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx)) + && varpool_node::get_create (decl)->offloadable) + break; + if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH) + && is_omp_target (ctx->stmt) + && !is_gimple_omp_offloaded (ctx->stmt)) + break; + if (DECL_P (decl)) + { + if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER) + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE + && !COMPLETE_TYPE_P (TREE_TYPE (decl))) + { + tree new_decl = lookup_decl (decl, ctx); + TREE_TYPE (new_decl) + = remap_type (TREE_TYPE (decl), &ctx->cb); + } + else if (DECL_SIZE (decl) + && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + { + tree decl2 = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (decl2) == INDIRECT_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + fixup_remapped_decl (decl2, ctx, false); + fixup_remapped_decl (decl, ctx, true); + } + else + fixup_remapped_decl (decl, ctx, false); + } + break; + + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_DEFAULT: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_NUM_TEAMS: + case OMP_CLAUSE_THREAD_LIMIT: + case OMP_CLAUSE_DEVICE: + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_DIST_SCHEDULE: + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_COLLAPSE: + case OMP_CLAUSE_UNTIED: + case OMP_CLAUSE_FINAL: + case OMP_CLAUSE_MERGEABLE: + case OMP_CLAUSE_PROC_BIND: + case OMP_CLAUSE_SAFELEN: + case OMP_CLAUSE_SIMDLEN: + case OMP_CLAUSE_ALIGNED: + case OMP_CLAUSE_DEPEND: + case OMP_CLAUSE_DETACH: + case OMP_CLAUSE_ALLOCATE: + case OMP_CLAUSE__LOOPTEMP_: + case OMP_CLAUSE__REDUCTEMP_: + case OMP_CLAUSE_TO: + case OMP_CLAUSE_FROM: + case OMP_CLAUSE_PRIORITY: + case OMP_CLAUSE_GRAINSIZE: + case OMP_CLAUSE_NUM_TASKS: + case OMP_CLAUSE_THREADS: + case OMP_CLAUSE_SIMD: + case OMP_CLAUSE_NOGROUP: + case OMP_CLAUSE_DEFAULTMAP: + case OMP_CLAUSE_ORDER: + case OMP_CLAUSE_BIND: + case OMP_CLAUSE_USE_DEVICE_PTR: + case OMP_CLAUSE_USE_DEVICE_ADDR: + case OMP_CLAUSE_NONTEMPORAL: + case OMP_CLAUSE_ASYNC: + case OMP_CLAUSE_WAIT: + case OMP_CLAUSE_NUM_GANGS: + case OMP_CLAUSE_NUM_WORKERS: + case OMP_CLAUSE_VECTOR_LENGTH: + case OMP_CLAUSE_GANG: + case OMP_CLAUSE_WORKER: + case OMP_CLAUSE_VECTOR: + case OMP_CLAUSE_INDEPENDENT: + case OMP_CLAUSE_AUTO: + case OMP_CLAUSE_SEQ: + case OMP_CLAUSE_TILE: + case OMP_CLAUSE__SIMT_: + case OMP_CLAUSE_IF_PRESENT: + case OMP_CLAUSE_FINALIZE: + case OMP_CLAUSE_FILTER: + case OMP_CLAUSE__CONDTEMP_: + break; + + case OMP_CLAUSE__CACHE_: + case OMP_CLAUSE_NOHOST: + default: + gcc_unreachable (); + } + } + + gcc_checking_assert (!scan_array_reductions + || !is_gimple_omp_oacc (ctx->stmt)); + if (scan_array_reductions) + { + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION) + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + omp_context *rctx = ctx; + if (is_omp_target (ctx->stmt)) + rctx = ctx->outer; + scan_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c), rctx); + scan_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c), rctx); + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c)) + scan_omp (&OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c), ctx); + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c)) + scan_omp (&OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c), ctx); + } +} + +/* Create a new name for omp child function. Returns an identifier. */ + +static tree +create_omp_child_function_name (bool task_copy) +{ + return clone_function_name_numbered (current_function_decl, + task_copy ? "_omp_cpyfn" : "_omp_fn"); +} + +/* Return true if CTX may belong to offloaded code: either if current function + is offloaded, or any enclosing context corresponds to a target region. */ + +static bool +omp_maybe_offloaded_ctx (omp_context *ctx) +{ + if (cgraph_node::get (current_function_decl)->offloadable) + return true; + for (; ctx; ctx = ctx->outer) + if (is_gimple_omp_offloaded (ctx->stmt)) + return true; + return false; +} + +/* Build a decl for the omp child function. It'll not contain a body + yet, just the bare decl. */ + +static void +create_omp_child_function (omp_context *ctx, bool task_copy) +{ + tree decl, type, name, t; + + name = create_omp_child_function_name (task_copy); + if (task_copy) + type = build_function_type_list (void_type_node, ptr_type_node, + ptr_type_node, NULL_TREE); + else + type = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + + decl = build_decl (gimple_location (ctx->stmt), FUNCTION_DECL, name, type); + + gcc_checking_assert (!is_gimple_omp_oacc (ctx->stmt) + || !task_copy); + if (!task_copy) + ctx->cb.dst_fn = decl; + else + gimple_omp_task_set_copy_fn (ctx->stmt, decl); + + TREE_STATIC (decl) = 1; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 0; + TREE_PUBLIC (decl) = 0; + DECL_UNINLINABLE (decl) = 1; + DECL_EXTERNAL (decl) = 0; + DECL_CONTEXT (decl) = NULL_TREE; + DECL_INITIAL (decl) = make_node (BLOCK); + BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl; + DECL_ATTRIBUTES (decl) = DECL_ATTRIBUTES (current_function_decl); + /* Remove omp declare simd attribute from the new attributes. */ + if (tree a = lookup_attribute ("omp declare simd", DECL_ATTRIBUTES (decl))) + { + while (tree a2 = lookup_attribute ("omp declare simd", TREE_CHAIN (a))) + a = a2; + a = TREE_CHAIN (a); + for (tree *p = &DECL_ATTRIBUTES (decl); *p != a;) + if (is_attribute_p ("omp declare simd", get_attribute_name (*p))) + *p = TREE_CHAIN (*p); + else + { + tree chain = TREE_CHAIN (*p); + *p = copy_node (*p); + p = &TREE_CHAIN (*p); + *p = chain; + } + } + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl) + = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (current_function_decl); + DECL_FUNCTION_SPECIFIC_TARGET (decl) + = DECL_FUNCTION_SPECIFIC_TARGET (current_function_decl); + DECL_FUNCTION_VERSIONED (decl) + = DECL_FUNCTION_VERSIONED (current_function_decl); + + if (omp_maybe_offloaded_ctx (ctx)) + { + cgraph_node::get_create (decl)->offloadable = 1; + if (ENABLE_OFFLOADING) + g->have_offload = true; + } + + if (cgraph_node::get_create (decl)->offloadable) + { + const char *target_attr = (is_gimple_omp_offloaded (ctx->stmt) + ? "omp target entrypoint" + : "omp declare target"); + if (lookup_attribute ("omp declare target", + DECL_ATTRIBUTES (current_function_decl))) + { + if (is_gimple_omp_offloaded (ctx->stmt)) + DECL_ATTRIBUTES (decl) + = remove_attribute ("omp declare target", + copy_list (DECL_ATTRIBUTES (decl))); + else + target_attr = NULL; + } + if (target_attr) + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier (target_attr), + NULL_TREE, DECL_ATTRIBUTES (decl)); + } + + t = build_decl (DECL_SOURCE_LOCATION (decl), + RESULT_DECL, NULL_TREE, void_type_node); + DECL_ARTIFICIAL (t) = 1; + DECL_IGNORED_P (t) = 1; + DECL_CONTEXT (t) = decl; + DECL_RESULT (decl) = t; + + tree data_name = get_identifier (".omp_data_i"); + t = build_decl (DECL_SOURCE_LOCATION (decl), PARM_DECL, data_name, + ptr_type_node); + DECL_ARTIFICIAL (t) = 1; + DECL_NAMELESS (t) = 1; + DECL_ARG_TYPE (t) = ptr_type_node; + DECL_CONTEXT (t) = current_function_decl; + TREE_USED (t) = 1; + TREE_READONLY (t) = 1; + DECL_ARGUMENTS (decl) = t; + if (!task_copy) + ctx->receiver_decl = t; + else + { + t = build_decl (DECL_SOURCE_LOCATION (decl), + PARM_DECL, get_identifier (".omp_data_o"), + ptr_type_node); + DECL_ARTIFICIAL (t) = 1; + DECL_NAMELESS (t) = 1; + DECL_ARG_TYPE (t) = ptr_type_node; + DECL_CONTEXT (t) = current_function_decl; + TREE_USED (t) = 1; + TREE_ADDRESSABLE (t) = 1; + DECL_CHAIN (t) = DECL_ARGUMENTS (decl); + DECL_ARGUMENTS (decl) = t; + } + + /* Allocate memory for the function structure. The call to + allocate_struct_function clobbers CFUN, so we need to restore + it afterward. */ + push_struct_function (decl); + cfun->function_end_locus = gimple_location (ctx->stmt); + init_tree_ssa (cfun); + pop_cfun (); +} + +/* Callback for walk_gimple_seq. Check if combined parallel + contains gimple_omp_for_combined_into_p OMP_FOR. */ + +tree +omp_find_combined_for (gimple_stmt_iterator *gsi_p, + bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + switch (gimple_code (stmt)) + { + WALK_SUBSTMTS; + + case GIMPLE_OMP_FOR: + if (gimple_omp_for_combined_into_p (stmt) + && gimple_omp_for_kind (stmt) + == *(const enum gf_mask *) (wi->info)) + { + wi->info = stmt; + return integer_zero_node; + } + break; + default: + break; + } + return NULL; +} + +/* Add _LOOPTEMP_/_REDUCTEMP_ clauses on OpenMP parallel or task. */ + +static void +add_taskreg_looptemp_clauses (enum gf_mask msk, gimple *stmt, + omp_context *outer_ctx) +{ + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &msk; + walk_gimple_seq (gimple_omp_body (stmt), omp_find_combined_for, NULL, &wi); + if (wi.info != (void *) &msk) + { + gomp_for *for_stmt = as_a <gomp_for *> ((gimple *) wi.info); + struct omp_for_data fd; + omp_extract_for_data (for_stmt, &fd, NULL); + /* We need two temporaries with fd.loop.v type (istart/iend) + and then (fd.collapse - 1) temporaries with the same + type for count2 ... countN-1 vars if not constant. */ + size_t count = 2, i; + tree type = fd.iter_type; + if (fd.collapse > 1 + && TREE_CODE (fd.loop.n2) != INTEGER_CST) + { + count += fd.collapse - 1; + /* If there are lastprivate clauses on the inner + GIMPLE_OMP_FOR, add one more temporaries for the total number + of iterations (product of count1 ... countN-1). */ + if (omp_find_clause (gimple_omp_for_clauses (for_stmt), + OMP_CLAUSE_LASTPRIVATE) + || (msk == GF_OMP_FOR_KIND_FOR + && omp_find_clause (gimple_omp_parallel_clauses (stmt), + OMP_CLAUSE_LASTPRIVATE))) + { + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, + OMP_CLAUSE__LOOPTEMP_); + insert_decl_map (&outer_ctx->cb, temp, temp); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt); + gimple_omp_taskreg_set_clauses (stmt, c); + } + if (fd.non_rect + && fd.last_nonrect == fd.first_nonrect + 1) + if (tree v = gimple_omp_for_index (for_stmt, fd.last_nonrect)) + if (!TYPE_UNSIGNED (TREE_TYPE (v))) + { + v = gimple_omp_for_index (for_stmt, fd.first_nonrect); + tree type2 = TREE_TYPE (v); + count++; + for (i = 0; i < 3; i++) + { + tree temp = create_tmp_var (type2); + tree c = build_omp_clause (UNKNOWN_LOCATION, + OMP_CLAUSE__LOOPTEMP_); + insert_decl_map (&outer_ctx->cb, temp, temp); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt); + gimple_omp_taskreg_set_clauses (stmt, c); + } + } + } + for (i = 0; i < count; i++) + { + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__LOOPTEMP_); + insert_decl_map (&outer_ctx->cb, temp, temp); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt); + gimple_omp_taskreg_set_clauses (stmt, c); + } + } + if (msk == GF_OMP_FOR_KIND_TASKLOOP + && omp_find_clause (gimple_omp_task_clauses (stmt), + OMP_CLAUSE_REDUCTION)) + { + tree type = build_pointer_type (pointer_sized_int_node); + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__REDUCTEMP_); + insert_decl_map (&outer_ctx->cb, temp, temp); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_task_clauses (stmt); + gimple_omp_task_set_clauses (stmt, c); + } +} + +/* Scan an OpenMP parallel directive. */ + +static void +scan_omp_parallel (gimple_stmt_iterator *gsi, omp_context *outer_ctx) +{ + omp_context *ctx; + tree name; + gomp_parallel *stmt = as_a <gomp_parallel *> (gsi_stmt (*gsi)); + + /* Ignore parallel directives with empty bodies, unless there + are copyin clauses. */ + if (optimize > 0 + && empty_body_p (gimple_omp_body (stmt)) + && omp_find_clause (gimple_omp_parallel_clauses (stmt), + OMP_CLAUSE_COPYIN) == NULL) + { + gsi_replace (gsi, gimple_build_nop (), false); + return; + } + + if (gimple_omp_parallel_combined_p (stmt)) + add_taskreg_looptemp_clauses (GF_OMP_FOR_KIND_FOR, stmt, outer_ctx); + for (tree c = omp_find_clause (gimple_omp_parallel_clauses (stmt), + OMP_CLAUSE_REDUCTION); + c; c = omp_find_clause (OMP_CLAUSE_CHAIN (c), OMP_CLAUSE_REDUCTION)) + if (OMP_CLAUSE_REDUCTION_TASK (c)) + { + tree type = build_pointer_type (pointer_sized_int_node); + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__REDUCTEMP_); + if (outer_ctx) + insert_decl_map (&outer_ctx->cb, temp, temp); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_parallel_clauses (stmt); + gimple_omp_parallel_set_clauses (stmt, c); + break; + } + else if (OMP_CLAUSE_CHAIN (c) == NULL_TREE) + break; + + ctx = new_omp_context (stmt, outer_ctx); + taskreg_contexts.safe_push (ctx); + if (taskreg_nesting_level > 1) + ctx->is_nested = true; + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + name = create_tmp_var_name (".omp_data_s"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->record_type); + DECL_ARTIFICIAL (name) = 1; + DECL_NAMELESS (name) = 1; + TYPE_NAME (ctx->record_type) = name; + TYPE_ARTIFICIAL (ctx->record_type) = 1; + create_omp_child_function (ctx, false); + gimple_omp_parallel_set_child_fn (stmt, ctx->cb.dst_fn); + + scan_sharing_clauses (gimple_omp_parallel_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = ctx->receiver_decl = NULL; +} + +/* Scan an OpenMP task directive. */ + +static void +scan_omp_task (gimple_stmt_iterator *gsi, omp_context *outer_ctx) +{ + omp_context *ctx; + tree name, t; + gomp_task *stmt = as_a <gomp_task *> (gsi_stmt (*gsi)); + + /* Ignore task directives with empty bodies, unless they have depend + clause. */ + if (optimize > 0 + && gimple_omp_body (stmt) + && empty_body_p (gimple_omp_body (stmt)) + && !omp_find_clause (gimple_omp_task_clauses (stmt), OMP_CLAUSE_DEPEND)) + { + gsi_replace (gsi, gimple_build_nop (), false); + return; + } + + if (gimple_omp_task_taskloop_p (stmt)) + add_taskreg_looptemp_clauses (GF_OMP_FOR_KIND_TASKLOOP, stmt, outer_ctx); + + ctx = new_omp_context (stmt, outer_ctx); + + if (gimple_omp_task_taskwait_p (stmt)) + { + scan_sharing_clauses (gimple_omp_task_clauses (stmt), ctx); + return; + } + + taskreg_contexts.safe_push (ctx); + if (taskreg_nesting_level > 1) + ctx->is_nested = true; + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + name = create_tmp_var_name (".omp_data_s"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->record_type); + DECL_ARTIFICIAL (name) = 1; + DECL_NAMELESS (name) = 1; + TYPE_NAME (ctx->record_type) = name; + TYPE_ARTIFICIAL (ctx->record_type) = 1; + create_omp_child_function (ctx, false); + gimple_omp_task_set_child_fn (stmt, ctx->cb.dst_fn); + + scan_sharing_clauses (gimple_omp_task_clauses (stmt), ctx); + + if (ctx->srecord_type) + { + name = create_tmp_var_name (".omp_data_a"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->srecord_type); + DECL_ARTIFICIAL (name) = 1; + DECL_NAMELESS (name) = 1; + TYPE_NAME (ctx->srecord_type) = name; + TYPE_ARTIFICIAL (ctx->srecord_type) = 1; + create_omp_child_function (ctx, true); + } + + scan_omp (gimple_omp_body_ptr (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + { + ctx->record_type = ctx->receiver_decl = NULL; + t = build_int_cst (long_integer_type_node, 0); + gimple_omp_task_set_arg_size (stmt, t); + t = build_int_cst (long_integer_type_node, 1); + gimple_omp_task_set_arg_align (stmt, t); + } +} + +/* Helper function for finish_taskreg_scan, called through walk_tree. + If maybe_lookup_decl_in_outer_context returns non-NULL for some + tree, replace it in the expression. */ + +static tree +finish_taskreg_remap (tree *tp, int *walk_subtrees, void *data) +{ + if (VAR_P (*tp)) + { + omp_context *ctx = (omp_context *) data; + tree t = maybe_lookup_decl_in_outer_ctx (*tp, ctx); + if (t != *tp) + { + if (DECL_HAS_VALUE_EXPR_P (t)) + t = unshare_expr (DECL_VALUE_EXPR (t)); + *tp = t; + } + *walk_subtrees = 0; + } + else if (IS_TYPE_OR_DECL_P (*tp)) + *walk_subtrees = 0; + return NULL_TREE; +} + +/* If any decls have been made addressable during scan_omp, + adjust their fields if needed, and layout record types + of parallel/task constructs. */ + +static void +finish_taskreg_scan (omp_context *ctx) +{ + if (ctx->record_type == NULL_TREE) + return; + + /* If any task_shared_vars were needed, verify all + OMP_CLAUSE_SHARED clauses on GIMPLE_OMP_{PARALLEL,TASK,TEAMS} + statements if use_pointer_for_field hasn't changed + because of that. If it did, update field types now. */ + if (task_shared_vars) + { + tree c; + + for (c = gimple_omp_taskreg_clauses (ctx->stmt); + c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED + && !OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + { + tree decl = OMP_CLAUSE_DECL (c); + + /* Global variables don't need to be copied, + the receiver side will use them directly. */ + if (is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))) + continue; + if (!bitmap_bit_p (task_shared_vars, DECL_UID (decl)) + || !use_pointer_for_field (decl, ctx)) + continue; + tree field = lookup_field (decl, ctx); + if (TREE_CODE (TREE_TYPE (field)) == POINTER_TYPE + && TREE_TYPE (TREE_TYPE (field)) == TREE_TYPE (decl)) + continue; + TREE_TYPE (field) = build_pointer_type (TREE_TYPE (decl)); + TREE_THIS_VOLATILE (field) = 0; + DECL_USER_ALIGN (field) = 0; + SET_DECL_ALIGN (field, TYPE_ALIGN (TREE_TYPE (field))); + if (TYPE_ALIGN (ctx->record_type) < DECL_ALIGN (field)) + SET_TYPE_ALIGN (ctx->record_type, DECL_ALIGN (field)); + if (ctx->srecord_type) + { + tree sfield = lookup_sfield (decl, ctx); + TREE_TYPE (sfield) = TREE_TYPE (field); + TREE_THIS_VOLATILE (sfield) = 0; + DECL_USER_ALIGN (sfield) = 0; + SET_DECL_ALIGN (sfield, DECL_ALIGN (field)); + if (TYPE_ALIGN (ctx->srecord_type) < DECL_ALIGN (sfield)) + SET_TYPE_ALIGN (ctx->srecord_type, DECL_ALIGN (sfield)); + } + } + } + + if (gimple_code (ctx->stmt) == GIMPLE_OMP_PARALLEL) + { + tree clauses = gimple_omp_parallel_clauses (ctx->stmt); + tree c = omp_find_clause (clauses, OMP_CLAUSE__REDUCTEMP_); + if (c) + { + /* Move the _reductemp_ clause first. GOMP_parallel_reductions + expects to find it at the start of data. */ + tree f = lookup_field (OMP_CLAUSE_DECL (c), ctx); + tree *p = &TYPE_FIELDS (ctx->record_type); + while (*p) + if (*p == f) + { + *p = DECL_CHAIN (*p); + break; + } + else + p = &DECL_CHAIN (*p); + DECL_CHAIN (f) = TYPE_FIELDS (ctx->record_type); + TYPE_FIELDS (ctx->record_type) = f; + } + layout_type (ctx->record_type); + fixup_child_record_type (ctx); + } + else if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS) + { + layout_type (ctx->record_type); + fixup_child_record_type (ctx); + } + else + { + location_t loc = gimple_location (ctx->stmt); + tree *p, vla_fields = NULL_TREE, *q = &vla_fields; + tree detach_clause + = omp_find_clause (gimple_omp_task_clauses (ctx->stmt), + OMP_CLAUSE_DETACH); + /* Move VLA fields to the end. */ + p = &TYPE_FIELDS (ctx->record_type); + while (*p) + if (!TYPE_SIZE_UNIT (TREE_TYPE (*p)) + || ! TREE_CONSTANT (TYPE_SIZE_UNIT (TREE_TYPE (*p)))) + { + *q = *p; + *p = TREE_CHAIN (*p); + TREE_CHAIN (*q) = NULL_TREE; + q = &TREE_CHAIN (*q); + } + else + p = &DECL_CHAIN (*p); + *p = vla_fields; + if (gimple_omp_task_taskloop_p (ctx->stmt)) + { + /* Move fields corresponding to first and second _looptemp_ + clause first. There are filled by GOMP_taskloop + and thus need to be in specific positions. */ + tree clauses = gimple_omp_task_clauses (ctx->stmt); + tree c1 = omp_find_clause (clauses, OMP_CLAUSE__LOOPTEMP_); + tree c2 = omp_find_clause (OMP_CLAUSE_CHAIN (c1), + OMP_CLAUSE__LOOPTEMP_); + tree c3 = omp_find_clause (clauses, OMP_CLAUSE__REDUCTEMP_); + tree f1 = lookup_field (OMP_CLAUSE_DECL (c1), ctx); + tree f2 = lookup_field (OMP_CLAUSE_DECL (c2), ctx); + tree f3 = c3 ? lookup_field (OMP_CLAUSE_DECL (c3), ctx) : NULL_TREE; + p = &TYPE_FIELDS (ctx->record_type); + while (*p) + if (*p == f1 || *p == f2 || *p == f3) + *p = DECL_CHAIN (*p); + else + p = &DECL_CHAIN (*p); + DECL_CHAIN (f1) = f2; + if (c3) + { + DECL_CHAIN (f2) = f3; + DECL_CHAIN (f3) = TYPE_FIELDS (ctx->record_type); + } + else + DECL_CHAIN (f2) = TYPE_FIELDS (ctx->record_type); + TYPE_FIELDS (ctx->record_type) = f1; + if (ctx->srecord_type) + { + f1 = lookup_sfield (OMP_CLAUSE_DECL (c1), ctx); + f2 = lookup_sfield (OMP_CLAUSE_DECL (c2), ctx); + if (c3) + f3 = lookup_sfield (OMP_CLAUSE_DECL (c3), ctx); + p = &TYPE_FIELDS (ctx->srecord_type); + while (*p) + if (*p == f1 || *p == f2 || *p == f3) + *p = DECL_CHAIN (*p); + else + p = &DECL_CHAIN (*p); + DECL_CHAIN (f1) = f2; + DECL_CHAIN (f2) = TYPE_FIELDS (ctx->srecord_type); + if (c3) + { + DECL_CHAIN (f2) = f3; + DECL_CHAIN (f3) = TYPE_FIELDS (ctx->srecord_type); + } + else + DECL_CHAIN (f2) = TYPE_FIELDS (ctx->srecord_type); + TYPE_FIELDS (ctx->srecord_type) = f1; + } + } + if (detach_clause) + { + tree c, field; + + /* Look for a firstprivate clause with the detach event handle. */ + for (c = gimple_omp_taskreg_clauses (ctx->stmt); + c; c = OMP_CLAUSE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_FIRSTPRIVATE) + continue; + if (maybe_lookup_decl_in_outer_ctx (OMP_CLAUSE_DECL (c), ctx) + == OMP_CLAUSE_DECL (detach_clause)) + break; + } + + gcc_assert (c); + field = lookup_field (OMP_CLAUSE_DECL (c), ctx); + + /* Move field corresponding to the detach clause first. + This is filled by GOMP_task and needs to be in a + specific position. */ + p = &TYPE_FIELDS (ctx->record_type); + while (*p) + if (*p == field) + *p = DECL_CHAIN (*p); + else + p = &DECL_CHAIN (*p); + DECL_CHAIN (field) = TYPE_FIELDS (ctx->record_type); + TYPE_FIELDS (ctx->record_type) = field; + if (ctx->srecord_type) + { + field = lookup_sfield (OMP_CLAUSE_DECL (c), ctx); + p = &TYPE_FIELDS (ctx->srecord_type); + while (*p) + if (*p == field) + *p = DECL_CHAIN (*p); + else + p = &DECL_CHAIN (*p); + DECL_CHAIN (field) = TYPE_FIELDS (ctx->srecord_type); + TYPE_FIELDS (ctx->srecord_type) = field; + } + } + layout_type (ctx->record_type); + fixup_child_record_type (ctx); + if (ctx->srecord_type) + layout_type (ctx->srecord_type); + tree t = fold_convert_loc (loc, long_integer_type_node, + TYPE_SIZE_UNIT (ctx->record_type)); + if (TREE_CODE (t) != INTEGER_CST) + { + t = unshare_expr (t); + walk_tree (&t, finish_taskreg_remap, ctx, NULL); + } + gimple_omp_task_set_arg_size (ctx->stmt, t); + t = build_int_cst (long_integer_type_node, + TYPE_ALIGN_UNIT (ctx->record_type)); + gimple_omp_task_set_arg_align (ctx->stmt, t); + } +} + +/* Find the enclosing offload context. */ + +static omp_context * +enclosing_target_ctx (omp_context *ctx) +{ + for (; ctx; ctx = ctx->outer) + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TARGET) + break; + + return ctx; +} + +/* Return whether CTX's parent compute construct is an OpenACC 'kernels' + construct. + (This doesn't include OpenACC 'kernels' decomposed parts.) */ + +static bool +ctx_in_oacc_kernels_region (omp_context *ctx) +{ + for (;ctx != NULL; ctx = ctx->outer) + { + gimple *stmt = ctx->stmt; + if (gimple_code (stmt) == GIMPLE_OMP_TARGET + && gimple_omp_target_kind (stmt) == GF_OMP_TARGET_KIND_OACC_KERNELS) + return true; + } + + return false; +} + +/* Check the parallelism clauses inside a OpenACC 'kernels' region. + (This doesn't include OpenACC 'kernels' decomposed parts.) + Until kernels handling moves to use the same loop indirection + scheme as parallel, we need to do this checking early. */ + +static unsigned +check_oacc_kernel_gwv (gomp_for *stmt, omp_context *ctx) +{ + bool checking = true; + unsigned outer_mask = 0; + unsigned this_mask = 0; + bool has_seq = false, has_auto = false; + + if (ctx->outer) + outer_mask = check_oacc_kernel_gwv (NULL, ctx->outer); + if (!stmt) + { + checking = false; + if (gimple_code (ctx->stmt) != GIMPLE_OMP_FOR) + return outer_mask; + stmt = as_a <gomp_for *> (ctx->stmt); + } + + for (tree c = gimple_omp_for_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + { + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_GANG: + this_mask |= GOMP_DIM_MASK (GOMP_DIM_GANG); + break; + case OMP_CLAUSE_WORKER: + this_mask |= GOMP_DIM_MASK (GOMP_DIM_WORKER); + break; + case OMP_CLAUSE_VECTOR: + this_mask |= GOMP_DIM_MASK (GOMP_DIM_VECTOR); + break; + case OMP_CLAUSE_SEQ: + has_seq = true; + break; + case OMP_CLAUSE_AUTO: + has_auto = true; + break; + default: + break; + } + } + + if (checking) + { + if (has_seq && (this_mask || has_auto)) + error_at (gimple_location (stmt), "%<seq%> overrides other" + " OpenACC loop specifiers"); + else if (has_auto && this_mask) + error_at (gimple_location (stmt), "%<auto%> conflicts with other" + " OpenACC loop specifiers"); + + if (this_mask & outer_mask) + error_at (gimple_location (stmt), "inner loop uses same" + " OpenACC parallelism as containing loop"); + } + + return outer_mask | this_mask; +} + +/* Scan a GIMPLE_OMP_FOR. */ + +static omp_context * +scan_omp_for (gomp_for *stmt, omp_context *outer_ctx) +{ + omp_context *ctx; + size_t i; + tree clauses = gimple_omp_for_clauses (stmt); + + ctx = new_omp_context (stmt, outer_ctx); + + if (is_gimple_omp_oacc (stmt)) + { + omp_context *tgt = enclosing_target_ctx (outer_ctx); + + if (!(tgt && is_oacc_kernels (tgt))) + for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + tree c_op0; + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_GANG: + c_op0 = OMP_CLAUSE_GANG_EXPR (c); + break; + + case OMP_CLAUSE_WORKER: + c_op0 = OMP_CLAUSE_WORKER_EXPR (c); + break; + + case OMP_CLAUSE_VECTOR: + c_op0 = OMP_CLAUSE_VECTOR_EXPR (c); + break; + + default: + continue; + } + + if (c_op0) + { + /* By construction, this is impossible for OpenACC 'kernels' + decomposed parts. */ + gcc_assert (!(tgt && is_oacc_kernels_decomposed_part (tgt))); + + error_at (OMP_CLAUSE_LOCATION (c), + "argument not permitted on %qs clause", + omp_clause_code_name[OMP_CLAUSE_CODE (c)]); + if (tgt) + inform (gimple_location (tgt->stmt), + "enclosing parent compute construct"); + else if (oacc_get_fn_attrib (current_function_decl)) + inform (DECL_SOURCE_LOCATION (current_function_decl), + "enclosing routine"); + else + gcc_unreachable (); + } + } + + if (tgt && is_oacc_kernels (tgt)) + check_oacc_kernel_gwv (stmt, ctx); + + /* Collect all variables named in reductions on this loop. Ensure + that, if this loop has a reduction on some variable v, and there is + a reduction on v somewhere in an outer context, then there is a + reduction on v on all intervening loops as well. */ + tree local_reduction_clauses = NULL; + for (tree c = gimple_omp_for_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) + local_reduction_clauses + = tree_cons (NULL, c, local_reduction_clauses); + } + if (ctx->outer_reduction_clauses == NULL && ctx->outer != NULL) + ctx->outer_reduction_clauses + = chainon (unshare_expr (ctx->outer->local_reduction_clauses), + ctx->outer->outer_reduction_clauses); + tree outer_reduction_clauses = ctx->outer_reduction_clauses; + tree local_iter = local_reduction_clauses; + for (; local_iter; local_iter = TREE_CHAIN (local_iter)) + { + tree local_clause = TREE_VALUE (local_iter); + tree local_var = OMP_CLAUSE_DECL (local_clause); + tree_code local_op = OMP_CLAUSE_REDUCTION_CODE (local_clause); + bool have_outer_reduction = false; + tree ctx_iter = outer_reduction_clauses; + for (; ctx_iter; ctx_iter = TREE_CHAIN (ctx_iter)) + { + tree outer_clause = TREE_VALUE (ctx_iter); + tree outer_var = OMP_CLAUSE_DECL (outer_clause); + tree_code outer_op = OMP_CLAUSE_REDUCTION_CODE (outer_clause); + if (outer_var == local_var && outer_op != local_op) + { + warning_at (OMP_CLAUSE_LOCATION (local_clause), 0, + "conflicting reduction operations for %qE", + local_var); + inform (OMP_CLAUSE_LOCATION (outer_clause), + "location of the previous reduction for %qE", + outer_var); + } + if (outer_var == local_var) + { + have_outer_reduction = true; + break; + } + } + if (have_outer_reduction) + { + /* There is a reduction on outer_var both on this loop and on + some enclosing loop. Walk up the context tree until such a + loop with a reduction on outer_var is found, and complain + about all intervening loops that do not have such a + reduction. */ + struct omp_context *curr_loop = ctx->outer; + bool found = false; + while (curr_loop != NULL) + { + tree curr_iter = curr_loop->local_reduction_clauses; + for (; curr_iter; curr_iter = TREE_CHAIN (curr_iter)) + { + tree curr_clause = TREE_VALUE (curr_iter); + tree curr_var = OMP_CLAUSE_DECL (curr_clause); + if (curr_var == local_var) + { + found = true; + break; + } + } + if (!found) + warning_at (gimple_location (curr_loop->stmt), 0, + "nested loop in reduction needs " + "reduction clause for %qE", + local_var); + else + break; + curr_loop = curr_loop->outer; + } + } + } + ctx->local_reduction_clauses = local_reduction_clauses; + ctx->outer_reduction_clauses + = chainon (unshare_expr (ctx->local_reduction_clauses), + ctx->outer_reduction_clauses); + + if (tgt && is_oacc_kernels (tgt)) + { + /* Strip out reductions, as they are not handled yet. */ + tree *prev_ptr = &clauses; + + while (tree probe = *prev_ptr) + { + tree *next_ptr = &OMP_CLAUSE_CHAIN (probe); + + if (OMP_CLAUSE_CODE (probe) == OMP_CLAUSE_REDUCTION) + *prev_ptr = *next_ptr; + else + prev_ptr = next_ptr; + } + + gimple_omp_for_set_clauses (stmt, clauses); + } + } + + scan_sharing_clauses (clauses, ctx); + + scan_omp (gimple_omp_for_pre_body_ptr (stmt), ctx); + for (i = 0; i < gimple_omp_for_collapse (stmt); i++) + { + scan_omp_op (gimple_omp_for_index_ptr (stmt, i), ctx); + scan_omp_op (gimple_omp_for_initial_ptr (stmt, i), ctx); + scan_omp_op (gimple_omp_for_final_ptr (stmt, i), ctx); + scan_omp_op (gimple_omp_for_incr_ptr (stmt, i), ctx); + } + scan_omp (gimple_omp_body_ptr (stmt), ctx); + return ctx; +} + +/* Duplicate #pragma omp simd, one for SIMT, another one for SIMD. */ + +static void +scan_omp_simd (gimple_stmt_iterator *gsi, gomp_for *stmt, + omp_context *outer_ctx) +{ + gbind *bind = gimple_build_bind (NULL, NULL, NULL); + gsi_replace (gsi, bind, false); + gimple_seq seq = NULL; + gimple *g = gimple_build_call_internal (IFN_GOMP_USE_SIMT, 0); + tree cond = create_tmp_var_raw (integer_type_node); + DECL_CONTEXT (cond) = current_function_decl; + DECL_SEEN_IN_BIND_EXPR_P (cond) = 1; + gimple_bind_set_vars (bind, cond); + gimple_call_set_lhs (g, cond); + gimple_seq_add_stmt (&seq, g); + tree lab1 = create_artificial_label (UNKNOWN_LOCATION); + tree lab2 = create_artificial_label (UNKNOWN_LOCATION); + tree lab3 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, cond, integer_zero_node, lab1, lab2); + gimple_seq_add_stmt (&seq, g); + g = gimple_build_label (lab1); + gimple_seq_add_stmt (&seq, g); + gimple_seq new_seq = copy_gimple_seq_and_replace_locals (stmt); + gomp_for *new_stmt = as_a <gomp_for *> (new_seq); + tree clause = build_omp_clause (gimple_location (stmt), OMP_CLAUSE__SIMT_); + OMP_CLAUSE_CHAIN (clause) = gimple_omp_for_clauses (new_stmt); + gimple_omp_for_set_clauses (new_stmt, clause); + gimple_seq_add_stmt (&seq, new_stmt); + g = gimple_build_goto (lab3); + gimple_seq_add_stmt (&seq, g); + g = gimple_build_label (lab2); + gimple_seq_add_stmt (&seq, g); + gimple_seq_add_stmt (&seq, stmt); + g = gimple_build_label (lab3); + gimple_seq_add_stmt (&seq, g); + gimple_bind_set_body (bind, seq); + update_stmt (bind); + scan_omp_for (new_stmt, outer_ctx); + scan_omp_for (stmt, outer_ctx)->simt_stmt = new_stmt; +} + +static tree omp_find_scan (gimple_stmt_iterator *, bool *, + struct walk_stmt_info *); +static omp_context *maybe_lookup_ctx (gimple *); + +/* Duplicate #pragma omp simd, one for the scan input phase loop and one + for scan phase loop. */ + +static void +scan_omp_simd_scan (gimple_stmt_iterator *gsi, gomp_for *stmt, + omp_context *outer_ctx) +{ + /* The only change between inclusive and exclusive scan will be + within the first simd loop, so just use inclusive in the + worksharing loop. */ + outer_ctx->scan_inclusive = true; + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE_INCLUSIVE); + OMP_CLAUSE_DECL (c) = integer_zero_node; + + gomp_scan *input_stmt = gimple_build_omp_scan (NULL, NULL_TREE); + gomp_scan *scan_stmt = gimple_build_omp_scan (NULL, c); + gsi_replace (gsi, input_stmt, false); + gimple_seq input_body = NULL; + gimple_seq_add_stmt (&input_body, stmt); + gsi_insert_after (gsi, scan_stmt, GSI_NEW_STMT); + + gimple_stmt_iterator input1_gsi = gsi_none (); + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input1_gsi; + walk_gimple_seq_mod (gimple_omp_body_ptr (stmt), omp_find_scan, NULL, &wi); + gcc_assert (!gsi_end_p (input1_gsi)); + + gimple *input_stmt1 = gsi_stmt (input1_gsi); + gsi_next (&input1_gsi); + gimple *scan_stmt1 = gsi_stmt (input1_gsi); + gcc_assert (scan_stmt1 && gimple_code (scan_stmt1) == GIMPLE_OMP_SCAN); + c = gimple_omp_scan_clauses (as_a <gomp_scan *> (scan_stmt1)); + if (c && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_EXCLUSIVE) + std::swap (input_stmt1, scan_stmt1); + + gimple_seq input_body1 = gimple_omp_body (input_stmt1); + gimple_omp_set_body (input_stmt1, NULL); + + gimple_seq scan_body = copy_gimple_seq_and_replace_locals (stmt); + gomp_for *new_stmt = as_a <gomp_for *> (scan_body); + + gimple_omp_set_body (input_stmt1, input_body1); + gimple_omp_set_body (scan_stmt1, NULL); + + gimple_stmt_iterator input2_gsi = gsi_none (); + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input2_gsi; + walk_gimple_seq_mod (gimple_omp_body_ptr (new_stmt), omp_find_scan, + NULL, &wi); + gcc_assert (!gsi_end_p (input2_gsi)); + + gimple *input_stmt2 = gsi_stmt (input2_gsi); + gsi_next (&input2_gsi); + gimple *scan_stmt2 = gsi_stmt (input2_gsi); + gcc_assert (scan_stmt2 && gimple_code (scan_stmt2) == GIMPLE_OMP_SCAN); + if (c && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_EXCLUSIVE) + std::swap (input_stmt2, scan_stmt2); + + gimple_omp_set_body (input_stmt2, NULL); + + gimple_omp_set_body (input_stmt, input_body); + gimple_omp_set_body (scan_stmt, scan_body); + + omp_context *ctx = new_omp_context (input_stmt, outer_ctx); + scan_omp (gimple_omp_body_ptr (input_stmt), ctx); + + ctx = new_omp_context (scan_stmt, outer_ctx); + scan_omp (gimple_omp_body_ptr (scan_stmt), ctx); + + maybe_lookup_ctx (new_stmt)->for_simd_scan_phase = true; +} + +/* Scan an OpenMP sections directive. */ + +static void +scan_omp_sections (gomp_sections *stmt, omp_context *outer_ctx) +{ + omp_context *ctx; + + ctx = new_omp_context (stmt, outer_ctx); + scan_sharing_clauses (gimple_omp_sections_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); +} + +/* Scan an OpenMP single directive. */ + +static void +scan_omp_single (gomp_single *stmt, omp_context *outer_ctx) +{ + omp_context *ctx; + tree name; + + ctx = new_omp_context (stmt, outer_ctx); + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + name = create_tmp_var_name (".omp_copy_s"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->record_type); + TYPE_NAME (ctx->record_type) = name; + + scan_sharing_clauses (gimple_omp_single_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = NULL; + else + layout_type (ctx->record_type); +} + +/* Scan a GIMPLE_OMP_TARGET. */ + +static void +scan_omp_target (gomp_target *stmt, omp_context *outer_ctx) +{ + omp_context *ctx; + tree name; + bool offloaded = is_gimple_omp_offloaded (stmt); + tree clauses = gimple_omp_target_clauses (stmt); + + ctx = new_omp_context (stmt, outer_ctx); + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + name = create_tmp_var_name (".omp_data_t"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->record_type); + DECL_ARTIFICIAL (name) = 1; + DECL_NAMELESS (name) = 1; + TYPE_NAME (ctx->record_type) = name; + TYPE_ARTIFICIAL (ctx->record_type) = 1; + + if (offloaded) + { + create_omp_child_function (ctx, false); + gimple_omp_target_set_child_fn (stmt, ctx->cb.dst_fn); + } + + scan_sharing_clauses (clauses, ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = ctx->receiver_decl = NULL; + else + { + TYPE_FIELDS (ctx->record_type) + = nreverse (TYPE_FIELDS (ctx->record_type)); + if (flag_checking) + { + unsigned int align = DECL_ALIGN (TYPE_FIELDS (ctx->record_type)); + for (tree field = TYPE_FIELDS (ctx->record_type); + field; + field = DECL_CHAIN (field)) + gcc_assert (DECL_ALIGN (field) == align); + } + layout_type (ctx->record_type); + if (offloaded) + fixup_child_record_type (ctx); + } + + if (ctx->teams_nested_p && ctx->nonteams_nested_p) + { + error_at (gimple_location (stmt), + "%<target%> construct with nested %<teams%> construct " + "contains directives outside of the %<teams%> construct"); + gimple_omp_set_body (stmt, gimple_build_bind (NULL, NULL, NULL)); + } +} + +/* Scan an OpenMP teams directive. */ + +static void +scan_omp_teams (gomp_teams *stmt, omp_context *outer_ctx) +{ + omp_context *ctx = new_omp_context (stmt, outer_ctx); + + if (!gimple_omp_teams_host (stmt)) + { + scan_sharing_clauses (gimple_omp_teams_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + return; + } + taskreg_contexts.safe_push (ctx); + gcc_assert (taskreg_nesting_level == 1); + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + tree name = create_tmp_var_name (".omp_data_s"); + name = build_decl (gimple_location (stmt), + TYPE_DECL, name, ctx->record_type); + DECL_ARTIFICIAL (name) = 1; + DECL_NAMELESS (name) = 1; + TYPE_NAME (ctx->record_type) = name; + TYPE_ARTIFICIAL (ctx->record_type) = 1; + create_omp_child_function (ctx, false); + gimple_omp_teams_set_child_fn (stmt, ctx->cb.dst_fn); + + scan_sharing_clauses (gimple_omp_teams_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = ctx->receiver_decl = NULL; +} + +/* Check nesting restrictions. */ +static bool +check_omp_nesting_restrictions (gimple *stmt, omp_context *ctx) +{ + tree c; + + /* No nesting of non-OpenACC STMT (that is, an OpenMP one, or a GOMP builtin) + inside an OpenACC CTX. */ + if (gimple_code (stmt) == GIMPLE_OMP_ATOMIC_LOAD + || gimple_code (stmt) == GIMPLE_OMP_ATOMIC_STORE) + /* ..., except for the atomic codes that OpenACC shares with OpenMP. */ + ; + else if (!(is_gimple_omp (stmt) + && is_gimple_omp_oacc (stmt))) + { + if (oacc_get_fn_attrib (cfun->decl) != NULL) + { + error_at (gimple_location (stmt), + "non-OpenACC construct inside of OpenACC routine"); + return false; + } + else + for (omp_context *octx = ctx; octx != NULL; octx = octx->outer) + if (is_gimple_omp (octx->stmt) + && is_gimple_omp_oacc (octx->stmt)) + { + error_at (gimple_location (stmt), + "non-OpenACC construct inside of OpenACC region"); + return false; + } + } + + if (ctx != NULL) + { + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TARGET + && gimple_omp_target_kind (ctx->stmt) == GF_OMP_TARGET_KIND_REGION) + { + c = omp_find_clause (gimple_omp_target_clauses (ctx->stmt), + OMP_CLAUSE_DEVICE); + if (c && OMP_CLAUSE_DEVICE_ANCESTOR (c)) + { + error_at (gimple_location (stmt), + "OpenMP constructs are not allowed in target region " + "with %<ancestor%>"); + return false; + } + + if (gimple_code (stmt) == GIMPLE_OMP_TEAMS && !ctx->teams_nested_p) + ctx->teams_nested_p = true; + else + ctx->nonteams_nested_p = true; + } + if (gimple_code (ctx->stmt) == GIMPLE_OMP_SCAN + && ctx->outer + && gimple_code (ctx->outer->stmt) == GIMPLE_OMP_FOR) + ctx = ctx->outer; + if (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD + && !ctx->loop_p) + { + c = NULL_TREE; + if (ctx->order_concurrent + && (gimple_code (stmt) == GIMPLE_OMP_ORDERED + || gimple_code (stmt) == GIMPLE_OMP_ATOMIC_LOAD + || gimple_code (stmt) == GIMPLE_OMP_ATOMIC_STORE)) + { + error_at (gimple_location (stmt), + "OpenMP constructs other than %<parallel%>, %<loop%>" + " or %<simd%> may not be nested inside a region with" + " the %<order(concurrent)%> clause"); + return false; + } + if (gimple_code (stmt) == GIMPLE_OMP_ORDERED) + { + c = gimple_omp_ordered_clauses (as_a <gomp_ordered *> (stmt)); + if (omp_find_clause (c, OMP_CLAUSE_SIMD)) + { + if (omp_find_clause (c, OMP_CLAUSE_THREADS) + && (ctx->outer == NULL + || !gimple_omp_for_combined_into_p (ctx->stmt) + || gimple_code (ctx->outer->stmt) != GIMPLE_OMP_FOR + || (gimple_omp_for_kind (ctx->outer->stmt) + != GF_OMP_FOR_KIND_FOR) + || !gimple_omp_for_combined_p (ctx->outer->stmt))) + { + error_at (gimple_location (stmt), + "%<ordered simd threads%> must be closely " + "nested inside of %<%s simd%> region", + lang_GNU_Fortran () ? "do" : "for"); + return false; + } + return true; + } + } + else if (gimple_code (stmt) == GIMPLE_OMP_ATOMIC_LOAD + || gimple_code (stmt) == GIMPLE_OMP_ATOMIC_STORE + || gimple_code (stmt) == GIMPLE_OMP_SCAN) + return true; + else if (gimple_code (stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD) + return true; + error_at (gimple_location (stmt), + "OpenMP constructs other than " + "%<ordered simd%>, %<simd%>, %<loop%> or %<atomic%> may " + "not be nested inside %<simd%> region"); + return false; + } + else if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS) + { + if ((gimple_code (stmt) != GIMPLE_OMP_FOR + || (gimple_omp_for_kind (stmt) != GF_OMP_FOR_KIND_DISTRIBUTE + && omp_find_clause (gimple_omp_for_clauses (stmt), + OMP_CLAUSE_BIND) == NULL_TREE)) + && gimple_code (stmt) != GIMPLE_OMP_PARALLEL) + { + error_at (gimple_location (stmt), + "only %<distribute%>, %<parallel%> or %<loop%> " + "regions are allowed to be strictly nested inside " + "%<teams%> region"); + return false; + } + } + else if (ctx->order_concurrent + && gimple_code (stmt) != GIMPLE_OMP_PARALLEL + && (gimple_code (stmt) != GIMPLE_OMP_FOR + || gimple_omp_for_kind (stmt) != GF_OMP_FOR_KIND_SIMD) + && gimple_code (stmt) != GIMPLE_OMP_SCAN) + { + if (ctx->loop_p) + error_at (gimple_location (stmt), + "OpenMP constructs other than %<parallel%>, %<loop%> or " + "%<simd%> may not be nested inside a %<loop%> region"); + else + error_at (gimple_location (stmt), + "OpenMP constructs other than %<parallel%>, %<loop%> or " + "%<simd%> may not be nested inside a region with " + "the %<order(concurrent)%> clause"); + return false; + } + } + switch (gimple_code (stmt)) + { + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_SIMD) + return true; + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_DISTRIBUTE) + { + if (ctx != NULL && gimple_code (ctx->stmt) != GIMPLE_OMP_TEAMS) + { + error_at (gimple_location (stmt), + "%<distribute%> region must be strictly nested " + "inside %<teams%> construct"); + return false; + } + return true; + } + /* We split taskloop into task and nested taskloop in it. */ + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_TASKLOOP) + return true; + /* For now, hope this will change and loop bind(parallel) will not + be allowed in lots of contexts. */ + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_FOR + && omp_find_clause (gimple_omp_for_clauses (stmt), OMP_CLAUSE_BIND)) + return true; + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_OACC_LOOP) + { + bool ok = false; + + if (ctx) + switch (gimple_code (ctx->stmt)) + { + case GIMPLE_OMP_FOR: + ok = (gimple_omp_for_kind (ctx->stmt) + == GF_OMP_FOR_KIND_OACC_LOOP); + break; + + case GIMPLE_OMP_TARGET: + switch (gimple_omp_target_kind (ctx->stmt)) + { + case GF_OMP_TARGET_KIND_OACC_PARALLEL: + case GF_OMP_TARGET_KIND_OACC_KERNELS: + case GF_OMP_TARGET_KIND_OACC_SERIAL: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_PARALLELIZED: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_GANG_SINGLE: + ok = true; + break; + + default: + break; + } + + default: + break; + } + else if (oacc_get_fn_attrib (current_function_decl)) + ok = true; + if (!ok) + { + error_at (gimple_location (stmt), + "OpenACC loop directive must be associated with" + " an OpenACC compute region"); + return false; + } + } + /* FALLTHRU */ + case GIMPLE_CALL: + if (is_gimple_call (stmt) + && (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCEL + || DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCELLATION_POINT)) + { + const char *bad = NULL; + const char *kind = NULL; + const char *construct + = (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCEL) + ? "cancel" + : "cancellation point"; + if (ctx == NULL) + { + error_at (gimple_location (stmt), "orphaned %qs construct", + construct); + return false; + } + switch (tree_fits_shwi_p (gimple_call_arg (stmt, 0)) + ? tree_to_shwi (gimple_call_arg (stmt, 0)) + : 0) + { + case 1: + if (gimple_code (ctx->stmt) != GIMPLE_OMP_PARALLEL) + bad = "parallel"; + else if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCEL + && !integer_zerop (gimple_call_arg (stmt, 1))) + ctx->cancellable = true; + kind = "parallel"; + break; + case 2: + if (gimple_code (ctx->stmt) != GIMPLE_OMP_FOR + || gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_FOR) + bad = "for"; + else if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCEL + && !integer_zerop (gimple_call_arg (stmt, 1))) + { + ctx->cancellable = true; + if (omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_NOWAIT)) + warning_at (gimple_location (stmt), 0, + "%<cancel for%> inside " + "%<nowait%> for construct"); + if (omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_ORDERED)) + warning_at (gimple_location (stmt), 0, + "%<cancel for%> inside " + "%<ordered%> for construct"); + } + kind = "for"; + break; + case 4: + if (gimple_code (ctx->stmt) != GIMPLE_OMP_SECTIONS + && gimple_code (ctx->stmt) != GIMPLE_OMP_SECTION) + bad = "sections"; + else if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + == BUILT_IN_GOMP_CANCEL + && !integer_zerop (gimple_call_arg (stmt, 1))) + { + if (gimple_code (ctx->stmt) == GIMPLE_OMP_SECTIONS) + { + ctx->cancellable = true; + if (omp_find_clause (gimple_omp_sections_clauses + (ctx->stmt), + OMP_CLAUSE_NOWAIT)) + warning_at (gimple_location (stmt), 0, + "%<cancel sections%> inside " + "%<nowait%> sections construct"); + } + else + { + gcc_assert (ctx->outer + && gimple_code (ctx->outer->stmt) + == GIMPLE_OMP_SECTIONS); + ctx->outer->cancellable = true; + if (omp_find_clause (gimple_omp_sections_clauses + (ctx->outer->stmt), + OMP_CLAUSE_NOWAIT)) + warning_at (gimple_location (stmt), 0, + "%<cancel sections%> inside " + "%<nowait%> sections construct"); + } + } + kind = "sections"; + break; + case 8: + if (!is_task_ctx (ctx) + && (!is_taskloop_ctx (ctx) + || ctx->outer == NULL + || !is_task_ctx (ctx->outer))) + bad = "task"; + else + { + for (omp_context *octx = ctx->outer; + octx; octx = octx->outer) + { + switch (gimple_code (octx->stmt)) + { + case GIMPLE_OMP_TASKGROUP: + break; + case GIMPLE_OMP_TARGET: + if (gimple_omp_target_kind (octx->stmt) + != GF_OMP_TARGET_KIND_REGION) + continue; + /* FALLTHRU */ + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TEAMS: + error_at (gimple_location (stmt), + "%<%s taskgroup%> construct not closely " + "nested inside of %<taskgroup%> region", + construct); + return false; + case GIMPLE_OMP_TASK: + if (gimple_omp_task_taskloop_p (octx->stmt) + && octx->outer + && is_taskloop_ctx (octx->outer)) + { + tree clauses + = gimple_omp_for_clauses (octx->outer->stmt); + if (!omp_find_clause (clauses, OMP_CLAUSE_NOGROUP)) + break; + } + continue; + default: + continue; + } + break; + } + ctx->cancellable = true; + } + kind = "taskgroup"; + break; + default: + error_at (gimple_location (stmt), "invalid arguments"); + return false; + } + if (bad) + { + error_at (gimple_location (stmt), + "%<%s %s%> construct not closely nested inside of %qs", + construct, kind, bad); + return false; + } + } + /* FALLTHRU */ + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + for (; ctx != NULL; ctx = ctx->outer) + switch (gimple_code (ctx->stmt)) + { + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_FOR + && gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_TASKLOOP) + break; + /* FALLTHRU */ + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + case GIMPLE_OMP_TASK: + case GIMPLE_OMP_CRITICAL: + if (is_gimple_call (stmt)) + { + if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) + != BUILT_IN_GOMP_BARRIER) + return true; + error_at (gimple_location (stmt), + "barrier region may not be closely nested inside " + "of work-sharing, %<loop%>, %<critical%>, " + "%<ordered%>, %<master%>, %<masked%>, explicit " + "%<task%> or %<taskloop%> region"); + return false; + } + error_at (gimple_location (stmt), + "work-sharing region may not be closely nested inside " + "of work-sharing, %<loop%>, %<critical%>, %<ordered%>, " + "%<master%>, %<masked%>, explicit %<task%> or " + "%<taskloop%> region"); + return false; + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TEAMS: + return true; + case GIMPLE_OMP_TARGET: + if (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_REGION) + return true; + break; + default: + break; + } + break; + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + for (; ctx != NULL; ctx = ctx->outer) + switch (gimple_code (ctx->stmt)) + { + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_FOR + && gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_TASKLOOP) + break; + /* FALLTHRU */ + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_TASK: + error_at (gimple_location (stmt), + "%qs region may not be closely nested inside " + "of work-sharing, %<loop%>, explicit %<task%> or " + "%<taskloop%> region", + gimple_code (stmt) == GIMPLE_OMP_MASTER + ? "master" : "masked"); + return false; + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TEAMS: + return true; + case GIMPLE_OMP_TARGET: + if (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_REGION) + return true; + break; + default: + break; + } + break; + case GIMPLE_OMP_SCOPE: + for (; ctx != NULL; ctx = ctx->outer) + switch (gimple_code (ctx->stmt)) + { + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_FOR + && gimple_omp_for_kind (ctx->stmt) != GF_OMP_FOR_KIND_TASKLOOP) + break; + /* FALLTHRU */ + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_TASK: + case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + error_at (gimple_location (stmt), + "%<scope%> region may not be closely nested inside " + "of work-sharing, %<loop%>, explicit %<task%>, " + "%<taskloop%>, %<critical%>, %<ordered%>, %<master%>, " + "or %<masked%> region"); + return false; + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TEAMS: + return true; + case GIMPLE_OMP_TARGET: + if (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_REGION) + return true; + break; + default: + break; + } + break; + case GIMPLE_OMP_TASK: + for (c = gimple_omp_task_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND + && (OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SOURCE + || OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SINK)) + { + enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_KIND (c); + error_at (OMP_CLAUSE_LOCATION (c), + "%<depend(%s)%> is only allowed in %<omp ordered%>", + kind == OMP_CLAUSE_DEPEND_SOURCE ? "source" : "sink"); + return false; + } + break; + case GIMPLE_OMP_ORDERED: + for (c = gimple_omp_ordered_clauses (as_a <gomp_ordered *> (stmt)); + c; c = OMP_CLAUSE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND) + { + gcc_assert (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_THREADS + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SIMD); + continue; + } + enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_KIND (c); + if (kind == OMP_CLAUSE_DEPEND_SOURCE + || kind == OMP_CLAUSE_DEPEND_SINK) + { + tree oclause; + /* Look for containing ordered(N) loop. */ + if (ctx == NULL + || gimple_code (ctx->stmt) != GIMPLE_OMP_FOR + || (oclause + = omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_ORDERED)) == NULL_TREE) + { + error_at (OMP_CLAUSE_LOCATION (c), + "%<ordered%> construct with %<depend%> clause " + "must be closely nested inside an %<ordered%> " + "loop"); + return false; + } + else if (OMP_CLAUSE_ORDERED_EXPR (oclause) == NULL_TREE) + { + error_at (OMP_CLAUSE_LOCATION (c), + "%<ordered%> construct with %<depend%> clause " + "must be closely nested inside a loop with " + "%<ordered%> clause with a parameter"); + return false; + } + } + else + { + error_at (OMP_CLAUSE_LOCATION (c), + "invalid depend kind in omp %<ordered%> %<depend%>"); + return false; + } + } + c = gimple_omp_ordered_clauses (as_a <gomp_ordered *> (stmt)); + if (omp_find_clause (c, OMP_CLAUSE_SIMD)) + { + /* ordered simd must be closely nested inside of simd region, + and simd region must not encounter constructs other than + ordered simd, therefore ordered simd may be either orphaned, + or ctx->stmt must be simd. The latter case is handled already + earlier. */ + if (ctx != NULL) + { + error_at (gimple_location (stmt), + "%<ordered%> %<simd%> must be closely nested inside " + "%<simd%> region"); + return false; + } + } + for (; ctx != NULL; ctx = ctx->outer) + switch (gimple_code (ctx->stmt)) + { + case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_TASK: + case GIMPLE_OMP_ORDERED: + ordered_in_taskloop: + error_at (gimple_location (stmt), + "%<ordered%> region may not be closely nested inside " + "of %<critical%>, %<ordered%>, explicit %<task%> or " + "%<taskloop%> region"); + return false; + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_TASKLOOP) + goto ordered_in_taskloop; + tree o; + o = omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_ORDERED); + if (o == NULL) + { + error_at (gimple_location (stmt), + "%<ordered%> region must be closely nested inside " + "a loop region with an %<ordered%> clause"); + return false; + } + if (OMP_CLAUSE_ORDERED_EXPR (o) != NULL_TREE + && omp_find_clause (c, OMP_CLAUSE_DEPEND) == NULL_TREE) + { + error_at (gimple_location (stmt), + "%<ordered%> region without %<depend%> clause may " + "not be closely nested inside a loop region with " + "an %<ordered%> clause with a parameter"); + return false; + } + return true; + case GIMPLE_OMP_TARGET: + if (gimple_omp_target_kind (ctx->stmt) + != GF_OMP_TARGET_KIND_REGION) + break; + /* FALLTHRU */ + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TEAMS: + error_at (gimple_location (stmt), + "%<ordered%> region must be closely nested inside " + "a loop region with an %<ordered%> clause"); + return false; + default: + break; + } + break; + case GIMPLE_OMP_CRITICAL: + { + tree this_stmt_name + = gimple_omp_critical_name (as_a <gomp_critical *> (stmt)); + for (; ctx != NULL; ctx = ctx->outer) + if (gomp_critical *other_crit + = dyn_cast <gomp_critical *> (ctx->stmt)) + if (this_stmt_name == gimple_omp_critical_name (other_crit)) + { + error_at (gimple_location (stmt), + "%<critical%> region may not be nested inside " + "a %<critical%> region with the same name"); + return false; + } + } + break; + case GIMPLE_OMP_TEAMS: + if (ctx == NULL) + break; + else if (gimple_code (ctx->stmt) != GIMPLE_OMP_TARGET + || (gimple_omp_target_kind (ctx->stmt) + != GF_OMP_TARGET_KIND_REGION)) + { + /* Teams construct can appear either strictly nested inside of + target construct with no intervening stmts, or can be encountered + only by initial task (so must not appear inside any OpenMP + construct. */ + error_at (gimple_location (stmt), + "%<teams%> construct must be closely nested inside of " + "%<target%> construct or not nested in any OpenMP " + "construct"); + return false; + } + break; + case GIMPLE_OMP_TARGET: + for (c = gimple_omp_target_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND + && (OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SOURCE + || OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SINK)) + { + enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_KIND (c); + error_at (OMP_CLAUSE_LOCATION (c), + "%<depend(%s)%> is only allowed in %<omp ordered%>", + kind == OMP_CLAUSE_DEPEND_SOURCE ? "source" : "sink"); + return false; + } + if (is_gimple_omp_offloaded (stmt) + && oacc_get_fn_attrib (cfun->decl) != NULL) + { + error_at (gimple_location (stmt), + "OpenACC region inside of OpenACC routine, nested " + "parallelism not supported yet"); + return false; + } + for (; ctx != NULL; ctx = ctx->outer) + { + if (gimple_code (ctx->stmt) != GIMPLE_OMP_TARGET) + { + if (is_gimple_omp (stmt) + && is_gimple_omp_oacc (stmt) + && is_gimple_omp (ctx->stmt)) + { + error_at (gimple_location (stmt), + "OpenACC construct inside of non-OpenACC region"); + return false; + } + continue; + } + + const char *stmt_name, *ctx_stmt_name; + switch (gimple_omp_target_kind (stmt)) + { + case GF_OMP_TARGET_KIND_REGION: stmt_name = "target"; break; + case GF_OMP_TARGET_KIND_DATA: stmt_name = "target data"; break; + case GF_OMP_TARGET_KIND_UPDATE: stmt_name = "target update"; break; + case GF_OMP_TARGET_KIND_ENTER_DATA: + stmt_name = "target enter data"; break; + case GF_OMP_TARGET_KIND_EXIT_DATA: + stmt_name = "target exit data"; break; + case GF_OMP_TARGET_KIND_OACC_PARALLEL: stmt_name = "parallel"; break; + case GF_OMP_TARGET_KIND_OACC_KERNELS: stmt_name = "kernels"; break; + case GF_OMP_TARGET_KIND_OACC_SERIAL: stmt_name = "serial"; break; + case GF_OMP_TARGET_KIND_OACC_DATA: stmt_name = "data"; break; + case GF_OMP_TARGET_KIND_OACC_UPDATE: stmt_name = "update"; break; + case GF_OMP_TARGET_KIND_OACC_ENTER_DATA: + stmt_name = "enter data"; break; + case GF_OMP_TARGET_KIND_OACC_EXIT_DATA: + stmt_name = "exit data"; break; + case GF_OMP_TARGET_KIND_OACC_DECLARE: stmt_name = "declare"; break; + case GF_OMP_TARGET_KIND_OACC_HOST_DATA: stmt_name = "host_data"; + break; + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_PARALLELIZED: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_GANG_SINGLE: + case GF_OMP_TARGET_KIND_OACC_DATA_KERNELS: + /* OpenACC 'kernels' decomposed parts. */ + stmt_name = "kernels"; break; + default: gcc_unreachable (); + } + switch (gimple_omp_target_kind (ctx->stmt)) + { + case GF_OMP_TARGET_KIND_REGION: ctx_stmt_name = "target"; break; + case GF_OMP_TARGET_KIND_DATA: ctx_stmt_name = "target data"; break; + case GF_OMP_TARGET_KIND_OACC_PARALLEL: + ctx_stmt_name = "parallel"; break; + case GF_OMP_TARGET_KIND_OACC_KERNELS: + ctx_stmt_name = "kernels"; break; + case GF_OMP_TARGET_KIND_OACC_SERIAL: + ctx_stmt_name = "serial"; break; + case GF_OMP_TARGET_KIND_OACC_DATA: ctx_stmt_name = "data"; break; + case GF_OMP_TARGET_KIND_OACC_HOST_DATA: + ctx_stmt_name = "host_data"; break; + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_PARALLELIZED: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_GANG_SINGLE: + case GF_OMP_TARGET_KIND_OACC_DATA_KERNELS: + /* OpenACC 'kernels' decomposed parts. */ + ctx_stmt_name = "kernels"; break; + default: gcc_unreachable (); + } + + /* OpenACC/OpenMP mismatch? */ + if (is_gimple_omp_oacc (stmt) + != is_gimple_omp_oacc (ctx->stmt)) + { + error_at (gimple_location (stmt), + "%s %qs construct inside of %s %qs region", + (is_gimple_omp_oacc (stmt) + ? "OpenACC" : "OpenMP"), stmt_name, + (is_gimple_omp_oacc (ctx->stmt) + ? "OpenACC" : "OpenMP"), ctx_stmt_name); + return false; + } + if (is_gimple_omp_offloaded (ctx->stmt)) + { + /* No GIMPLE_OMP_TARGET inside offloaded OpenACC CTX. */ + if (is_gimple_omp_oacc (ctx->stmt)) + { + error_at (gimple_location (stmt), + "%qs construct inside of %qs region", + stmt_name, ctx_stmt_name); + return false; + } + else + { + warning_at (gimple_location (stmt), 0, + "%qs construct inside of %qs region", + stmt_name, ctx_stmt_name); + } + } + } + break; + default: + break; + } + return true; +} + + +/* Helper function scan_omp. + + Callback for walk_tree or operators in walk_gimple_stmt used to + scan for OMP directives in TP. */ + +static tree +scan_omp_1_op (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_stmt_info *wi = (struct walk_stmt_info *) data; + omp_context *ctx = (omp_context *) wi->info; + tree t = *tp; + + switch (TREE_CODE (t)) + { + case VAR_DECL: + case PARM_DECL: + case LABEL_DECL: + case RESULT_DECL: + if (ctx) + { + tree repl = remap_decl (t, &ctx->cb); + gcc_checking_assert (TREE_CODE (repl) != ERROR_MARK); + *tp = repl; + } + break; + + default: + if (ctx && TYPE_P (t)) + *tp = remap_type (t, &ctx->cb); + else if (!DECL_P (t)) + { + *walk_subtrees = 1; + if (ctx) + { + tree tem = remap_type (TREE_TYPE (t), &ctx->cb); + if (tem != TREE_TYPE (t)) + { + if (TREE_CODE (t) == INTEGER_CST) + *tp = wide_int_to_tree (tem, wi::to_wide (t)); + else + TREE_TYPE (t) = tem; + } + } + } + break; + } + + return NULL_TREE; +} + +/* Return true if FNDECL is a setjmp or a longjmp. */ + +static bool +setjmp_or_longjmp_p (const_tree fndecl) +{ + if (fndecl_built_in_p (fndecl, BUILT_IN_SETJMP) + || fndecl_built_in_p (fndecl, BUILT_IN_LONGJMP)) + return true; + + tree declname = DECL_NAME (fndecl); + if (!declname + || (DECL_CONTEXT (fndecl) != NULL_TREE + && TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL) + || !TREE_PUBLIC (fndecl)) + return false; + + const char *name = IDENTIFIER_POINTER (declname); + return !strcmp (name, "setjmp") || !strcmp (name, "longjmp"); +} + +/* Return true if FNDECL is an omp_* runtime API call. */ + +static bool +omp_runtime_api_call (const_tree fndecl) +{ + tree declname = DECL_NAME (fndecl); + if (!declname + || (DECL_CONTEXT (fndecl) != NULL_TREE + && TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL) + || !TREE_PUBLIC (fndecl)) + return false; + + const char *name = IDENTIFIER_POINTER (declname); + if (!startswith (name, "omp_")) + return false; + + static const char *omp_runtime_apis[] = + { + /* This array has 3 sections. First omp_* calls that don't + have any suffixes. */ + "aligned_alloc", + "aligned_calloc", + "alloc", + "calloc", + "free", + "realloc", + "target_alloc", + "target_associate_ptr", + "target_disassociate_ptr", + "target_free", + "target_is_present", + "target_memcpy", + "target_memcpy_rect", + NULL, + /* Now omp_* calls that are available as omp_* and omp_*_; however, the + DECL_NAME is always omp_* without tailing underscore. */ + "capture_affinity", + "destroy_allocator", + "destroy_lock", + "destroy_nest_lock", + "display_affinity", + "fulfill_event", + "get_active_level", + "get_affinity_format", + "get_cancellation", + "get_default_allocator", + "get_default_device", + "get_device_num", + "get_dynamic", + "get_initial_device", + "get_level", + "get_max_active_levels", + "get_max_task_priority", + "get_max_teams", + "get_max_threads", + "get_nested", + "get_num_devices", + "get_num_places", + "get_num_procs", + "get_num_teams", + "get_num_threads", + "get_partition_num_places", + "get_place_num", + "get_proc_bind", + "get_supported_active_levels", + "get_team_num", + "get_teams_thread_limit", + "get_thread_limit", + "get_thread_num", + "get_wtick", + "get_wtime", + "in_final", + "in_parallel", + "init_lock", + "init_nest_lock", + "is_initial_device", + "pause_resource", + "pause_resource_all", + "set_affinity_format", + "set_default_allocator", + "set_lock", + "set_nest_lock", + "test_lock", + "test_nest_lock", + "unset_lock", + "unset_nest_lock", + NULL, + /* And finally calls available as omp_*, omp_*_ and omp_*_8_; however, + as DECL_NAME only omp_* and omp_*_8 appear. */ + "display_env", + "get_ancestor_thread_num", + "init_allocator", + "get_partition_place_nums", + "get_place_num_procs", + "get_place_proc_ids", + "get_schedule", + "get_team_size", + "set_default_device", + "set_dynamic", + "set_max_active_levels", + "set_nested", + "set_num_teams", + "set_num_threads", + "set_schedule", + "set_teams_thread_limit" + }; + + int mode = 0; + for (unsigned i = 0; i < ARRAY_SIZE (omp_runtime_apis); i++) + { + if (omp_runtime_apis[i] == NULL) + { + mode++; + continue; + } + size_t len = strlen (omp_runtime_apis[i]); + if (strncmp (name + 4, omp_runtime_apis[i], len) == 0 + && (name[4 + len] == '\0' + || (mode > 1 && strcmp (name + 4 + len, "_8") == 0))) + return true; + } + return false; +} + +/* Helper function for scan_omp. + + Callback for walk_gimple_stmt used to scan for OMP directives in + the current statement in GSI. */ + +static tree +scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + gimple *stmt = gsi_stmt (*gsi); + omp_context *ctx = (omp_context *) wi->info; + + if (gimple_has_location (stmt)) + input_location = gimple_location (stmt); + + /* Check the nesting restrictions. */ + bool remove = false; + if (is_gimple_omp (stmt)) + remove = !check_omp_nesting_restrictions (stmt, ctx); + else if (is_gimple_call (stmt)) + { + tree fndecl = gimple_call_fndecl (stmt); + if (fndecl) + { + if (ctx + && gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD + && setjmp_or_longjmp_p (fndecl) + && !ctx->loop_p) + { + remove = true; + error_at (gimple_location (stmt), + "setjmp/longjmp inside %<simd%> construct"); + } + else if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_GOMP_BARRIER: + case BUILT_IN_GOMP_CANCEL: + case BUILT_IN_GOMP_CANCELLATION_POINT: + case BUILT_IN_GOMP_TASKYIELD: + case BUILT_IN_GOMP_TASKWAIT: + case BUILT_IN_GOMP_TASKGROUP_START: + case BUILT_IN_GOMP_TASKGROUP_END: + remove = !check_omp_nesting_restrictions (stmt, ctx); + break; + default: + break; + } + else if (ctx) + { + omp_context *octx = ctx; + if (gimple_code (ctx->stmt) == GIMPLE_OMP_SCAN && ctx->outer) + octx = ctx->outer; + if (octx->order_concurrent && omp_runtime_api_call (fndecl)) + { + remove = true; + error_at (gimple_location (stmt), + "OpenMP runtime API call %qD in a region with " + "%<order(concurrent)%> clause", fndecl); + } + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && omp_runtime_api_call (fndecl) + && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) + != strlen ("omp_get_num_teams")) + || strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), + "omp_get_num_teams") != 0) + && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) + != strlen ("omp_get_team_num")) + || strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), + "omp_get_team_num") != 0)) + { + remove = true; + error_at (gimple_location (stmt), + "OpenMP runtime API call %qD strictly nested in a " + "%<teams%> region", fndecl); + } + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TARGET + && (gimple_omp_target_kind (ctx->stmt) + == GF_OMP_TARGET_KIND_REGION) + && omp_runtime_api_call (fndecl)) + { + tree tgt_clauses = gimple_omp_target_clauses (ctx->stmt); + tree c = omp_find_clause (tgt_clauses, OMP_CLAUSE_DEVICE); + if (c && OMP_CLAUSE_DEVICE_ANCESTOR (c)) + error_at (gimple_location (stmt), + "OpenMP runtime API call %qD in a region with " + "%<device(ancestor)%> clause", fndecl); + } + } + } + } + if (remove) + { + stmt = gimple_build_nop (); + gsi_replace (gsi, stmt, false); + } + + *handled_ops_p = true; + + switch (gimple_code (stmt)) + { + case GIMPLE_OMP_PARALLEL: + taskreg_nesting_level++; + scan_omp_parallel (gsi, ctx); + taskreg_nesting_level--; + break; + + case GIMPLE_OMP_TASK: + taskreg_nesting_level++; + scan_omp_task (gsi, ctx); + taskreg_nesting_level--; + break; + + case GIMPLE_OMP_FOR: + if ((gimple_omp_for_kind (as_a <gomp_for *> (stmt)) + == GF_OMP_FOR_KIND_SIMD) + && gimple_omp_for_combined_into_p (stmt) + && gimple_code (ctx->stmt) != GIMPLE_OMP_SCAN) + { + tree clauses = gimple_omp_for_clauses (as_a <gomp_for *> (stmt)); + tree c = omp_find_clause (clauses, OMP_CLAUSE_REDUCTION); + if (c && OMP_CLAUSE_REDUCTION_INSCAN (c) && !seen_error ()) + { + scan_omp_simd_scan (gsi, as_a <gomp_for *> (stmt), ctx); + break; + } + } + if ((gimple_omp_for_kind (as_a <gomp_for *> (stmt)) + == GF_OMP_FOR_KIND_SIMD) + && omp_maybe_offloaded_ctx (ctx) + && omp_max_simt_vf () + && gimple_omp_for_collapse (stmt) == 1) + scan_omp_simd (gsi, as_a <gomp_for *> (stmt), ctx); + else + scan_omp_for (as_a <gomp_for *> (stmt), ctx); + break; + + case GIMPLE_OMP_SCOPE: + ctx = new_omp_context (stmt, ctx); + scan_sharing_clauses (gimple_omp_scope_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + break; + + case GIMPLE_OMP_SECTIONS: + scan_omp_sections (as_a <gomp_sections *> (stmt), ctx); + break; + + case GIMPLE_OMP_SINGLE: + scan_omp_single (as_a <gomp_single *> (stmt), ctx); + break; + + case GIMPLE_OMP_SCAN: + if (tree clauses = gimple_omp_scan_clauses (as_a <gomp_scan *> (stmt))) + { + if (OMP_CLAUSE_CODE (clauses) == OMP_CLAUSE_INCLUSIVE) + ctx->scan_inclusive = true; + else if (OMP_CLAUSE_CODE (clauses) == OMP_CLAUSE_EXCLUSIVE) + ctx->scan_exclusive = true; + } + /* FALLTHRU */ + case GIMPLE_OMP_SECTION: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_CRITICAL: + ctx = new_omp_context (stmt, ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + break; + + case GIMPLE_OMP_MASKED: + ctx = new_omp_context (stmt, ctx); + scan_sharing_clauses (gimple_omp_masked_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + break; + + case GIMPLE_OMP_TASKGROUP: + ctx = new_omp_context (stmt, ctx); + scan_sharing_clauses (gimple_omp_taskgroup_clauses (stmt), ctx); + scan_omp (gimple_omp_body_ptr (stmt), ctx); + break; + + case GIMPLE_OMP_TARGET: + if (is_gimple_omp_offloaded (stmt)) + { + taskreg_nesting_level++; + scan_omp_target (as_a <gomp_target *> (stmt), ctx); + taskreg_nesting_level--; + } + else + scan_omp_target (as_a <gomp_target *> (stmt), ctx); + break; + + case GIMPLE_OMP_TEAMS: + if (gimple_omp_teams_host (as_a <gomp_teams *> (stmt))) + { + taskreg_nesting_level++; + scan_omp_teams (as_a <gomp_teams *> (stmt), ctx); + taskreg_nesting_level--; + } + else + scan_omp_teams (as_a <gomp_teams *> (stmt), ctx); + break; + + case GIMPLE_BIND: + { + tree var; + + *handled_ops_p = false; + if (ctx) + for (var = gimple_bind_vars (as_a <gbind *> (stmt)); + var ; + var = DECL_CHAIN (var)) + insert_decl_map (&ctx->cb, var, var); + } + break; + default: + *handled_ops_p = false; + break; + } + + return NULL_TREE; +} + + +/* Scan all the statements starting at the current statement. CTX + contains context information about the OMP directives and + clauses found during the scan. */ + +static void +scan_omp (gimple_seq *body_p, omp_context *ctx) +{ + location_t saved_location; + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.info = ctx; + wi.want_locations = true; + + saved_location = input_location; + walk_gimple_seq_mod (body_p, scan_omp_1_stmt, scan_omp_1_op, &wi); + input_location = saved_location; +} + +/* Re-gimplification and code generation routines. */ + +/* Remove omp_member_access_dummy_var variables from gimple_bind_vars + of BIND if in a method. */ + +static void +maybe_remove_omp_member_access_dummy_vars (gbind *bind) +{ + if (DECL_ARGUMENTS (current_function_decl) + && DECL_ARTIFICIAL (DECL_ARGUMENTS (current_function_decl)) + && (TREE_CODE (TREE_TYPE (DECL_ARGUMENTS (current_function_decl))) + == POINTER_TYPE)) + { + tree vars = gimple_bind_vars (bind); + for (tree *pvar = &vars; *pvar; ) + if (omp_member_access_dummy_var (*pvar)) + *pvar = DECL_CHAIN (*pvar); + else + pvar = &DECL_CHAIN (*pvar); + gimple_bind_set_vars (bind, vars); + } +} + +/* Remove omp_member_access_dummy_var variables from BLOCK_VARS of + block and its subblocks. */ + +static void +remove_member_access_dummy_vars (tree block) +{ + for (tree *pvar = &BLOCK_VARS (block); *pvar; ) + if (omp_member_access_dummy_var (*pvar)) + *pvar = DECL_CHAIN (*pvar); + else + pvar = &DECL_CHAIN (*pvar); + + for (block = BLOCK_SUBBLOCKS (block); block; block = BLOCK_CHAIN (block)) + remove_member_access_dummy_vars (block); +} + +/* If a context was created for STMT when it was scanned, return it. */ + +static omp_context * +maybe_lookup_ctx (gimple *stmt) +{ + splay_tree_node n; + n = splay_tree_lookup (all_contexts, (splay_tree_key) stmt); + return n ? (omp_context *) n->value : NULL; +} + + +/* Find the mapping for DECL in CTX or the immediately enclosing + context that has a mapping for DECL. + + If CTX is a nested parallel directive, we may have to use the decl + mappings created in CTX's parent context. Suppose that we have the + following parallel nesting (variable UIDs showed for clarity): + + iD.1562 = 0; + #omp parallel shared(iD.1562) -> outer parallel + iD.1562 = iD.1562 + 1; + + #omp parallel shared (iD.1562) -> inner parallel + iD.1562 = iD.1562 - 1; + + Each parallel structure will create a distinct .omp_data_s structure + for copying iD.1562 in/out of the directive: + + outer parallel .omp_data_s.1.i -> iD.1562 + inner parallel .omp_data_s.2.i -> iD.1562 + + A shared variable mapping will produce a copy-out operation before + the parallel directive and a copy-in operation after it. So, in + this case we would have: + + iD.1562 = 0; + .omp_data_o.1.i = iD.1562; + #omp parallel shared(iD.1562) -> outer parallel + .omp_data_i.1 = &.omp_data_o.1 + .omp_data_i.1->i = .omp_data_i.1->i + 1; + + .omp_data_o.2.i = iD.1562; -> ** + #omp parallel shared(iD.1562) -> inner parallel + .omp_data_i.2 = &.omp_data_o.2 + .omp_data_i.2->i = .omp_data_i.2->i - 1; + + + ** This is a problem. The symbol iD.1562 cannot be referenced + inside the body of the outer parallel region. But since we are + emitting this copy operation while expanding the inner parallel + directive, we need to access the CTX structure of the outer + parallel directive to get the correct mapping: + + .omp_data_o.2.i = .omp_data_i.1->i + + Since there may be other workshare or parallel directives enclosing + the parallel directive, it may be necessary to walk up the context + parent chain. This is not a problem in general because nested + parallelism happens only rarely. */ + +static tree +lookup_decl_in_outer_ctx (tree decl, omp_context *ctx) +{ + tree t; + omp_context *up; + + for (up = ctx->outer, t = NULL; up && t == NULL; up = up->outer) + t = maybe_lookup_decl (decl, up); + + gcc_assert (!ctx->is_nested || t || is_global_var (decl)); + + return t ? t : decl; +} + + +/* Similar to lookup_decl_in_outer_ctx, but return DECL if not found + in outer contexts. */ + +static tree +maybe_lookup_decl_in_outer_ctx (tree decl, omp_context *ctx) +{ + tree t = NULL; + omp_context *up; + + for (up = ctx->outer, t = NULL; up && t == NULL; up = up->outer) + t = maybe_lookup_decl (decl, up); + + return t ? t : decl; +} + + +/* Construct the initialization value for reduction operation OP. */ + +tree +omp_reduction_init_op (location_t loc, enum tree_code op, tree type) +{ + switch (op) + { + case PLUS_EXPR: + case MINUS_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_XOR_EXPR: + case NE_EXPR: + return build_zero_cst (type); + + case MULT_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_ANDIF_EXPR: + case EQ_EXPR: + return fold_convert_loc (loc, type, integer_one_node); + + case BIT_AND_EXPR: + return fold_convert_loc (loc, type, integer_minus_one_node); + + case MAX_EXPR: + if (SCALAR_FLOAT_TYPE_P (type)) + { + REAL_VALUE_TYPE max, min; + if (HONOR_INFINITIES (type)) + { + real_inf (&max); + real_arithmetic (&min, NEGATE_EXPR, &max, NULL); + } + else + real_maxval (&min, 1, TYPE_MODE (type)); + return build_real (type, min); + } + else if (POINTER_TYPE_P (type)) + { + wide_int min + = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + return wide_int_to_tree (type, min); + } + else + { + gcc_assert (INTEGRAL_TYPE_P (type)); + return TYPE_MIN_VALUE (type); + } + + case MIN_EXPR: + if (SCALAR_FLOAT_TYPE_P (type)) + { + REAL_VALUE_TYPE max; + if (HONOR_INFINITIES (type)) + real_inf (&max); + else + real_maxval (&max, 0, TYPE_MODE (type)); + return build_real (type, max); + } + else if (POINTER_TYPE_P (type)) + { + wide_int max + = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + return wide_int_to_tree (type, max); + } + else + { + gcc_assert (INTEGRAL_TYPE_P (type)); + return TYPE_MAX_VALUE (type); + } + + default: + gcc_unreachable (); + } +} + +/* Construct the initialization value for reduction CLAUSE. */ + +tree +omp_reduction_init (tree clause, tree type) +{ + return omp_reduction_init_op (OMP_CLAUSE_LOCATION (clause), + OMP_CLAUSE_REDUCTION_CODE (clause), type); +} + +/* Return alignment to be assumed for var in CLAUSE, which should be + OMP_CLAUSE_ALIGNED. */ + +static tree +omp_clause_aligned_alignment (tree clause) +{ + if (OMP_CLAUSE_ALIGNED_ALIGNMENT (clause)) + return OMP_CLAUSE_ALIGNED_ALIGNMENT (clause); + + /* Otherwise return implementation defined alignment. */ + unsigned int al = 1; + opt_scalar_mode mode_iter; + auto_vector_modes modes; + targetm.vectorize.autovectorize_vector_modes (&modes, true); + static enum mode_class classes[] + = { MODE_INT, MODE_VECTOR_INT, MODE_FLOAT, MODE_VECTOR_FLOAT }; + for (int i = 0; i < 4; i += 2) + /* The for loop above dictates that we only walk through scalar classes. */ + FOR_EACH_MODE_IN_CLASS (mode_iter, classes[i]) + { + scalar_mode mode = mode_iter.require (); + machine_mode vmode = targetm.vectorize.preferred_simd_mode (mode); + if (GET_MODE_CLASS (vmode) != classes[i + 1]) + continue; + machine_mode alt_vmode; + for (unsigned int j = 0; j < modes.length (); ++j) + if (related_vector_mode (modes[j], mode).exists (&alt_vmode) + && known_ge (GET_MODE_SIZE (alt_vmode), GET_MODE_SIZE (vmode))) + vmode = alt_vmode; + + tree type = lang_hooks.types.type_for_mode (mode, 1); + if (type == NULL_TREE || TYPE_MODE (type) != mode) + continue; + type = build_vector_type_for_mode (type, vmode); + if (TYPE_MODE (type) != vmode) + continue; + if (TYPE_ALIGN_UNIT (type) > al) + al = TYPE_ALIGN_UNIT (type); + } + return build_int_cst (integer_type_node, al); +} + + +/* This structure is part of the interface between lower_rec_simd_input_clauses + and lower_rec_input_clauses. */ + +class omplow_simd_context { +public: + omplow_simd_context () { memset (this, 0, sizeof (*this)); } + tree idx; + tree lane; + tree lastlane; + vec<tree, va_heap> simt_eargs; + gimple_seq simt_dlist; + poly_uint64_pod max_vf; + bool is_simt; +}; + +/* Helper function of lower_rec_input_clauses, used for #pragma omp simd + privatization. */ + +static bool +lower_rec_simd_input_clauses (tree new_var, omp_context *ctx, + omplow_simd_context *sctx, tree &ivar, + tree &lvar, tree *rvar = NULL, + tree *rvar2 = NULL) +{ + if (known_eq (sctx->max_vf, 0U)) + { + sctx->max_vf = sctx->is_simt ? omp_max_simt_vf () : omp_max_vf (); + if (maybe_gt (sctx->max_vf, 1U)) + { + tree c = omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_SAFELEN); + if (c) + { + poly_uint64 safe_len; + if (!poly_int_tree_p (OMP_CLAUSE_SAFELEN_EXPR (c), &safe_len) + || maybe_lt (safe_len, 1U)) + sctx->max_vf = 1; + else + sctx->max_vf = lower_bound (sctx->max_vf, safe_len); + } + } + if (sctx->is_simt && !known_eq (sctx->max_vf, 1U)) + { + for (tree c = gimple_omp_for_clauses (ctx->stmt); c; + c = OMP_CLAUSE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + continue; + + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + /* UDR reductions are not supported yet for SIMT, disable + SIMT. */ + sctx->max_vf = 1; + break; + } + + if (truth_value_p (OMP_CLAUSE_REDUCTION_CODE (c)) + && !INTEGRAL_TYPE_P (TREE_TYPE (new_var))) + { + /* Doing boolean operations on non-integral types is + for conformance only, it's not worth supporting this + for SIMT. */ + sctx->max_vf = 1; + break; + } + } + } + if (maybe_gt (sctx->max_vf, 1U)) + { + sctx->idx = create_tmp_var (unsigned_type_node); + sctx->lane = create_tmp_var (unsigned_type_node); + } + } + if (known_eq (sctx->max_vf, 1U)) + return false; + + if (sctx->is_simt) + { + if (is_gimple_reg (new_var)) + { + ivar = lvar = new_var; + return true; + } + tree type = TREE_TYPE (new_var), ptype = build_pointer_type (type); + ivar = lvar = create_tmp_var (type); + TREE_ADDRESSABLE (ivar) = 1; + DECL_ATTRIBUTES (ivar) = tree_cons (get_identifier ("omp simt private"), + NULL, DECL_ATTRIBUTES (ivar)); + sctx->simt_eargs.safe_push (build1 (ADDR_EXPR, ptype, ivar)); + tree clobber = build_clobber (type); + gimple *g = gimple_build_assign (ivar, clobber); + gimple_seq_add_stmt (&sctx->simt_dlist, g); + } + else + { + tree atype = build_array_type_nelts (TREE_TYPE (new_var), sctx->max_vf); + tree avar = create_tmp_var_raw (atype); + if (TREE_ADDRESSABLE (new_var)) + TREE_ADDRESSABLE (avar) = 1; + DECL_ATTRIBUTES (avar) + = tree_cons (get_identifier ("omp simd array"), NULL, + DECL_ATTRIBUTES (avar)); + gimple_add_tmp_var (avar); + tree iavar = avar; + if (rvar && !ctx->for_simd_scan_phase) + { + /* For inscan reductions, create another array temporary, + which will hold the reduced value. */ + iavar = create_tmp_var_raw (atype); + if (TREE_ADDRESSABLE (new_var)) + TREE_ADDRESSABLE (iavar) = 1; + DECL_ATTRIBUTES (iavar) + = tree_cons (get_identifier ("omp simd array"), NULL, + tree_cons (get_identifier ("omp simd inscan"), NULL, + DECL_ATTRIBUTES (iavar))); + gimple_add_tmp_var (iavar); + ctx->cb.decl_map->put (avar, iavar); + if (sctx->lastlane == NULL_TREE) + sctx->lastlane = create_tmp_var (unsigned_type_node); + *rvar = build4 (ARRAY_REF, TREE_TYPE (new_var), iavar, + sctx->lastlane, NULL_TREE, NULL_TREE); + TREE_THIS_NOTRAP (*rvar) = 1; + + if (ctx->scan_exclusive) + { + /* And for exclusive scan yet another one, which will + hold the value during the scan phase. */ + tree savar = create_tmp_var_raw (atype); + if (TREE_ADDRESSABLE (new_var)) + TREE_ADDRESSABLE (savar) = 1; + DECL_ATTRIBUTES (savar) + = tree_cons (get_identifier ("omp simd array"), NULL, + tree_cons (get_identifier ("omp simd inscan " + "exclusive"), NULL, + DECL_ATTRIBUTES (savar))); + gimple_add_tmp_var (savar); + ctx->cb.decl_map->put (iavar, savar); + *rvar2 = build4 (ARRAY_REF, TREE_TYPE (new_var), savar, + sctx->idx, NULL_TREE, NULL_TREE); + TREE_THIS_NOTRAP (*rvar2) = 1; + } + } + ivar = build4 (ARRAY_REF, TREE_TYPE (new_var), iavar, sctx->idx, + NULL_TREE, NULL_TREE); + lvar = build4 (ARRAY_REF, TREE_TYPE (new_var), avar, sctx->lane, + NULL_TREE, NULL_TREE); + TREE_THIS_NOTRAP (ivar) = 1; + TREE_THIS_NOTRAP (lvar) = 1; + } + if (DECL_P (new_var)) + { + SET_DECL_VALUE_EXPR (new_var, lvar); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + return true; +} + +/* Helper function of lower_rec_input_clauses. For a reference + in simd reduction, add an underlying variable it will reference. */ + +static void +handle_simd_reference (location_t loc, tree new_vard, gimple_seq *ilist) +{ + tree z = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_vard))); + if (TREE_CONSTANT (z)) + { + z = create_tmp_var_raw (TREE_TYPE (TREE_TYPE (new_vard)), + get_name (new_vard)); + gimple_add_tmp_var (z); + TREE_ADDRESSABLE (z) = 1; + z = build_fold_addr_expr_loc (loc, z); + gimplify_assign (new_vard, z, ilist); + } +} + +/* Helper function for lower_rec_input_clauses. Emit into ilist sequence + code to emit (type) (tskred_temp[idx]). */ + +static tree +task_reduction_read (gimple_seq *ilist, tree tskred_temp, tree type, + unsigned idx) +{ + unsigned HOST_WIDE_INT sz + = tree_to_uhwi (TYPE_SIZE_UNIT (pointer_sized_int_node)); + tree r = build2 (MEM_REF, pointer_sized_int_node, + tskred_temp, build_int_cst (TREE_TYPE (tskred_temp), + idx * sz)); + tree v = create_tmp_var (pointer_sized_int_node); + gimple *g = gimple_build_assign (v, r); + gimple_seq_add_stmt (ilist, g); + if (!useless_type_conversion_p (type, pointer_sized_int_node)) + { + v = create_tmp_var (type); + g = gimple_build_assign (v, NOP_EXPR, gimple_assign_lhs (g)); + gimple_seq_add_stmt (ilist, g); + } + return v; +} + +/* Lower early initialization of privatized variable NEW_VAR + if it needs an allocator (has allocate clause). */ + +static bool +lower_private_allocate (tree var, tree new_var, tree &allocator, + tree &allocate_ptr, gimple_seq *ilist, + omp_context *ctx, bool is_ref, tree size) +{ + if (allocator) + return false; + gcc_assert (allocate_ptr == NULL_TREE); + if (ctx->allocate_map + && (DECL_P (new_var) || (TYPE_P (new_var) && size))) + if (tree *allocatorp = ctx->allocate_map->get (var)) + allocator = *allocatorp; + if (allocator == NULL_TREE) + return false; + if (!is_ref && omp_privatize_by_reference (var)) + { + allocator = NULL_TREE; + return false; + } + + unsigned HOST_WIDE_INT ialign = 0; + if (TREE_CODE (allocator) == TREE_LIST) + { + ialign = tree_to_uhwi (TREE_VALUE (allocator)); + allocator = TREE_PURPOSE (allocator); + } + if (TREE_CODE (allocator) != INTEGER_CST) + allocator = build_outer_var_ref (allocator, ctx); + allocator = fold_convert (pointer_sized_int_node, allocator); + if (TREE_CODE (allocator) != INTEGER_CST) + { + tree var = create_tmp_var (TREE_TYPE (allocator)); + gimplify_assign (var, allocator, ilist); + allocator = var; + } + + tree ptr_type, align, sz = size; + if (TYPE_P (new_var)) + { + ptr_type = build_pointer_type (new_var); + ialign = MAX (ialign, TYPE_ALIGN_UNIT (new_var)); + } + else if (is_ref) + { + ptr_type = build_pointer_type (TREE_TYPE (TREE_TYPE (new_var))); + ialign = MAX (ialign, TYPE_ALIGN_UNIT (TREE_TYPE (ptr_type))); + } + else + { + ptr_type = build_pointer_type (TREE_TYPE (new_var)); + ialign = MAX (ialign, DECL_ALIGN_UNIT (new_var)); + if (sz == NULL_TREE) + sz = fold_convert (size_type_node, DECL_SIZE_UNIT (new_var)); + } + align = build_int_cst (size_type_node, ialign); + if (TREE_CODE (sz) != INTEGER_CST) + { + tree szvar = create_tmp_var (size_type_node); + gimplify_assign (szvar, sz, ilist); + sz = szvar; + } + allocate_ptr = create_tmp_var (ptr_type); + tree a = builtin_decl_explicit (BUILT_IN_GOMP_ALLOC); + gimple *g = gimple_build_call (a, 3, align, sz, allocator); + gimple_call_set_lhs (g, allocate_ptr); + gimple_seq_add_stmt (ilist, g); + if (!is_ref) + { + tree x = build_simple_mem_ref (allocate_ptr); + TREE_THIS_NOTRAP (x) = 1; + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + return true; +} + +/* Generate code to implement the input clauses, FIRSTPRIVATE and COPYIN, + from the receiver (aka child) side and initializers for REFERENCE_TYPE + private variables. Initialization statements go in ILIST, while calls + to destructors go in DLIST. */ + +static void +lower_rec_input_clauses (tree clauses, gimple_seq *ilist, gimple_seq *dlist, + omp_context *ctx, struct omp_for_data *fd) +{ + tree c, copyin_seq, x, ptr; + bool copyin_by_ref = false; + bool lastprivate_firstprivate = false; + bool reduction_omp_orig_ref = false; + int pass; + bool is_simd = (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD); + omplow_simd_context sctx = omplow_simd_context (); + tree simt_lane = NULL_TREE, simtrec = NULL_TREE; + tree ivar = NULL_TREE, lvar = NULL_TREE, uid = NULL_TREE; + gimple_seq llist[4] = { }; + tree nonconst_simd_if = NULL_TREE; + + copyin_seq = NULL; + sctx.is_simt = is_simd && omp_find_clause (clauses, OMP_CLAUSE__SIMT_); + + /* Set max_vf=1 (which will later enforce safelen=1) in simd loops + with data sharing clauses referencing variable sized vars. That + is unnecessarily hard to support and very unlikely to result in + vectorized code anyway. */ + if (is_simd) + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_LINEAR: + if (OMP_CLAUSE_LINEAR_ARRAY (c)) + sctx.max_vf = 1; + /* FALLTHRU */ + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + if (is_variable_sized (OMP_CLAUSE_DECL (c))) + sctx.max_vf = 1; + else if (omp_privatize_by_reference (OMP_CLAUSE_DECL (c))) + { + tree rtype = TREE_TYPE (TREE_TYPE (OMP_CLAUSE_DECL (c))); + if (!TREE_CONSTANT (TYPE_SIZE_UNIT (rtype))) + sctx.max_vf = 1; + } + break; + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + if (TREE_CODE (OMP_CLAUSE_DECL (c)) == MEM_REF + || is_variable_sized (OMP_CLAUSE_DECL (c))) + sctx.max_vf = 1; + else if (omp_privatize_by_reference (OMP_CLAUSE_DECL (c))) + { + tree rtype = TREE_TYPE (TREE_TYPE (OMP_CLAUSE_DECL (c))); + if (!TREE_CONSTANT (TYPE_SIZE_UNIT (rtype))) + sctx.max_vf = 1; + } + break; + case OMP_CLAUSE_IF: + if (integer_zerop (OMP_CLAUSE_IF_EXPR (c))) + sctx.max_vf = 1; + else if (TREE_CODE (OMP_CLAUSE_IF_EXPR (c)) != INTEGER_CST) + nonconst_simd_if = OMP_CLAUSE_IF_EXPR (c); + break; + case OMP_CLAUSE_SIMDLEN: + if (integer_onep (OMP_CLAUSE_SIMDLEN_EXPR (c))) + sctx.max_vf = 1; + break; + case OMP_CLAUSE__CONDTEMP_: + /* FIXME: lastprivate(conditional:) not handled for SIMT yet. */ + if (sctx.is_simt) + sctx.max_vf = 1; + break; + default: + continue; + } + + /* Add a placeholder for simduid. */ + if (sctx.is_simt && maybe_ne (sctx.max_vf, 1U)) + sctx.simt_eargs.safe_push (NULL_TREE); + + unsigned task_reduction_cnt = 0; + unsigned task_reduction_cntorig = 0; + unsigned task_reduction_cnt_full = 0; + unsigned task_reduction_cntorig_full = 0; + unsigned task_reduction_other_cnt = 0; + tree tskred_atype = NULL_TREE, tskred_avar = NULL_TREE; + tree tskred_base = NULL_TREE, tskred_temp = NULL_TREE; + /* Do all the fixed sized types in the first pass, and the variable sized + types in the second pass. This makes sure that the scalar arguments to + the variable sized types are processed before we use them in the + variable sized operations. For task reductions we use 4 passes, in the + first two we ignore them, in the third one gather arguments for + GOMP_task_reduction_remap call and in the last pass actually handle + the task reductions. */ + for (pass = 0; pass < ((task_reduction_cnt || task_reduction_other_cnt) + ? 4 : 2); ++pass) + { + if (pass == 2 && task_reduction_cnt) + { + tskred_atype + = build_array_type_nelts (ptr_type_node, task_reduction_cnt + + task_reduction_cntorig); + tskred_avar = create_tmp_var_raw (tskred_atype); + gimple_add_tmp_var (tskred_avar); + TREE_ADDRESSABLE (tskred_avar) = 1; + task_reduction_cnt_full = task_reduction_cnt; + task_reduction_cntorig_full = task_reduction_cntorig; + } + else if (pass == 3 && task_reduction_cnt) + { + x = builtin_decl_explicit (BUILT_IN_GOMP_TASK_REDUCTION_REMAP); + gimple *g + = gimple_build_call (x, 3, size_int (task_reduction_cnt), + size_int (task_reduction_cntorig), + build_fold_addr_expr (tskred_avar)); + gimple_seq_add_stmt (ilist, g); + } + if (pass == 3 && task_reduction_other_cnt) + { + /* For reduction clauses, build + tskred_base = (void *) tskred_temp[2] + + omp_get_thread_num () * tskred_temp[1] + or if tskred_temp[1] is known to be constant, that constant + directly. This is the start of the private reduction copy block + for the current thread. */ + tree v = create_tmp_var (integer_type_node); + x = builtin_decl_explicit (BUILT_IN_OMP_GET_THREAD_NUM); + gimple *g = gimple_build_call (x, 0); + gimple_call_set_lhs (g, v); + gimple_seq_add_stmt (ilist, g); + c = omp_find_clause (clauses, OMP_CLAUSE__REDUCTEMP_); + tskred_temp = OMP_CLAUSE_DECL (c); + if (is_taskreg_ctx (ctx)) + tskred_temp = lookup_decl (tskred_temp, ctx); + tree v2 = create_tmp_var (sizetype); + g = gimple_build_assign (v2, NOP_EXPR, v); + gimple_seq_add_stmt (ilist, g); + if (ctx->task_reductions[0]) + v = fold_convert (sizetype, ctx->task_reductions[0]); + else + v = task_reduction_read (ilist, tskred_temp, sizetype, 1); + tree v3 = create_tmp_var (sizetype); + g = gimple_build_assign (v3, MULT_EXPR, v2, v); + gimple_seq_add_stmt (ilist, g); + v = task_reduction_read (ilist, tskred_temp, ptr_type_node, 2); + tskred_base = create_tmp_var (ptr_type_node); + g = gimple_build_assign (tskred_base, POINTER_PLUS_EXPR, v, v3); + gimple_seq_add_stmt (ilist, g); + } + task_reduction_cnt = 0; + task_reduction_cntorig = 0; + task_reduction_other_cnt = 0; + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + enum omp_clause_code c_kind = OMP_CLAUSE_CODE (c); + tree var, new_var; + bool by_ref; + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + bool task_reduction_p = false; + bool task_reduction_needs_orig_p = false; + tree cond = NULL_TREE; + tree allocator, allocate_ptr; + + switch (c_kind) + { + case OMP_CLAUSE_PRIVATE: + if (OMP_CLAUSE_PRIVATE_DEBUG (c)) + continue; + break; + case OMP_CLAUSE_SHARED: + /* Ignore shared directives in teams construct inside + of target construct. */ + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && !is_host_teams_ctx (ctx)) + continue; + if (maybe_lookup_decl (OMP_CLAUSE_DECL (c), ctx) == NULL) + { + gcc_assert (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c) + || is_global_var (OMP_CLAUSE_DECL (c))); + continue; + } + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_COPYIN: + break; + case OMP_CLAUSE_LINEAR: + if (!OMP_CLAUSE_LINEAR_NO_COPYIN (c) + && !OMP_CLAUSE_LINEAR_NO_COPYOUT (c)) + lastprivate_firstprivate = true; + break; + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION + || is_task_ctx (ctx) + || OMP_CLAUSE_REDUCTION_TASK (c)) + { + task_reduction_p = true; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) + { + task_reduction_other_cnt++; + if (pass == 2) + continue; + } + else + task_reduction_cnt++; + if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + { + var = OMP_CLAUSE_DECL (c); + /* If var is a global variable that isn't privatized + in outer contexts, we don't need to look up the + original address, it is always the address of the + global variable itself. */ + if (!DECL_P (var) + || omp_privatize_by_reference (var) + || !is_global_var + (maybe_lookup_decl_in_outer_ctx (var, ctx))) + { + task_reduction_needs_orig_p = true; + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + task_reduction_cntorig++; + } + } + } + else if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + reduction_omp_orig_ref = true; + break; + case OMP_CLAUSE__REDUCTEMP_: + if (!is_taskreg_ctx (ctx)) + continue; + /* FALLTHRU */ + case OMP_CLAUSE__LOOPTEMP_: + /* Handle _looptemp_/_reductemp_ clauses only on + parallel/task. */ + if (fd) + continue; + break; + case OMP_CLAUSE_LASTPRIVATE: + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + { + lastprivate_firstprivate = true; + if (pass != 0 || is_taskloop_ctx (ctx)) + continue; + } + /* Even without corresponding firstprivate, if + decl is Fortran allocatable, it needs outer var + reference. */ + else if (pass == 0 + && lang_hooks.decls.omp_private_outer_ref + (OMP_CLAUSE_DECL (c))) + lastprivate_firstprivate = true; + break; + case OMP_CLAUSE_ALIGNED: + if (pass != 1) + continue; + var = OMP_CLAUSE_DECL (c); + if (TREE_CODE (TREE_TYPE (var)) == POINTER_TYPE + && !is_global_var (var)) + { + new_var = maybe_lookup_decl (var, ctx); + if (new_var == NULL_TREE) + new_var = maybe_lookup_decl_in_outer_ctx (var, ctx); + x = builtin_decl_explicit (BUILT_IN_ASSUME_ALIGNED); + tree alarg = omp_clause_aligned_alignment (c); + alarg = fold_convert_loc (clause_loc, size_type_node, alarg); + x = build_call_expr_loc (clause_loc, x, 2, new_var, alarg); + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + x = build2 (MODIFY_EXPR, TREE_TYPE (new_var), new_var, x); + gimplify_and_add (x, ilist); + } + else if (TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE + && is_global_var (var)) + { + tree ptype = build_pointer_type (TREE_TYPE (var)), t, t2; + new_var = lookup_decl (var, ctx); + t = maybe_lookup_decl_in_outer_ctx (var, ctx); + t = build_fold_addr_expr_loc (clause_loc, t); + t2 = builtin_decl_explicit (BUILT_IN_ASSUME_ALIGNED); + tree alarg = omp_clause_aligned_alignment (c); + alarg = fold_convert_loc (clause_loc, size_type_node, alarg); + t = build_call_expr_loc (clause_loc, t2, 2, t, alarg); + t = fold_convert_loc (clause_loc, ptype, t); + x = create_tmp_var (ptype); + t = build2 (MODIFY_EXPR, ptype, x, t); + gimplify_and_add (t, ilist); + t = build_simple_mem_ref_loc (clause_loc, x); + SET_DECL_VALUE_EXPR (new_var, t); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + continue; + case OMP_CLAUSE__CONDTEMP_: + if (is_parallel_ctx (ctx) + || (is_simd && !OMP_CLAUSE__CONDTEMP__ITER (c))) + break; + continue; + default: + continue; + } + + if (task_reduction_p != (pass >= 2)) + continue; + + allocator = NULL_TREE; + allocate_ptr = NULL_TREE; + new_var = var = OMP_CLAUSE_DECL (c); + if ((c_kind == OMP_CLAUSE_REDUCTION + || c_kind == OMP_CLAUSE_IN_REDUCTION) + && TREE_CODE (var) == MEM_REF) + { + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == INDIRECT_REF + || TREE_CODE (var) == ADDR_EXPR) + var = TREE_OPERAND (var, 0); + if (is_variable_sized (var)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (var)); + var = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (var) == INDIRECT_REF); + var = TREE_OPERAND (var, 0); + gcc_assert (DECL_P (var)); + } + new_var = var; + } + if (c_kind == OMP_CLAUSE_IN_REDUCTION && is_omp_target (ctx->stmt)) + { + splay_tree_key key = (splay_tree_key) &DECL_CONTEXT (var); + new_var = (tree) splay_tree_lookup (ctx->field_map, key)->value; + } + else if (c_kind != OMP_CLAUSE_COPYIN) + new_var = lookup_decl (var, ctx); + + if (c_kind == OMP_CLAUSE_SHARED || c_kind == OMP_CLAUSE_COPYIN) + { + if (pass != 0) + continue; + } + /* C/C++ array section reductions. */ + else if ((c_kind == OMP_CLAUSE_REDUCTION + || c_kind == OMP_CLAUSE_IN_REDUCTION) + && var != OMP_CLAUSE_DECL (c)) + { + if (pass == 0) + continue; + + tree bias = TREE_OPERAND (OMP_CLAUSE_DECL (c), 1); + tree orig_var = TREE_OPERAND (OMP_CLAUSE_DECL (c), 0); + + if (TREE_CODE (orig_var) == POINTER_PLUS_EXPR) + { + tree b = TREE_OPERAND (orig_var, 1); + if (is_omp_target (ctx->stmt)) + b = NULL_TREE; + else + b = maybe_lookup_decl (b, ctx); + if (b == NULL) + { + b = TREE_OPERAND (orig_var, 1); + b = maybe_lookup_decl_in_outer_ctx (b, ctx); + } + if (integer_zerop (bias)) + bias = b; + else + { + bias = fold_convert_loc (clause_loc, + TREE_TYPE (b), bias); + bias = fold_build2_loc (clause_loc, PLUS_EXPR, + TREE_TYPE (b), b, bias); + } + orig_var = TREE_OPERAND (orig_var, 0); + } + if (pass == 2) + { + tree out = maybe_lookup_decl_in_outer_ctx (var, ctx); + if (is_global_var (out) + && TREE_CODE (TREE_TYPE (out)) != POINTER_TYPE + && (TREE_CODE (TREE_TYPE (out)) != REFERENCE_TYPE + || (TREE_CODE (TREE_TYPE (TREE_TYPE (out))) + != POINTER_TYPE))) + x = var; + else if (is_omp_target (ctx->stmt)) + x = out; + else + { + bool by_ref = use_pointer_for_field (var, NULL); + x = build_receiver_ref (var, by_ref, ctx); + if (TREE_CODE (TREE_TYPE (var)) == REFERENCE_TYPE + && (TREE_CODE (TREE_TYPE (TREE_TYPE (var))) + == POINTER_TYPE)) + x = build_fold_addr_expr (x); + } + if (TREE_CODE (orig_var) == INDIRECT_REF) + x = build_simple_mem_ref (x); + else if (TREE_CODE (orig_var) == ADDR_EXPR) + { + if (var == TREE_OPERAND (orig_var, 0)) + x = build_fold_addr_expr (x); + } + bias = fold_convert (sizetype, bias); + x = fold_convert (ptr_type_node, x); + x = fold_build2_loc (clause_loc, POINTER_PLUS_EXPR, + TREE_TYPE (x), x, bias); + unsigned cnt = task_reduction_cnt - 1; + if (!task_reduction_needs_orig_p) + cnt += (task_reduction_cntorig_full + - task_reduction_cntorig); + else + cnt = task_reduction_cntorig - 1; + tree r = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (cnt), NULL_TREE, NULL_TREE); + gimplify_assign (r, x, ilist); + continue; + } + + if (TREE_CODE (orig_var) == INDIRECT_REF + || TREE_CODE (orig_var) == ADDR_EXPR) + orig_var = TREE_OPERAND (orig_var, 0); + tree d = OMP_CLAUSE_DECL (c); + tree type = TREE_TYPE (d); + gcc_assert (TREE_CODE (type) == ARRAY_TYPE); + tree v = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree sz = v; + const char *name = get_name (orig_var); + if (pass != 3 && !TREE_CONSTANT (v)) + { + tree t; + if (is_omp_target (ctx->stmt)) + t = NULL_TREE; + else + t = maybe_lookup_decl (v, ctx); + if (t) + v = t; + else + v = maybe_lookup_decl_in_outer_ctx (v, ctx); + gimplify_expr (&v, ilist, NULL, is_gimple_val, fb_rvalue); + t = fold_build2_loc (clause_loc, PLUS_EXPR, + TREE_TYPE (v), v, + build_int_cst (TREE_TYPE (v), 1)); + sz = fold_build2_loc (clause_loc, MULT_EXPR, + TREE_TYPE (v), t, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + } + if (pass == 3) + { + tree xv = create_tmp_var (ptr_type_node); + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + { + unsigned cnt = task_reduction_cnt - 1; + if (!task_reduction_needs_orig_p) + cnt += (task_reduction_cntorig_full + - task_reduction_cntorig); + else + cnt = task_reduction_cntorig - 1; + x = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (cnt), NULL_TREE, NULL_TREE); + + gimple *g = gimple_build_assign (xv, x); + gimple_seq_add_stmt (ilist, g); + } + else + { + unsigned int idx = *ctx->task_reduction_map->get (c); + tree off; + if (ctx->task_reductions[1 + idx]) + off = fold_convert (sizetype, + ctx->task_reductions[1 + idx]); + else + off = task_reduction_read (ilist, tskred_temp, sizetype, + 7 + 3 * idx + 1); + gimple *g = gimple_build_assign (xv, POINTER_PLUS_EXPR, + tskred_base, off); + gimple_seq_add_stmt (ilist, g); + } + x = fold_convert (build_pointer_type (boolean_type_node), + xv); + if (TREE_CONSTANT (v)) + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (x), x, + TYPE_SIZE_UNIT (type)); + else + { + tree t; + if (is_omp_target (ctx->stmt)) + t = NULL_TREE; + else + t = maybe_lookup_decl (v, ctx); + if (t) + v = t; + else + v = maybe_lookup_decl_in_outer_ctx (v, ctx); + gimplify_expr (&v, ilist, NULL, is_gimple_val, + fb_rvalue); + t = fold_build2_loc (clause_loc, PLUS_EXPR, + TREE_TYPE (v), v, + build_int_cst (TREE_TYPE (v), 1)); + t = fold_build2_loc (clause_loc, MULT_EXPR, + TREE_TYPE (v), t, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (x), x, t); + } + cond = create_tmp_var (TREE_TYPE (x)); + gimplify_assign (cond, x, ilist); + x = xv; + } + else if (lower_private_allocate (var, type, allocator, + allocate_ptr, ilist, ctx, + true, + TREE_CONSTANT (v) + ? TYPE_SIZE_UNIT (type) + : sz)) + x = allocate_ptr; + else if (TREE_CONSTANT (v)) + { + x = create_tmp_var_raw (type, name); + gimple_add_tmp_var (x); + TREE_ADDRESSABLE (x) = 1; + x = build_fold_addr_expr_loc (clause_loc, x); + } + else + { + tree atmp + = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); + tree al = size_int (TYPE_ALIGN (TREE_TYPE (type))); + x = build_call_expr_loc (clause_loc, atmp, 2, sz, al); + } + + tree ptype = build_pointer_type (TREE_TYPE (type)); + x = fold_convert_loc (clause_loc, ptype, x); + tree y = create_tmp_var (ptype, name); + gimplify_assign (y, x, ilist); + x = y; + tree yb = y; + + if (!integer_zerop (bias)) + { + bias = fold_convert_loc (clause_loc, pointer_sized_int_node, + bias); + yb = fold_convert_loc (clause_loc, pointer_sized_int_node, + x); + yb = fold_build2_loc (clause_loc, MINUS_EXPR, + pointer_sized_int_node, yb, bias); + x = fold_convert_loc (clause_loc, TREE_TYPE (x), yb); + yb = create_tmp_var (ptype, name); + gimplify_assign (yb, x, ilist); + x = yb; + } + + d = TREE_OPERAND (d, 0); + if (TREE_CODE (d) == POINTER_PLUS_EXPR) + d = TREE_OPERAND (d, 0); + if (TREE_CODE (d) == ADDR_EXPR) + { + if (orig_var != var) + { + gcc_assert (is_variable_sized (orig_var)); + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), + x); + gimplify_assign (new_var, x, ilist); + tree new_orig_var = lookup_decl (orig_var, ctx); + tree t = build_fold_indirect_ref (new_var); + DECL_IGNORED_P (new_var) = 0; + TREE_THIS_NOTRAP (t) = 1; + SET_DECL_VALUE_EXPR (new_orig_var, t); + DECL_HAS_VALUE_EXPR_P (new_orig_var) = 1; + } + else + { + x = build2 (MEM_REF, TREE_TYPE (new_var), x, + build_int_cst (ptype, 0)); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + } + else + { + gcc_assert (orig_var == var); + if (TREE_CODE (d) == INDIRECT_REF) + { + x = create_tmp_var (ptype, name); + TREE_ADDRESSABLE (x) = 1; + gimplify_assign (x, yb, ilist); + x = build_fold_addr_expr_loc (clause_loc, x); + } + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + gimplify_assign (new_var, x, ilist); + } + /* GOMP_taskgroup_reduction_register memsets the whole + array to zero. If the initializer is zero, we don't + need to initialize it again, just mark it as ever + used unconditionally, i.e. cond = true. */ + if (cond + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == NULL_TREE + && initializer_zerop (omp_reduction_init (c, + TREE_TYPE (type)))) + { + gimple *g = gimple_build_assign (build_simple_mem_ref (cond), + boolean_true_node); + gimple_seq_add_stmt (ilist, g); + continue; + } + tree end = create_artificial_label (UNKNOWN_LOCATION); + if (cond) + { + gimple *g; + if (!is_parallel_ctx (ctx)) + { + tree condv = create_tmp_var (boolean_type_node); + g = gimple_build_assign (condv, + build_simple_mem_ref (cond)); + gimple_seq_add_stmt (ilist, g); + tree lab1 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, condv, + boolean_false_node, end, lab1); + gimple_seq_add_stmt (ilist, g); + gimple_seq_add_stmt (ilist, gimple_build_label (lab1)); + } + g = gimple_build_assign (build_simple_mem_ref (cond), + boolean_true_node); + gimple_seq_add_stmt (ilist, g); + } + + tree y1 = create_tmp_var (ptype); + gimplify_assign (y1, y, ilist); + tree i2 = NULL_TREE, y2 = NULL_TREE; + tree body2 = NULL_TREE, end2 = NULL_TREE; + tree y3 = NULL_TREE, y4 = NULL_TREE; + if (task_reduction_needs_orig_p) + { + y3 = create_tmp_var (ptype); + tree ref; + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + ref = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (task_reduction_cnt_full + + task_reduction_cntorig - 1), + NULL_TREE, NULL_TREE); + else + { + unsigned int idx = *ctx->task_reduction_map->get (c); + ref = task_reduction_read (ilist, tskred_temp, ptype, + 7 + 3 * idx); + } + gimplify_assign (y3, ref, ilist); + } + else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) || is_simd) + { + if (pass != 3) + { + y2 = create_tmp_var (ptype); + gimplify_assign (y2, y, ilist); + } + if (is_simd || OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + { + tree ref = build_outer_var_ref (var, ctx); + /* For ref build_outer_var_ref already performs this. */ + if (TREE_CODE (d) == INDIRECT_REF) + gcc_assert (omp_privatize_by_reference (var)); + else if (TREE_CODE (d) == ADDR_EXPR) + ref = build_fold_addr_expr (ref); + else if (omp_privatize_by_reference (var)) + ref = build_fold_addr_expr (ref); + ref = fold_convert_loc (clause_loc, ptype, ref); + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) + && OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + { + y3 = create_tmp_var (ptype); + gimplify_assign (y3, unshare_expr (ref), ilist); + } + if (is_simd) + { + y4 = create_tmp_var (ptype); + gimplify_assign (y4, ref, dlist); + } + } + } + tree i = create_tmp_var (TREE_TYPE (v)); + gimplify_assign (i, build_int_cst (TREE_TYPE (v), 0), ilist); + tree body = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (ilist, gimple_build_label (body)); + if (y2) + { + i2 = create_tmp_var (TREE_TYPE (v)); + gimplify_assign (i2, build_int_cst (TREE_TYPE (v), 0), dlist); + body2 = create_artificial_label (UNKNOWN_LOCATION); + end2 = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (dlist, gimple_build_label (body2)); + } + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + tree decl_placeholder + = OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c); + SET_DECL_VALUE_EXPR (decl_placeholder, + build_simple_mem_ref (y1)); + DECL_HAS_VALUE_EXPR_P (decl_placeholder) = 1; + SET_DECL_VALUE_EXPR (placeholder, + y3 ? build_simple_mem_ref (y3) + : error_mark_node); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + x = lang_hooks.decls.omp_clause_default_ctor + (c, build_simple_mem_ref (y1), + y3 ? build_simple_mem_ref (y3) : NULL_TREE); + if (x) + gimplify_and_add (x, ilist); + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (ilist, tseq); + } + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + if (is_simd) + { + SET_DECL_VALUE_EXPR (decl_placeholder, + build_simple_mem_ref (y2)); + SET_DECL_VALUE_EXPR (placeholder, + build_simple_mem_ref (y4)); + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (dlist, tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + } + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + DECL_HAS_VALUE_EXPR_P (decl_placeholder) = 0; + if (y2) + { + x = lang_hooks.decls.omp_clause_dtor + (c, build_simple_mem_ref (y2)); + if (x) + gimplify_and_add (x, dlist); + } + } + else + { + x = omp_reduction_init (c, TREE_TYPE (type)); + enum tree_code code = OMP_CLAUSE_REDUCTION_CODE (c); + + /* reduction(-:var) sums up the partial results, so it + acts identically to reduction(+:var). */ + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + gimplify_assign (build_simple_mem_ref (y1), x, ilist); + if (is_simd) + { + x = build2 (code, TREE_TYPE (type), + build_simple_mem_ref (y4), + build_simple_mem_ref (y2)); + gimplify_assign (build_simple_mem_ref (y4), x, dlist); + } + } + gimple *g + = gimple_build_assign (y1, POINTER_PLUS_EXPR, y1, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (ilist, g); + if (y3) + { + g = gimple_build_assign (y3, POINTER_PLUS_EXPR, y3, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (ilist, g); + } + g = gimple_build_assign (i, PLUS_EXPR, i, + build_int_cst (TREE_TYPE (i), 1)); + gimple_seq_add_stmt (ilist, g); + g = gimple_build_cond (LE_EXPR, i, v, body, end); + gimple_seq_add_stmt (ilist, g); + gimple_seq_add_stmt (ilist, gimple_build_label (end)); + if (y2) + { + g = gimple_build_assign (y2, POINTER_PLUS_EXPR, y2, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (dlist, g); + if (y4) + { + g = gimple_build_assign + (y4, POINTER_PLUS_EXPR, y4, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (dlist, g); + } + g = gimple_build_assign (i2, PLUS_EXPR, i2, + build_int_cst (TREE_TYPE (i2), 1)); + gimple_seq_add_stmt (dlist, g); + g = gimple_build_cond (LE_EXPR, i2, v, body2, end2); + gimple_seq_add_stmt (dlist, g); + gimple_seq_add_stmt (dlist, gimple_build_label (end2)); + } + if (allocator) + { + tree f = builtin_decl_explicit (BUILT_IN_GOMP_FREE); + g = gimple_build_call (f, 2, allocate_ptr, allocator); + gimple_seq_add_stmt (dlist, g); + } + continue; + } + else if (pass == 2) + { + tree out = maybe_lookup_decl_in_outer_ctx (var, ctx); + if (is_global_var (out)) + x = var; + else if (is_omp_target (ctx->stmt)) + x = out; + else + { + bool by_ref = use_pointer_for_field (var, ctx); + x = build_receiver_ref (var, by_ref, ctx); + } + if (!omp_privatize_by_reference (var)) + x = build_fold_addr_expr (x); + x = fold_convert (ptr_type_node, x); + unsigned cnt = task_reduction_cnt - 1; + if (!task_reduction_needs_orig_p) + cnt += task_reduction_cntorig_full - task_reduction_cntorig; + else + cnt = task_reduction_cntorig - 1; + tree r = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (cnt), NULL_TREE, NULL_TREE); + gimplify_assign (r, x, ilist); + continue; + } + else if (pass == 3) + { + tree type = TREE_TYPE (new_var); + if (!omp_privatize_by_reference (var)) + type = build_pointer_type (type); + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + { + unsigned cnt = task_reduction_cnt - 1; + if (!task_reduction_needs_orig_p) + cnt += (task_reduction_cntorig_full + - task_reduction_cntorig); + else + cnt = task_reduction_cntorig - 1; + x = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (cnt), NULL_TREE, NULL_TREE); + } + else + { + unsigned int idx = *ctx->task_reduction_map->get (c); + tree off; + if (ctx->task_reductions[1 + idx]) + off = fold_convert (sizetype, + ctx->task_reductions[1 + idx]); + else + off = task_reduction_read (ilist, tskred_temp, sizetype, + 7 + 3 * idx + 1); + x = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, + tskred_base, off); + } + x = fold_convert (type, x); + tree t; + if (omp_privatize_by_reference (var)) + { + gimplify_assign (new_var, x, ilist); + t = new_var; + new_var = build_simple_mem_ref (new_var); + } + else + { + t = create_tmp_var (type); + gimplify_assign (t, x, ilist); + SET_DECL_VALUE_EXPR (new_var, build_simple_mem_ref (t)); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + t = fold_convert (build_pointer_type (boolean_type_node), t); + t = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + cond = create_tmp_var (TREE_TYPE (t)); + gimplify_assign (cond, t, ilist); + } + else if (is_variable_sized (var)) + { + /* For variable sized types, we need to allocate the + actual storage here. Call alloca and store the + result in the pointer decl that we created elsewhere. */ + if (pass == 0) + continue; + + if (c_kind != OMP_CLAUSE_FIRSTPRIVATE || !is_task_ctx (ctx)) + { + tree tmp; + + ptr = DECL_VALUE_EXPR (new_var); + gcc_assert (TREE_CODE (ptr) == INDIRECT_REF); + ptr = TREE_OPERAND (ptr, 0); + gcc_assert (DECL_P (ptr)); + x = TYPE_SIZE_UNIT (TREE_TYPE (new_var)); + + if (lower_private_allocate (var, new_var, allocator, + allocate_ptr, ilist, ctx, + false, x)) + tmp = allocate_ptr; + else + { + /* void *tmp = __builtin_alloca */ + tree atmp + = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); + gcall *stmt + = gimple_build_call (atmp, 2, x, + size_int (DECL_ALIGN (var))); + cfun->calls_alloca = 1; + tmp = create_tmp_var_raw (ptr_type_node); + gimple_add_tmp_var (tmp); + gimple_call_set_lhs (stmt, tmp); + + gimple_seq_add_stmt (ilist, stmt); + } + + x = fold_convert_loc (clause_loc, TREE_TYPE (ptr), tmp); + gimplify_assign (ptr, x, ilist); + } + } + else if (omp_privatize_by_reference (var) + && (c_kind != OMP_CLAUSE_FIRSTPRIVATE + || !OMP_CLAUSE_FIRSTPRIVATE_NO_REFERENCE (c))) + { + /* For references that are being privatized for Fortran, + allocate new backing storage for the new pointer + variable. This allows us to avoid changing all the + code that expects a pointer to something that expects + a direct variable. */ + if (pass == 0) + continue; + + x = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_var))); + if (c_kind == OMP_CLAUSE_FIRSTPRIVATE && is_task_ctx (ctx)) + { + x = build_receiver_ref (var, false, ctx); + if (ctx->allocate_map) + if (tree *allocatep = ctx->allocate_map->get (var)) + { + allocator = *allocatep; + if (TREE_CODE (allocator) == TREE_LIST) + allocator = TREE_PURPOSE (allocator); + if (TREE_CODE (allocator) != INTEGER_CST) + allocator = build_outer_var_ref (allocator, ctx); + allocator = fold_convert (pointer_sized_int_node, + allocator); + allocate_ptr = unshare_expr (x); + } + if (allocator == NULL_TREE) + x = build_fold_addr_expr_loc (clause_loc, x); + } + else if (lower_private_allocate (var, new_var, allocator, + allocate_ptr, + ilist, ctx, true, x)) + x = allocate_ptr; + else if (TREE_CONSTANT (x)) + { + /* For reduction in SIMD loop, defer adding the + initialization of the reference, because if we decide + to use SIMD array for it, the initilization could cause + expansion ICE. Ditto for other privatization clauses. */ + if (is_simd) + x = NULL_TREE; + else + { + x = create_tmp_var_raw (TREE_TYPE (TREE_TYPE (new_var)), + get_name (var)); + gimple_add_tmp_var (x); + TREE_ADDRESSABLE (x) = 1; + x = build_fold_addr_expr_loc (clause_loc, x); + } + } + else + { + tree atmp + = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); + tree rtype = TREE_TYPE (TREE_TYPE (new_var)); + tree al = size_int (TYPE_ALIGN (rtype)); + x = build_call_expr_loc (clause_loc, atmp, 2, x, al); + } + + if (x) + { + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + gimplify_assign (new_var, x, ilist); + } + + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + } + else if ((c_kind == OMP_CLAUSE_REDUCTION + || c_kind == OMP_CLAUSE_IN_REDUCTION) + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + if (pass == 0) + continue; + } + else if (pass != 0) + continue; + + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_SHARED: + /* Ignore shared directives in teams construct inside + target construct. */ + if (gimple_code (ctx->stmt) == GIMPLE_OMP_TEAMS + && !is_host_teams_ctx (ctx)) + continue; + /* Shared global vars are just accessed directly. */ + if (is_global_var (new_var)) + break; + /* For taskloop firstprivate/lastprivate, represented + as firstprivate and shared clause on the task, new_var + is the firstprivate var. */ + if (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + break; + /* Set up the DECL_VALUE_EXPR for shared variables now. This + needs to be delayed until after fixup_child_record_type so + that we get the correct type during the dereference. */ + by_ref = use_pointer_for_field (var, ctx); + x = build_receiver_ref (var, by_ref, ctx); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + + /* ??? If VAR is not passed by reference, and the variable + hasn't been initialized yet, then we'll get a warning for + the store into the omp_data_s structure. Ideally, we'd be + able to notice this and not store anything at all, but + we're generating code too early. Suppress the warning. */ + if (!by_ref) + suppress_warning (var, OPT_Wuninitialized); + break; + + case OMP_CLAUSE__CONDTEMP_: + if (is_parallel_ctx (ctx)) + { + x = build_receiver_ref (var, false, ctx); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + else if (is_simd && !OMP_CLAUSE__CONDTEMP__ITER (c)) + { + x = build_zero_cst (TREE_TYPE (var)); + goto do_private; + } + break; + + case OMP_CLAUSE_LASTPRIVATE: + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_PRIVATE: + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_PRIVATE) + x = build_outer_var_ref (var, ctx); + else if (OMP_CLAUSE_PRIVATE_OUTER_REF (c)) + { + if (is_task_ctx (ctx)) + x = build_receiver_ref (var, false, ctx); + else + x = build_outer_var_ref (var, ctx, OMP_CLAUSE_PRIVATE); + } + else + x = NULL; + do_private: + tree nx; + bool copy_ctor; + copy_ctor = false; + lower_private_allocate (var, new_var, allocator, allocate_ptr, + ilist, ctx, false, NULL_TREE); + nx = unshare_expr (new_var); + if (is_simd + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_LOOP_IV (c)) + copy_ctor = true; + if (copy_ctor) + nx = lang_hooks.decls.omp_clause_copy_ctor (c, nx, x); + else + nx = lang_hooks.decls.omp_clause_default_ctor (c, nx, x); + if (is_simd) + { + tree y = lang_hooks.decls.omp_clause_dtor (c, new_var); + if ((TREE_ADDRESSABLE (new_var) || nx || y + || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && (gimple_omp_for_collapse (ctx->stmt) != 1 + || (gimple_omp_for_index (ctx->stmt, 0) + != new_var))) + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE__CONDTEMP_ + || omp_privatize_by_reference (var)) + && lower_rec_simd_input_clauses (new_var, ctx, &sctx, + ivar, lvar)) + { + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + tree new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + + if (nx) + { + tree iv = unshare_expr (ivar); + if (copy_ctor) + x = lang_hooks.decls.omp_clause_copy_ctor (c, iv, + x); + else + x = lang_hooks.decls.omp_clause_default_ctor (c, + iv, + x); + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE__CONDTEMP_) + { + x = build2 (MODIFY_EXPR, TREE_TYPE (ivar), + unshare_expr (ivar), x); + nx = x; + } + if (nx && x) + gimplify_and_add (x, &llist[0]); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c)) + { + tree v = new_var; + if (!DECL_P (v)) + { + gcc_assert (TREE_CODE (v) == MEM_REF); + v = TREE_OPERAND (v, 0); + gcc_assert (DECL_P (v)); + } + v = *ctx->lastprivate_conditional_map->get (v); + tree t = create_tmp_var (TREE_TYPE (v)); + tree z = build_zero_cst (TREE_TYPE (v)); + tree orig_v + = build_outer_var_ref (var, ctx, + OMP_CLAUSE_LASTPRIVATE); + gimple_seq_add_stmt (dlist, + gimple_build_assign (t, z)); + gcc_assert (DECL_HAS_VALUE_EXPR_P (v)); + tree civar = DECL_VALUE_EXPR (v); + gcc_assert (TREE_CODE (civar) == ARRAY_REF); + civar = unshare_expr (civar); + TREE_OPERAND (civar, 1) = sctx.idx; + x = build2 (MODIFY_EXPR, TREE_TYPE (t), t, + unshare_expr (civar)); + x = build2 (COMPOUND_EXPR, TREE_TYPE (orig_v), x, + build2 (MODIFY_EXPR, TREE_TYPE (orig_v), + orig_v, unshare_expr (ivar))); + tree cond = build2 (LT_EXPR, boolean_type_node, t, + civar); + x = build3 (COND_EXPR, void_type_node, cond, x, + void_node); + gimple_seq tseq = NULL; + gimplify_and_add (x, &tseq); + if (ctx->outer) + lower_omp (&tseq, ctx->outer); + gimple_seq_add_seq (&llist[1], tseq); + } + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && ctx->for_simd_scan_phase) + { + x = unshare_expr (ivar); + tree orig_v + = build_outer_var_ref (var, ctx, + OMP_CLAUSE_LASTPRIVATE); + x = lang_hooks.decls.omp_clause_assign_op (c, x, + orig_v); + gimplify_and_add (x, &llist[0]); + } + if (y) + { + y = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (y) + gimplify_and_add (y, &llist[1]); + } + break; + } + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + tree new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + tree type = TREE_TYPE (TREE_TYPE (new_vard)); + x = TYPE_SIZE_UNIT (type); + if (TREE_CONSTANT (x)) + { + x = create_tmp_var_raw (type, get_name (var)); + gimple_add_tmp_var (x); + TREE_ADDRESSABLE (x) = 1; + x = build_fold_addr_expr_loc (clause_loc, x); + x = fold_convert_loc (clause_loc, + TREE_TYPE (new_vard), x); + gimplify_assign (new_vard, x, ilist); + } + } + } + if (nx) + gimplify_and_add (nx, ilist); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && is_simd + && ctx->for_simd_scan_phase) + { + tree orig_v = build_outer_var_ref (var, ctx, + OMP_CLAUSE_LASTPRIVATE); + x = lang_hooks.decls.omp_clause_assign_op (c, new_var, + orig_v); + gimplify_and_add (x, ilist); + } + /* FALLTHRU */ + + do_dtor: + x = lang_hooks.decls.omp_clause_dtor (c, new_var); + if (x) + gimplify_and_add (x, dlist); + if (allocator) + { + if (!is_gimple_val (allocator)) + { + tree avar = create_tmp_var (TREE_TYPE (allocator)); + gimplify_assign (avar, allocator, dlist); + allocator = avar; + } + if (!is_gimple_val (allocate_ptr)) + { + tree apvar = create_tmp_var (TREE_TYPE (allocate_ptr)); + gimplify_assign (apvar, allocate_ptr, dlist); + allocate_ptr = apvar; + } + tree f = builtin_decl_explicit (BUILT_IN_GOMP_FREE); + gimple *g + = gimple_build_call (f, 2, allocate_ptr, allocator); + gimple_seq_add_stmt (dlist, g); + } + break; + + case OMP_CLAUSE_LINEAR: + if (!OMP_CLAUSE_LINEAR_NO_COPYIN (c)) + goto do_firstprivate; + if (OMP_CLAUSE_LINEAR_NO_COPYOUT (c)) + x = NULL; + else + x = build_outer_var_ref (var, ctx); + goto do_private; + + case OMP_CLAUSE_FIRSTPRIVATE: + if (is_task_ctx (ctx)) + { + if ((omp_privatize_by_reference (var) + && !OMP_CLAUSE_FIRSTPRIVATE_NO_REFERENCE (c)) + || is_variable_sized (var)) + goto do_dtor; + else if (is_global_var (maybe_lookup_decl_in_outer_ctx (var, + ctx)) + || use_pointer_for_field (var, NULL)) + { + x = build_receiver_ref (var, false, ctx); + if (ctx->allocate_map) + if (tree *allocatep = ctx->allocate_map->get (var)) + { + allocator = *allocatep; + if (TREE_CODE (allocator) == TREE_LIST) + allocator = TREE_PURPOSE (allocator); + if (TREE_CODE (allocator) != INTEGER_CST) + allocator = build_outer_var_ref (allocator, ctx); + allocator = fold_convert (pointer_sized_int_node, + allocator); + allocate_ptr = unshare_expr (x); + x = build_simple_mem_ref (x); + TREE_THIS_NOTRAP (x) = 1; + } + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + goto do_dtor; + } + } + if (OMP_CLAUSE_FIRSTPRIVATE_NO_REFERENCE (c) + && omp_privatize_by_reference (var)) + { + x = build_outer_var_ref (var, ctx); + gcc_assert (TREE_CODE (x) == MEM_REF + && integer_zerop (TREE_OPERAND (x, 1))); + x = TREE_OPERAND (x, 0); + x = lang_hooks.decls.omp_clause_copy_ctor + (c, unshare_expr (new_var), x); + gimplify_and_add (x, ilist); + goto do_dtor; + } + do_firstprivate: + lower_private_allocate (var, new_var, allocator, allocate_ptr, + ilist, ctx, false, NULL_TREE); + x = build_outer_var_ref (var, ctx); + if (is_simd) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && gimple_omp_for_combined_into_p (ctx->stmt)) + { + tree t = OMP_CLAUSE_LINEAR_STEP (c); + tree stept = TREE_TYPE (t); + tree ct = omp_find_clause (clauses, + OMP_CLAUSE__LOOPTEMP_); + gcc_assert (ct); + tree l = OMP_CLAUSE_DECL (ct); + tree n1 = fd->loop.n1; + tree step = fd->loop.step; + tree itype = TREE_TYPE (l); + if (POINTER_TYPE_P (itype)) + itype = signed_type_for (itype); + l = fold_build2 (MINUS_EXPR, itype, l, n1); + if (TYPE_UNSIGNED (itype) + && fd->loop.cond_code == GT_EXPR) + l = fold_build2 (TRUNC_DIV_EXPR, itype, + fold_build1 (NEGATE_EXPR, itype, l), + fold_build1 (NEGATE_EXPR, + itype, step)); + else + l = fold_build2 (TRUNC_DIV_EXPR, itype, l, step); + t = fold_build2 (MULT_EXPR, stept, + fold_convert (stept, l), t); + + if (OMP_CLAUSE_LINEAR_ARRAY (c)) + { + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + tree new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + tree type = TREE_TYPE (TREE_TYPE (new_vard)); + nx = TYPE_SIZE_UNIT (type); + if (TREE_CONSTANT (nx)) + { + nx = create_tmp_var_raw (type, + get_name (var)); + gimple_add_tmp_var (nx); + TREE_ADDRESSABLE (nx) = 1; + nx = build_fold_addr_expr_loc (clause_loc, + nx); + nx = fold_convert_loc (clause_loc, + TREE_TYPE (new_vard), + nx); + gimplify_assign (new_vard, nx, ilist); + } + } + + x = lang_hooks.decls.omp_clause_linear_ctor + (c, new_var, x, t); + gimplify_and_add (x, ilist); + goto do_dtor; + } + + if (POINTER_TYPE_P (TREE_TYPE (x))) + x = fold_build2 (POINTER_PLUS_EXPR, + TREE_TYPE (x), x, t); + else + x = fold_build2 (PLUS_EXPR, TREE_TYPE (x), x, t); + } + + if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_LINEAR + || TREE_ADDRESSABLE (new_var) + || omp_privatize_by_reference (var)) + && lower_rec_simd_input_clauses (new_var, ctx, &sctx, + ivar, lvar)) + { + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + tree new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR) + { + tree iv = create_tmp_var (TREE_TYPE (new_var)); + x = lang_hooks.decls.omp_clause_copy_ctor (c, iv, x); + gimplify_and_add (x, ilist); + gimple_stmt_iterator gsi + = gsi_start_1 (gimple_omp_body_ptr (ctx->stmt)); + gassign *g + = gimple_build_assign (unshare_expr (lvar), iv); + gsi_insert_before_without_update (&gsi, g, + GSI_SAME_STMT); + tree t = OMP_CLAUSE_LINEAR_STEP (c); + enum tree_code code = PLUS_EXPR; + if (POINTER_TYPE_P (TREE_TYPE (new_var))) + code = POINTER_PLUS_EXPR; + g = gimple_build_assign (iv, code, iv, t); + gsi_insert_before_without_update (&gsi, g, + GSI_SAME_STMT); + break; + } + x = lang_hooks.decls.omp_clause_copy_ctor + (c, unshare_expr (ivar), x); + gimplify_and_add (x, &llist[0]); + x = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (x) + gimplify_and_add (x, &llist[1]); + break; + } + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + tree new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + tree type = TREE_TYPE (TREE_TYPE (new_vard)); + nx = TYPE_SIZE_UNIT (type); + if (TREE_CONSTANT (nx)) + { + nx = create_tmp_var_raw (type, get_name (var)); + gimple_add_tmp_var (nx); + TREE_ADDRESSABLE (nx) = 1; + nx = build_fold_addr_expr_loc (clause_loc, nx); + nx = fold_convert_loc (clause_loc, + TREE_TYPE (new_vard), nx); + gimplify_assign (new_vard, nx, ilist); + } + } + } + x = lang_hooks.decls.omp_clause_copy_ctor + (c, unshare_expr (new_var), x); + gimplify_and_add (x, ilist); + goto do_dtor; + + case OMP_CLAUSE__LOOPTEMP_: + case OMP_CLAUSE__REDUCTEMP_: + gcc_assert (is_taskreg_ctx (ctx)); + x = build_outer_var_ref (var, ctx); + x = build2 (MODIFY_EXPR, TREE_TYPE (new_var), new_var, x); + gimplify_and_add (x, ilist); + break; + + case OMP_CLAUSE_COPYIN: + by_ref = use_pointer_for_field (var, NULL); + x = build_receiver_ref (var, by_ref, ctx); + x = lang_hooks.decls.omp_clause_assign_op (c, new_var, x); + append_to_statement_list (x, ©in_seq); + copyin_by_ref |= by_ref; + break; + + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + /* OpenACC reductions are initialized using the + GOACC_REDUCTION internal function. */ + if (is_gimple_omp_oacc (ctx->stmt)) + break; + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + gimple *tseq; + tree ptype = TREE_TYPE (placeholder); + if (cond) + { + x = error_mark_node; + if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c) + && !task_reduction_needs_orig_p) + x = var; + else if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + { + tree pptype = build_pointer_type (ptype); + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) + x = build4 (ARRAY_REF, ptr_type_node, tskred_avar, + size_int (task_reduction_cnt_full + + task_reduction_cntorig - 1), + NULL_TREE, NULL_TREE); + else + { + unsigned int idx + = *ctx->task_reduction_map->get (c); + x = task_reduction_read (ilist, tskred_temp, + pptype, 7 + 3 * idx); + } + x = fold_convert (pptype, x); + x = build_simple_mem_ref (x); + } + } + else + { + lower_private_allocate (var, new_var, allocator, + allocate_ptr, ilist, ctx, false, + NULL_TREE); + x = build_outer_var_ref (var, ctx); + + if (omp_privatize_by_reference (var) + && !useless_type_conversion_p (ptype, TREE_TYPE (x))) + x = build_fold_addr_expr_loc (clause_loc, x); + } + SET_DECL_VALUE_EXPR (placeholder, x); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + tree new_vard = new_var; + if (omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + } + tree rvar = NULL_TREE, *rvarp = NULL, rvar2 = NULL_TREE; + if (is_simd + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + rvarp = &rvar; + if (is_simd + && lower_rec_simd_input_clauses (new_var, ctx, &sctx, + ivar, lvar, rvarp, + &rvar2)) + { + if (new_vard == new_var) + { + gcc_assert (DECL_VALUE_EXPR (new_var) == lvar); + SET_DECL_VALUE_EXPR (new_var, ivar); + } + else + { + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (ivar)); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + x = lang_hooks.decls.omp_clause_default_ctor + (c, unshare_expr (ivar), + build_outer_var_ref (var, ctx)); + if (rvarp && ctx->for_simd_scan_phase) + { + if (x) + gimplify_and_add (x, &llist[0]); + x = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (x) + gimplify_and_add (x, &llist[1]); + break; + } + else if (rvarp) + { + if (x) + { + gimplify_and_add (x, &llist[0]); + + tree ivar2 = unshare_expr (lvar); + TREE_OPERAND (ivar2, 1) = sctx.idx; + x = lang_hooks.decls.omp_clause_default_ctor + (c, ivar2, build_outer_var_ref (var, ctx)); + gimplify_and_add (x, &llist[0]); + + if (rvar2) + { + x = lang_hooks.decls.omp_clause_default_ctor + (c, unshare_expr (rvar2), + build_outer_var_ref (var, ctx)); + gimplify_and_add (x, &llist[0]); + } + + /* For types that need construction, add another + private var which will be default constructed + and optionally initialized with + OMP_CLAUSE_REDUCTION_GIMPLE_INIT, as in the + loop we want to assign this value instead of + constructing and destructing it in each + iteration. */ + tree nv = create_tmp_var_raw (TREE_TYPE (ivar)); + gimple_add_tmp_var (nv); + ctx->cb.decl_map->put (TREE_OPERAND (rvar2 + ? rvar2 + : ivar, 0), + nv); + x = lang_hooks.decls.omp_clause_default_ctor + (c, nv, build_outer_var_ref (var, ctx)); + gimplify_and_add (x, ilist); + + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + x = DECL_VALUE_EXPR (new_vard); + tree vexpr = nv; + if (new_vard != new_var) + vexpr = build_fold_addr_expr (nv); + SET_DECL_VALUE_EXPR (new_vard, vexpr); + lower_omp (&tseq, ctx); + SET_DECL_VALUE_EXPR (new_vard, x); + gimple_seq_add_seq (ilist, tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + } + + x = lang_hooks.decls.omp_clause_dtor (c, nv); + if (x) + gimplify_and_add (x, dlist); + } + + tree ref = build_outer_var_ref (var, ctx); + x = unshare_expr (ivar); + x = lang_hooks.decls.omp_clause_assign_op (c, x, + ref); + gimplify_and_add (x, &llist[0]); + + ref = build_outer_var_ref (var, ctx); + x = lang_hooks.decls.omp_clause_assign_op (c, ref, + rvar); + gimplify_and_add (x, &llist[3]); + + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (new_vard == new_var) + SET_DECL_VALUE_EXPR (new_var, lvar); + else + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + + x = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (x) + gimplify_and_add (x, &llist[1]); + + tree ivar2 = unshare_expr (lvar); + TREE_OPERAND (ivar2, 1) = sctx.idx; + x = lang_hooks.decls.omp_clause_dtor (c, ivar2); + if (x) + gimplify_and_add (x, &llist[1]); + + if (rvar2) + { + x = lang_hooks.decls.omp_clause_dtor (c, rvar2); + if (x) + gimplify_and_add (x, &llist[1]); + } + break; + } + if (x) + gimplify_and_add (x, &llist[0]); + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&llist[0], tseq); + } + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&llist[1], tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (new_vard == new_var) + SET_DECL_VALUE_EXPR (new_var, lvar); + else + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + x = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (x) + gimplify_and_add (x, &llist[1]); + break; + } + /* If this is a reference to constant size reduction var + with placeholder, we haven't emitted the initializer + for it because it is undesirable if SIMD arrays are used. + But if they aren't used, we need to emit the deferred + initialization now. */ + else if (omp_privatize_by_reference (var) && is_simd) + handle_simd_reference (clause_loc, new_vard, ilist); + + tree lab2 = NULL_TREE; + if (cond) + { + gimple *g; + if (!is_parallel_ctx (ctx)) + { + tree condv = create_tmp_var (boolean_type_node); + tree m = build_simple_mem_ref (cond); + g = gimple_build_assign (condv, m); + gimple_seq_add_stmt (ilist, g); + tree lab1 + = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, condv, + boolean_false_node, + lab2, lab1); + gimple_seq_add_stmt (ilist, g); + gimple_seq_add_stmt (ilist, + gimple_build_label (lab1)); + } + g = gimple_build_assign (build_simple_mem_ref (cond), + boolean_true_node); + gimple_seq_add_stmt (ilist, g); + } + x = lang_hooks.decls.omp_clause_default_ctor + (c, unshare_expr (new_var), + cond ? NULL_TREE + : build_outer_var_ref (var, ctx)); + if (x) + gimplify_and_add (x, ilist); + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + { + if (ctx->for_simd_scan_phase) + goto do_dtor; + if (x || (!is_simd + && OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c))) + { + tree nv = create_tmp_var_raw (TREE_TYPE (new_var)); + gimple_add_tmp_var (nv); + ctx->cb.decl_map->put (new_vard, nv); + x = lang_hooks.decls.omp_clause_default_ctor + (c, nv, build_outer_var_ref (var, ctx)); + if (x) + gimplify_and_add (x, ilist); + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + tree vexpr = nv; + if (new_vard != new_var) + vexpr = build_fold_addr_expr (nv); + SET_DECL_VALUE_EXPR (new_vard, vexpr); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + lower_omp (&tseq, ctx); + SET_DECL_VALUE_EXPR (new_vard, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (new_vard) = 0; + gimple_seq_add_seq (ilist, tseq); + } + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + if (is_simd && ctx->scan_exclusive) + { + tree nv2 + = create_tmp_var_raw (TREE_TYPE (new_var)); + gimple_add_tmp_var (nv2); + ctx->cb.decl_map->put (nv, nv2); + x = lang_hooks.decls.omp_clause_default_ctor + (c, nv2, build_outer_var_ref (var, ctx)); + gimplify_and_add (x, ilist); + x = lang_hooks.decls.omp_clause_dtor (c, nv2); + if (x) + gimplify_and_add (x, dlist); + } + x = lang_hooks.decls.omp_clause_dtor (c, nv); + if (x) + gimplify_and_add (x, dlist); + } + else if (is_simd + && ctx->scan_exclusive + && TREE_ADDRESSABLE (TREE_TYPE (new_var))) + { + tree nv2 = create_tmp_var_raw (TREE_TYPE (new_var)); + gimple_add_tmp_var (nv2); + ctx->cb.decl_map->put (new_vard, nv2); + x = lang_hooks.decls.omp_clause_dtor (c, nv2); + if (x) + gimplify_and_add (x, dlist); + } + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + goto do_dtor; + } + + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + if (c_kind == OMP_CLAUSE_IN_REDUCTION + && is_omp_target (ctx->stmt)) + { + tree d = maybe_lookup_decl_in_outer_ctx (var, ctx); + tree oldv = NULL_TREE; + gcc_assert (d); + if (DECL_HAS_VALUE_EXPR_P (d)) + oldv = DECL_VALUE_EXPR (d); + SET_DECL_VALUE_EXPR (d, new_vard); + DECL_HAS_VALUE_EXPR_P (d) = 1; + lower_omp (&tseq, ctx); + if (oldv) + SET_DECL_VALUE_EXPR (d, oldv); + else + { + SET_DECL_VALUE_EXPR (d, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (d) = 0; + } + } + else + lower_omp (&tseq, ctx); + gimple_seq_add_seq (ilist, tseq); + } + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + if (is_simd) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (dlist, tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + } + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (cond) + { + if (lab2) + gimple_seq_add_stmt (ilist, gimple_build_label (lab2)); + break; + } + goto do_dtor; + } + else + { + x = omp_reduction_init (c, TREE_TYPE (new_var)); + gcc_assert (TREE_CODE (TREE_TYPE (new_var)) != ARRAY_TYPE); + enum tree_code code = OMP_CLAUSE_REDUCTION_CODE (c); + + if (cond) + { + gimple *g; + tree lab2 = NULL_TREE; + /* GOMP_taskgroup_reduction_register memsets the whole + array to zero. If the initializer is zero, we don't + need to initialize it again, just mark it as ever + used unconditionally, i.e. cond = true. */ + if (initializer_zerop (x)) + { + g = gimple_build_assign (build_simple_mem_ref (cond), + boolean_true_node); + gimple_seq_add_stmt (ilist, g); + break; + } + + /* Otherwise, emit + if (!cond) { cond = true; new_var = x; } */ + if (!is_parallel_ctx (ctx)) + { + tree condv = create_tmp_var (boolean_type_node); + tree m = build_simple_mem_ref (cond); + g = gimple_build_assign (condv, m); + gimple_seq_add_stmt (ilist, g); + tree lab1 + = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, condv, + boolean_false_node, + lab2, lab1); + gimple_seq_add_stmt (ilist, g); + gimple_seq_add_stmt (ilist, + gimple_build_label (lab1)); + } + g = gimple_build_assign (build_simple_mem_ref (cond), + boolean_true_node); + gimple_seq_add_stmt (ilist, g); + gimplify_assign (new_var, x, ilist); + if (lab2) + gimple_seq_add_stmt (ilist, gimple_build_label (lab2)); + break; + } + + /* reduction(-:var) sums up the partial results, so it + acts identically to reduction(+:var). */ + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + bool is_truth_op + = (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR); + tree new_vard = new_var; + if (is_simd && omp_privatize_by_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + } + tree rvar = NULL_TREE, *rvarp = NULL, rvar2 = NULL_TREE; + if (is_simd + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + rvarp = &rvar; + if (is_simd + && lower_rec_simd_input_clauses (new_var, ctx, &sctx, + ivar, lvar, rvarp, + &rvar2)) + { + if (new_vard != new_var) + { + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + + tree ref = build_outer_var_ref (var, ctx); + + if (rvarp) + { + if (ctx->for_simd_scan_phase) + break; + gimplify_assign (ivar, ref, &llist[0]); + ref = build_outer_var_ref (var, ctx); + gimplify_assign (ref, rvar, &llist[3]); + break; + } + + gimplify_assign (unshare_expr (ivar), x, &llist[0]); + + if (sctx.is_simt) + { + if (!simt_lane) + simt_lane = create_tmp_var (unsigned_type_node); + x = build_call_expr_internal_loc + (UNKNOWN_LOCATION, IFN_GOMP_SIMT_XCHG_BFLY, + TREE_TYPE (ivar), 2, ivar, simt_lane); + x = build2 (code, TREE_TYPE (ivar), ivar, x); + gimplify_assign (ivar, x, &llist[2]); + } + tree ivar2 = ivar; + tree ref2 = ref; + if (is_truth_op) + { + tree zero = build_zero_cst (TREE_TYPE (ivar)); + ivar2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, ivar, + zero); + ref2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, ref, + zero); + } + x = build2 (code, TREE_TYPE (ref), ref2, ivar2); + if (is_truth_op) + x = fold_convert (TREE_TYPE (ref), x); + ref = build_outer_var_ref (var, ctx); + gimplify_assign (ref, x, &llist[1]); + + } + else + { + lower_private_allocate (var, new_var, allocator, + allocate_ptr, ilist, ctx, + false, NULL_TREE); + if (omp_privatize_by_reference (var) && is_simd) + handle_simd_reference (clause_loc, new_vard, ilist); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + break; + gimplify_assign (new_var, x, ilist); + if (is_simd) + { + tree ref = build_outer_var_ref (var, ctx); + tree new_var2 = new_var; + tree ref2 = ref; + if (is_truth_op) + { + tree zero = build_zero_cst (TREE_TYPE (new_var)); + new_var2 + = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, new_var, + zero); + ref2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, ref, + zero); + } + x = build2 (code, TREE_TYPE (ref2), ref2, new_var2); + if (is_truth_op) + x = fold_convert (TREE_TYPE (new_var), x); + ref = build_outer_var_ref (var, ctx); + gimplify_assign (ref, x, dlist); + } + if (allocator) + goto do_dtor; + } + } + break; + + default: + gcc_unreachable (); + } + } + } + if (tskred_avar) + { + tree clobber = build_clobber (TREE_TYPE (tskred_avar)); + gimple_seq_add_stmt (ilist, gimple_build_assign (tskred_avar, clobber)); + } + + if (known_eq (sctx.max_vf, 1U)) + { + sctx.is_simt = false; + if (ctx->lastprivate_conditional_map) + { + if (gimple_omp_for_combined_into_p (ctx->stmt)) + { + /* Signal to lower_omp_1 that it should use parent context. */ + ctx->combined_into_simd_safelen1 = true; + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c)) + { + tree o = lookup_decl (OMP_CLAUSE_DECL (c), ctx); + omp_context *outer = ctx->outer; + if (gimple_code (outer->stmt) == GIMPLE_OMP_SCAN) + outer = outer->outer; + tree *v = ctx->lastprivate_conditional_map->get (o); + tree po = lookup_decl (OMP_CLAUSE_DECL (c), outer); + tree *pv = outer->lastprivate_conditional_map->get (po); + *v = *pv; + } + } + else + { + /* When not vectorized, treat lastprivate(conditional:) like + normal lastprivate, as there will be just one simd lane + writing the privatized variable. */ + delete ctx->lastprivate_conditional_map; + ctx->lastprivate_conditional_map = NULL; + } + } + } + + if (nonconst_simd_if) + { + if (sctx.lane == NULL_TREE) + { + sctx.idx = create_tmp_var (unsigned_type_node); + sctx.lane = create_tmp_var (unsigned_type_node); + } + /* FIXME: For now. */ + sctx.is_simt = false; + } + + if (sctx.lane || sctx.is_simt) + { + uid = create_tmp_var (ptr_type_node, "simduid"); + /* Don't want uninit warnings on simduid, it is always uninitialized, + but we use it not for the value, but for the DECL_UID only. */ + suppress_warning (uid, OPT_Wuninitialized); + c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SIMDUID_); + OMP_CLAUSE__SIMDUID__DECL (c) = uid; + OMP_CLAUSE_CHAIN (c) = gimple_omp_for_clauses (ctx->stmt); + gimple_omp_for_set_clauses (ctx->stmt, c); + } + /* Emit calls denoting privatized variables and initializing a pointer to + structure that holds private variables as fields after ompdevlow pass. */ + if (sctx.is_simt) + { + sctx.simt_eargs[0] = uid; + gimple *g + = gimple_build_call_internal_vec (IFN_GOMP_SIMT_ENTER, sctx.simt_eargs); + gimple_call_set_lhs (g, uid); + gimple_seq_add_stmt (ilist, g); + sctx.simt_eargs.release (); + + simtrec = create_tmp_var (ptr_type_node, ".omp_simt"); + g = gimple_build_call_internal (IFN_GOMP_SIMT_ENTER_ALLOC, 1, uid); + gimple_call_set_lhs (g, simtrec); + gimple_seq_add_stmt (ilist, g); + } + if (sctx.lane) + { + gimple *g = gimple_build_call_internal (IFN_GOMP_SIMD_LANE, + 2 + (nonconst_simd_if != NULL), + uid, integer_zero_node, + nonconst_simd_if); + gimple_call_set_lhs (g, sctx.lane); + gimple_stmt_iterator gsi = gsi_start_1 (gimple_omp_body_ptr (ctx->stmt)); + gsi_insert_before_without_update (&gsi, g, GSI_SAME_STMT); + g = gimple_build_assign (sctx.lane, INTEGER_CST, + build_int_cst (unsigned_type_node, 0)); + gimple_seq_add_stmt (ilist, g); + if (sctx.lastlane) + { + g = gimple_build_call_internal (IFN_GOMP_SIMD_LAST_LANE, + 2, uid, sctx.lane); + gimple_call_set_lhs (g, sctx.lastlane); + gimple_seq_add_stmt (dlist, g); + gimple_seq_add_seq (dlist, llist[3]); + } + /* Emit reductions across SIMT lanes in log_2(simt_vf) steps. */ + if (llist[2]) + { + tree simt_vf = create_tmp_var (unsigned_type_node); + g = gimple_build_call_internal (IFN_GOMP_SIMT_VF, 0); + gimple_call_set_lhs (g, simt_vf); + gimple_seq_add_stmt (dlist, g); + + tree t = build_int_cst (unsigned_type_node, 1); + g = gimple_build_assign (simt_lane, INTEGER_CST, t); + gimple_seq_add_stmt (dlist, g); + + t = build_int_cst (unsigned_type_node, 0); + g = gimple_build_assign (sctx.idx, INTEGER_CST, t); + gimple_seq_add_stmt (dlist, g); + + tree body = create_artificial_label (UNKNOWN_LOCATION); + tree header = create_artificial_label (UNKNOWN_LOCATION); + tree end = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (dlist, gimple_build_goto (header)); + gimple_seq_add_stmt (dlist, gimple_build_label (body)); + + gimple_seq_add_seq (dlist, llist[2]); + + g = gimple_build_assign (simt_lane, LSHIFT_EXPR, simt_lane, integer_one_node); + gimple_seq_add_stmt (dlist, g); + + gimple_seq_add_stmt (dlist, gimple_build_label (header)); + g = gimple_build_cond (LT_EXPR, simt_lane, simt_vf, body, end); + gimple_seq_add_stmt (dlist, g); + + gimple_seq_add_stmt (dlist, gimple_build_label (end)); + } + for (int i = 0; i < 2; i++) + if (llist[i]) + { + tree vf = create_tmp_var (unsigned_type_node); + g = gimple_build_call_internal (IFN_GOMP_SIMD_VF, 1, uid); + gimple_call_set_lhs (g, vf); + gimple_seq *seq = i == 0 ? ilist : dlist; + gimple_seq_add_stmt (seq, g); + tree t = build_int_cst (unsigned_type_node, 0); + g = gimple_build_assign (sctx.idx, INTEGER_CST, t); + gimple_seq_add_stmt (seq, g); + tree body = create_artificial_label (UNKNOWN_LOCATION); + tree header = create_artificial_label (UNKNOWN_LOCATION); + tree end = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (seq, gimple_build_goto (header)); + gimple_seq_add_stmt (seq, gimple_build_label (body)); + gimple_seq_add_seq (seq, llist[i]); + t = build_int_cst (unsigned_type_node, 1); + g = gimple_build_assign (sctx.idx, PLUS_EXPR, sctx.idx, t); + gimple_seq_add_stmt (seq, g); + gimple_seq_add_stmt (seq, gimple_build_label (header)); + g = gimple_build_cond (LT_EXPR, sctx.idx, vf, body, end); + gimple_seq_add_stmt (seq, g); + gimple_seq_add_stmt (seq, gimple_build_label (end)); + } + } + if (sctx.is_simt) + { + gimple_seq_add_seq (dlist, sctx.simt_dlist); + gimple *g + = gimple_build_call_internal (IFN_GOMP_SIMT_EXIT, 1, simtrec); + gimple_seq_add_stmt (dlist, g); + } + + /* The copyin sequence is not to be executed by the main thread, since + that would result in self-copies. Perhaps not visible to scalars, + but it certainly is to C++ operator=. */ + if (copyin_seq) + { + x = build_call_expr (builtin_decl_explicit (BUILT_IN_OMP_GET_THREAD_NUM), + 0); + x = build2 (NE_EXPR, boolean_type_node, x, + build_int_cst (TREE_TYPE (x), 0)); + x = build3 (COND_EXPR, void_type_node, x, copyin_seq, NULL); + gimplify_and_add (x, ilist); + } + + /* If any copyin variable is passed by reference, we must ensure the + master thread doesn't modify it before it is copied over in all + threads. Similarly for variables in both firstprivate and + lastprivate clauses we need to ensure the lastprivate copying + happens after firstprivate copying in all threads. And similarly + for UDRs if initializer expression refers to omp_orig. */ + if (copyin_by_ref || lastprivate_firstprivate + || (reduction_omp_orig_ref + && !ctx->scan_inclusive + && !ctx->scan_exclusive)) + { + /* Don't add any barrier for #pragma omp simd or + #pragma omp distribute. */ + if (!is_task_ctx (ctx) + && (gimple_code (ctx->stmt) != GIMPLE_OMP_FOR + || gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_FOR)) + gimple_seq_add_stmt (ilist, omp_build_barrier (NULL_TREE)); + } + + /* If max_vf is non-zero, then we can use only a vectorization factor + up to the max_vf we chose. So stick it into the safelen clause. */ + if (maybe_ne (sctx.max_vf, 0U)) + { + tree c = omp_find_clause (gimple_omp_for_clauses (ctx->stmt), + OMP_CLAUSE_SAFELEN); + poly_uint64 safe_len; + if (c == NULL_TREE + || (poly_int_tree_p (OMP_CLAUSE_SAFELEN_EXPR (c), &safe_len) + && maybe_gt (safe_len, sctx.max_vf))) + { + c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE_SAFELEN); + OMP_CLAUSE_SAFELEN_EXPR (c) = build_int_cst (integer_type_node, + sctx.max_vf); + OMP_CLAUSE_CHAIN (c) = gimple_omp_for_clauses (ctx->stmt); + gimple_omp_for_set_clauses (ctx->stmt, c); + } + } +} + +/* Create temporary variables for lastprivate(conditional:) implementation + in context CTX with CLAUSES. */ + +static void +lower_lastprivate_conditional_clauses (tree *clauses, omp_context *ctx) +{ + tree iter_type = NULL_TREE; + tree cond_ptr = NULL_TREE; + tree iter_var = NULL_TREE; + bool is_simd = (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD); + tree next = *clauses; + for (tree c = *clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c)) + { + if (is_simd) + { + tree cc = omp_find_clause (next, OMP_CLAUSE__CONDTEMP_); + gcc_assert (cc); + if (iter_type == NULL_TREE) + { + iter_type = TREE_TYPE (OMP_CLAUSE_DECL (cc)); + iter_var = create_tmp_var_raw (iter_type); + DECL_CONTEXT (iter_var) = current_function_decl; + DECL_SEEN_IN_BIND_EXPR_P (iter_var) = 1; + DECL_CHAIN (iter_var) = ctx->block_vars; + ctx->block_vars = iter_var; + tree c3 + = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__CONDTEMP_); + OMP_CLAUSE__CONDTEMP__ITER (c3) = 1; + OMP_CLAUSE_DECL (c3) = iter_var; + OMP_CLAUSE_CHAIN (c3) = *clauses; + *clauses = c3; + ctx->lastprivate_conditional_map = new hash_map<tree, tree>; + } + next = OMP_CLAUSE_CHAIN (cc); + tree o = lookup_decl (OMP_CLAUSE_DECL (c), ctx); + tree v = lookup_decl (OMP_CLAUSE_DECL (cc), ctx); + ctx->lastprivate_conditional_map->put (o, v); + continue; + } + if (iter_type == NULL) + { + if (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR) + { + struct omp_for_data fd; + omp_extract_for_data (as_a <gomp_for *> (ctx->stmt), &fd, + NULL); + iter_type = unsigned_type_for (fd.iter_type); + } + else if (gimple_code (ctx->stmt) == GIMPLE_OMP_SECTIONS) + iter_type = unsigned_type_node; + tree c2 = omp_find_clause (*clauses, OMP_CLAUSE__CONDTEMP_); + if (c2) + { + cond_ptr + = lookup_decl_in_outer_ctx (OMP_CLAUSE_DECL (c2), ctx); + OMP_CLAUSE_DECL (c2) = cond_ptr; + } + else + { + cond_ptr = create_tmp_var_raw (build_pointer_type (iter_type)); + DECL_CONTEXT (cond_ptr) = current_function_decl; + DECL_SEEN_IN_BIND_EXPR_P (cond_ptr) = 1; + DECL_CHAIN (cond_ptr) = ctx->block_vars; + ctx->block_vars = cond_ptr; + c2 = build_omp_clause (UNKNOWN_LOCATION, + OMP_CLAUSE__CONDTEMP_); + OMP_CLAUSE_DECL (c2) = cond_ptr; + OMP_CLAUSE_CHAIN (c2) = *clauses; + *clauses = c2; + } + iter_var = create_tmp_var_raw (iter_type); + DECL_CONTEXT (iter_var) = current_function_decl; + DECL_SEEN_IN_BIND_EXPR_P (iter_var) = 1; + DECL_CHAIN (iter_var) = ctx->block_vars; + ctx->block_vars = iter_var; + tree c3 + = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__CONDTEMP_); + OMP_CLAUSE__CONDTEMP__ITER (c3) = 1; + OMP_CLAUSE_DECL (c3) = iter_var; + OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c2); + OMP_CLAUSE_CHAIN (c2) = c3; + ctx->lastprivate_conditional_map = new hash_map<tree, tree>; + } + tree v = create_tmp_var_raw (iter_type); + DECL_CONTEXT (v) = current_function_decl; + DECL_SEEN_IN_BIND_EXPR_P (v) = 1; + DECL_CHAIN (v) = ctx->block_vars; + ctx->block_vars = v; + tree o = lookup_decl (OMP_CLAUSE_DECL (c), ctx); + ctx->lastprivate_conditional_map->put (o, v); + } +} + + +/* Generate code to implement the LASTPRIVATE clauses. This is used for + both parallel and workshare constructs. PREDICATE may be NULL if it's + always true. BODY_P is the sequence to insert early initialization + if needed, STMT_LIST is where the non-conditional lastprivate handling + goes into and CSTMT_LIST is a sequence that needs to be run in a critical + section. */ + +static void +lower_lastprivate_clauses (tree clauses, tree predicate, gimple_seq *body_p, + gimple_seq *stmt_list, gimple_seq *cstmt_list, + omp_context *ctx) +{ + tree x, c, label = NULL, orig_clauses = clauses; + bool par_clauses = false; + tree simduid = NULL, lastlane = NULL, simtcond = NULL, simtlast = NULL; + unsigned HOST_WIDE_INT conditional_off = 0; + gimple_seq post_stmt_list = NULL; + + /* Early exit if there are no lastprivate or linear clauses. */ + for (; clauses ; clauses = OMP_CLAUSE_CHAIN (clauses)) + if (OMP_CLAUSE_CODE (clauses) == OMP_CLAUSE_LASTPRIVATE + || (OMP_CLAUSE_CODE (clauses) == OMP_CLAUSE_LINEAR + && !OMP_CLAUSE_LINEAR_NO_COPYOUT (clauses))) + break; + if (clauses == NULL) + { + /* If this was a workshare clause, see if it had been combined + with its parallel. In that case, look for the clauses on the + parallel statement itself. */ + if (is_parallel_ctx (ctx)) + return; + + ctx = ctx->outer; + if (ctx == NULL || !is_parallel_ctx (ctx)) + return; + + clauses = omp_find_clause (gimple_omp_parallel_clauses (ctx->stmt), + OMP_CLAUSE_LASTPRIVATE); + if (clauses == NULL) + return; + par_clauses = true; + } + + bool maybe_simt = false; + if (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD) + { + maybe_simt = omp_find_clause (orig_clauses, OMP_CLAUSE__SIMT_); + simduid = omp_find_clause (orig_clauses, OMP_CLAUSE__SIMDUID_); + if (simduid) + simduid = OMP_CLAUSE__SIMDUID__DECL (simduid); + } + + if (predicate) + { + gcond *stmt; + tree label_true, arm1, arm2; + enum tree_code pred_code = TREE_CODE (predicate); + + label = create_artificial_label (UNKNOWN_LOCATION); + label_true = create_artificial_label (UNKNOWN_LOCATION); + if (TREE_CODE_CLASS (pred_code) == tcc_comparison) + { + arm1 = TREE_OPERAND (predicate, 0); + arm2 = TREE_OPERAND (predicate, 1); + gimplify_expr (&arm1, stmt_list, NULL, is_gimple_val, fb_rvalue); + gimplify_expr (&arm2, stmt_list, NULL, is_gimple_val, fb_rvalue); + } + else + { + arm1 = predicate; + gimplify_expr (&arm1, stmt_list, NULL, is_gimple_val, fb_rvalue); + arm2 = boolean_false_node; + pred_code = NE_EXPR; + } + if (maybe_simt) + { + c = build2 (pred_code, boolean_type_node, arm1, arm2); + c = fold_convert (integer_type_node, c); + simtcond = create_tmp_var (integer_type_node); + gimplify_assign (simtcond, c, stmt_list); + gcall *g = gimple_build_call_internal (IFN_GOMP_SIMT_VOTE_ANY, + 1, simtcond); + c = create_tmp_var (integer_type_node); + gimple_call_set_lhs (g, c); + gimple_seq_add_stmt (stmt_list, g); + stmt = gimple_build_cond (NE_EXPR, c, integer_zero_node, + label_true, label); + } + else + stmt = gimple_build_cond (pred_code, arm1, arm2, label_true, label); + gimple_seq_add_stmt (stmt_list, stmt); + gimple_seq_add_stmt (stmt_list, gimple_build_label (label_true)); + } + + tree cond_ptr = NULL_TREE; + for (c = clauses; c ;) + { + tree var, new_var; + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + gimple_seq *this_stmt_list = stmt_list; + tree lab2 = NULL_TREE; + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c) + && ctx->lastprivate_conditional_map + && !ctx->combined_into_simd_safelen1) + { + gcc_assert (body_p); + if (simduid) + goto next; + if (cond_ptr == NULL_TREE) + { + cond_ptr = omp_find_clause (orig_clauses, OMP_CLAUSE__CONDTEMP_); + cond_ptr = OMP_CLAUSE_DECL (cond_ptr); + } + tree type = TREE_TYPE (TREE_TYPE (cond_ptr)); + tree o = lookup_decl (OMP_CLAUSE_DECL (c), ctx); + tree v = *ctx->lastprivate_conditional_map->get (o); + gimplify_assign (v, build_zero_cst (type), body_p); + this_stmt_list = cstmt_list; + tree mem; + if (POINTER_TYPE_P (TREE_TYPE (cond_ptr))) + { + mem = build2 (MEM_REF, type, cond_ptr, + build_int_cst (TREE_TYPE (cond_ptr), + conditional_off)); + conditional_off += tree_to_uhwi (TYPE_SIZE_UNIT (type)); + } + else + mem = build4 (ARRAY_REF, type, cond_ptr, + size_int (conditional_off++), NULL_TREE, NULL_TREE); + tree mem2 = copy_node (mem); + gimple_seq seq = NULL; + mem = force_gimple_operand (mem, &seq, true, NULL_TREE); + gimple_seq_add_seq (this_stmt_list, seq); + tree lab1 = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + gimple *g = gimple_build_cond (GT_EXPR, v, mem, lab1, lab2); + gimple_seq_add_stmt (this_stmt_list, g); + gimple_seq_add_stmt (this_stmt_list, gimple_build_label (lab1)); + gimplify_assign (mem2, v, this_stmt_list); + } + else if (predicate + && ctx->combined_into_simd_safelen1 + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c) + && ctx->lastprivate_conditional_map) + this_stmt_list = &post_stmt_list; + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && !OMP_CLAUSE_LINEAR_NO_COPYOUT (c))) + { + var = OMP_CLAUSE_DECL (c); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c) + && is_taskloop_ctx (ctx)) + { + gcc_checking_assert (ctx->outer && is_task_ctx (ctx->outer)); + new_var = lookup_decl (var, ctx->outer); + } + else + { + new_var = lookup_decl (var, ctx); + /* Avoid uninitialized warnings for lastprivate and + for linear iterators. */ + if (predicate + && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + || OMP_CLAUSE_LINEAR_NO_COPYIN (c))) + suppress_warning (new_var, OPT_Wuninitialized); + } + + if (!maybe_simt && simduid && DECL_HAS_VALUE_EXPR_P (new_var)) + { + tree val = DECL_VALUE_EXPR (new_var); + if (TREE_CODE (val) == ARRAY_REF + && VAR_P (TREE_OPERAND (val, 0)) + && lookup_attribute ("omp simd array", + DECL_ATTRIBUTES (TREE_OPERAND (val, + 0)))) + { + if (lastlane == NULL) + { + lastlane = create_tmp_var (unsigned_type_node); + gcall *g + = gimple_build_call_internal (IFN_GOMP_SIMD_LAST_LANE, + 2, simduid, + TREE_OPERAND (val, 1)); + gimple_call_set_lhs (g, lastlane); + gimple_seq_add_stmt (this_stmt_list, g); + } + new_var = build4 (ARRAY_REF, TREE_TYPE (val), + TREE_OPERAND (val, 0), lastlane, + NULL_TREE, NULL_TREE); + TREE_THIS_NOTRAP (new_var) = 1; + } + } + else if (maybe_simt) + { + tree val = (DECL_HAS_VALUE_EXPR_P (new_var) + ? DECL_VALUE_EXPR (new_var) + : new_var); + if (simtlast == NULL) + { + simtlast = create_tmp_var (unsigned_type_node); + gcall *g = gimple_build_call_internal + (IFN_GOMP_SIMT_LAST_LANE, 1, simtcond); + gimple_call_set_lhs (g, simtlast); + gimple_seq_add_stmt (this_stmt_list, g); + } + x = build_call_expr_internal_loc + (UNKNOWN_LOCATION, IFN_GOMP_SIMT_XCHG_IDX, + TREE_TYPE (val), 2, val, simtlast); + new_var = unshare_expr (new_var); + gimplify_assign (new_var, x, this_stmt_list); + new_var = unshare_expr (new_var); + } + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c)) + { + lower_omp (&OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c), ctx); + gimple_seq_add_seq (this_stmt_list, + OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c)); + OMP_CLAUSE_LASTPRIVATE_GIMPLE_SEQ (c) = NULL; + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c)) + { + lower_omp (&OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c), ctx); + gimple_seq_add_seq (this_stmt_list, + OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c)); + OMP_CLAUSE_LINEAR_GIMPLE_SEQ (c) = NULL; + } + + x = NULL_TREE; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_LASTPRIVATE_LOOP_IV (c) + && is_taskloop_ctx (ctx)) + { + tree ovar = maybe_lookup_decl_in_outer_ctx (var, + ctx->outer->outer); + if (is_global_var (ovar)) + x = ovar; + } + if (!x) + x = build_outer_var_ref (var, ctx, OMP_CLAUSE_LASTPRIVATE); + if (omp_privatize_by_reference (var)) + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + x = lang_hooks.decls.omp_clause_assign_op (c, x, new_var); + gimplify_and_add (x, this_stmt_list); + + if (lab2) + gimple_seq_add_stmt (this_stmt_list, gimple_build_label (lab2)); + } + + next: + c = OMP_CLAUSE_CHAIN (c); + if (c == NULL && !par_clauses) + { + /* If this was a workshare clause, see if it had been combined + with its parallel. In that case, continue looking for the + clauses also on the parallel statement itself. */ + if (is_parallel_ctx (ctx)) + break; + + ctx = ctx->outer; + if (ctx == NULL || !is_parallel_ctx (ctx)) + break; + + c = omp_find_clause (gimple_omp_parallel_clauses (ctx->stmt), + OMP_CLAUSE_LASTPRIVATE); + par_clauses = true; + } + } + + if (label) + gimple_seq_add_stmt (stmt_list, gimple_build_label (label)); + gimple_seq_add_seq (stmt_list, post_stmt_list); +} + +/* Lower the OpenACC reductions of CLAUSES for compute axis LEVEL + (which might be a placeholder). INNER is true if this is an inner + axis of a multi-axis loop. FORK and JOIN are (optional) fork and + join markers. Generate the before-loop forking sequence in + FORK_SEQ and the after-loop joining sequence to JOIN_SEQ. The + general form of these sequences is + + GOACC_REDUCTION_SETUP + GOACC_FORK + GOACC_REDUCTION_INIT + ... + GOACC_REDUCTION_FINI + GOACC_JOIN + GOACC_REDUCTION_TEARDOWN. */ + +static void +lower_oacc_reductions (location_t loc, tree clauses, tree level, bool inner, + gcall *fork, gcall *private_marker, gcall *join, + gimple_seq *fork_seq, gimple_seq *join_seq, + omp_context *ctx) +{ + gimple_seq before_fork = NULL; + gimple_seq after_fork = NULL; + gimple_seq before_join = NULL; + gimple_seq after_join = NULL; + tree init_code = NULL_TREE, fini_code = NULL_TREE, + setup_code = NULL_TREE, teardown_code = NULL_TREE; + unsigned offset = 0; + + for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) + { + /* No 'reduction' clauses on OpenACC 'kernels'. */ + gcc_checking_assert (!is_oacc_kernels (ctx)); + /* Likewise, on OpenACC 'kernels' decomposed parts. */ + gcc_checking_assert (!is_oacc_kernels_decomposed_part (ctx)); + + tree orig = OMP_CLAUSE_DECL (c); + tree var = maybe_lookup_decl (orig, ctx); + tree ref_to_res = NULL_TREE; + tree incoming, outgoing, v1, v2, v3; + bool is_private = false; + + enum tree_code rcode = OMP_CLAUSE_REDUCTION_CODE (c); + if (rcode == MINUS_EXPR) + rcode = PLUS_EXPR; + else if (rcode == TRUTH_ANDIF_EXPR) + rcode = BIT_AND_EXPR; + else if (rcode == TRUTH_ORIF_EXPR) + rcode = BIT_IOR_EXPR; + tree op = build_int_cst (unsigned_type_node, rcode); + + if (!var) + var = orig; + + incoming = outgoing = var; + + if (!inner) + { + /* See if an outer construct also reduces this variable. */ + omp_context *outer = ctx; + + while (omp_context *probe = outer->outer) + { + enum gimple_code type = gimple_code (probe->stmt); + tree cls; + + switch (type) + { + case GIMPLE_OMP_FOR: + cls = gimple_omp_for_clauses (probe->stmt); + break; + + case GIMPLE_OMP_TARGET: + /* No 'reduction' clauses inside OpenACC 'kernels' + regions. */ + gcc_checking_assert (!is_oacc_kernels (probe)); + + if (!is_gimple_omp_offloaded (probe->stmt)) + goto do_lookup; + + cls = gimple_omp_target_clauses (probe->stmt); + break; + + default: + goto do_lookup; + } + + outer = probe; + for (; cls; cls = OMP_CLAUSE_CHAIN (cls)) + if (OMP_CLAUSE_CODE (cls) == OMP_CLAUSE_REDUCTION + && orig == OMP_CLAUSE_DECL (cls)) + { + incoming = outgoing = lookup_decl (orig, probe); + goto has_outer_reduction; + } + else if ((OMP_CLAUSE_CODE (cls) == OMP_CLAUSE_FIRSTPRIVATE + || OMP_CLAUSE_CODE (cls) == OMP_CLAUSE_PRIVATE) + && orig == OMP_CLAUSE_DECL (cls)) + { + is_private = true; + goto do_lookup; + } + } + + do_lookup: + /* This is the outermost construct with this reduction, + see if there's a mapping for it. */ + if (gimple_code (outer->stmt) == GIMPLE_OMP_TARGET + && maybe_lookup_field (orig, outer) && !is_private) + { + ref_to_res = build_receiver_ref (orig, false, outer); + if (omp_privatize_by_reference (orig)) + ref_to_res = build_simple_mem_ref (ref_to_res); + + tree type = TREE_TYPE (var); + if (POINTER_TYPE_P (type)) + type = TREE_TYPE (type); + + outgoing = var; + incoming = omp_reduction_init_op (loc, rcode, type); + } + else + { + /* Try to look at enclosing contexts for reduction var, + use original if no mapping found. */ + tree t = NULL_TREE; + omp_context *c = ctx->outer; + while (c && !t) + { + t = maybe_lookup_decl (orig, c); + c = c->outer; + } + incoming = outgoing = (t ? t : orig); + } + + has_outer_reduction:; + } + + if (!ref_to_res) + ref_to_res = integer_zero_node; + + if (omp_privatize_by_reference (orig)) + { + tree type = TREE_TYPE (var); + const char *id = IDENTIFIER_POINTER (DECL_NAME (var)); + + if (!inner) + { + tree x = create_tmp_var (TREE_TYPE (type), id); + gimplify_assign (var, build_fold_addr_expr (x), fork_seq); + } + + v1 = create_tmp_var (type, id); + v2 = create_tmp_var (type, id); + v3 = create_tmp_var (type, id); + + gimplify_assign (v1, var, fork_seq); + gimplify_assign (v2, var, fork_seq); + gimplify_assign (v3, var, fork_seq); + + var = build_simple_mem_ref (var); + v1 = build_simple_mem_ref (v1); + v2 = build_simple_mem_ref (v2); + v3 = build_simple_mem_ref (v3); + outgoing = build_simple_mem_ref (outgoing); + + if (!TREE_CONSTANT (incoming)) + incoming = build_simple_mem_ref (incoming); + } + else + v1 = v2 = v3 = var; + + /* Determine position in reduction buffer, which may be used + by target. The parser has ensured that this is not a + variable-sized type. */ + fixed_size_mode mode + = as_a <fixed_size_mode> (TYPE_MODE (TREE_TYPE (var))); + unsigned align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + offset = (offset + align - 1) & ~(align - 1); + tree off = build_int_cst (sizetype, offset); + offset += GET_MODE_SIZE (mode); + + if (!init_code) + { + init_code = build_int_cst (integer_type_node, + IFN_GOACC_REDUCTION_INIT); + fini_code = build_int_cst (integer_type_node, + IFN_GOACC_REDUCTION_FINI); + setup_code = build_int_cst (integer_type_node, + IFN_GOACC_REDUCTION_SETUP); + teardown_code = build_int_cst (integer_type_node, + IFN_GOACC_REDUCTION_TEARDOWN); + } + + tree setup_call + = build_call_expr_internal_loc (loc, IFN_GOACC_REDUCTION, + TREE_TYPE (var), 6, setup_code, + unshare_expr (ref_to_res), + incoming, level, op, off); + tree init_call + = build_call_expr_internal_loc (loc, IFN_GOACC_REDUCTION, + TREE_TYPE (var), 6, init_code, + unshare_expr (ref_to_res), + v1, level, op, off); + tree fini_call + = build_call_expr_internal_loc (loc, IFN_GOACC_REDUCTION, + TREE_TYPE (var), 6, fini_code, + unshare_expr (ref_to_res), + v2, level, op, off); + tree teardown_call + = build_call_expr_internal_loc (loc, IFN_GOACC_REDUCTION, + TREE_TYPE (var), 6, teardown_code, + ref_to_res, v3, level, op, off); + + gimplify_assign (v1, setup_call, &before_fork); + gimplify_assign (v2, init_call, &after_fork); + gimplify_assign (v3, fini_call, &before_join); + gimplify_assign (outgoing, teardown_call, &after_join); + } + + /* Now stitch things together. */ + gimple_seq_add_seq (fork_seq, before_fork); + if (private_marker) + gimple_seq_add_stmt (fork_seq, private_marker); + if (fork) + gimple_seq_add_stmt (fork_seq, fork); + gimple_seq_add_seq (fork_seq, after_fork); + + gimple_seq_add_seq (join_seq, before_join); + if (join) + gimple_seq_add_stmt (join_seq, join); + gimple_seq_add_seq (join_seq, after_join); +} + +/* Generate code to implement the REDUCTION clauses, append it + to STMT_SEQP. CLIST if non-NULL is a pointer to a sequence + that should be emitted also inside of the critical section, + in that case clear *CLIST afterwards, otherwise leave it as is + and let the caller emit it itself. */ + +static void +lower_reduction_clauses (tree clauses, gimple_seq *stmt_seqp, + gimple_seq *clist, omp_context *ctx) +{ + gimple_seq sub_seq = NULL; + gimple *stmt; + tree x, c; + int count = 0; + + /* OpenACC loop reductions are handled elsewhere. */ + if (is_gimple_omp_oacc (ctx->stmt)) + return; + + /* SIMD reductions are handled in lower_rec_input_clauses. */ + if (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD) + return; + + /* inscan reductions are handled elsewhere. */ + if (ctx->scan_inclusive || ctx->scan_exclusive) + return; + + /* First see if there is exactly one reduction clause. Use OMP_ATOMIC + update in that case, otherwise use a lock. */ + for (c = clauses; c && count < 2; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && !OMP_CLAUSE_REDUCTION_TASK (c)) + { + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) + || TREE_CODE (OMP_CLAUSE_DECL (c)) == MEM_REF) + { + /* Never use OMP_ATOMIC for array reductions or UDRs. */ + count = -1; + break; + } + count++; + } + + if (count == 0) + return; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree var, ref, new_var, orig_var; + enum tree_code code; + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION + || OMP_CLAUSE_REDUCTION_TASK (c)) + continue; + + enum omp_clause_code ccode = OMP_CLAUSE_REDUCTION; + orig_var = var = OMP_CLAUSE_DECL (c); + if (TREE_CODE (var) == MEM_REF) + { + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == ADDR_EXPR) + var = TREE_OPERAND (var, 0); + else + { + /* If this is a pointer or referenced based array + section, the var could be private in the outer + context e.g. on orphaned loop construct. Pretend this + is private variable's outer reference. */ + ccode = OMP_CLAUSE_PRIVATE; + if (TREE_CODE (var) == INDIRECT_REF) + var = TREE_OPERAND (var, 0); + } + orig_var = var; + if (is_variable_sized (var)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (var)); + var = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (var) == INDIRECT_REF); + var = TREE_OPERAND (var, 0); + gcc_assert (DECL_P (var)); + } + } + new_var = lookup_decl (var, ctx); + if (var == OMP_CLAUSE_DECL (c) + && omp_privatize_by_reference (var)) + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + ref = build_outer_var_ref (var, ctx, ccode); + code = OMP_CLAUSE_REDUCTION_CODE (c); + + /* reduction(-:var) sums up the partial results, so it acts + identically to reduction(+:var). */ + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + bool is_truth_op = (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR); + if (count == 1) + { + tree addr = build_fold_addr_expr_loc (clause_loc, ref); + + addr = save_expr (addr); + ref = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (addr)), addr); + tree new_var2 = new_var; + tree ref2 = ref; + if (is_truth_op) + { + tree zero = build_zero_cst (TREE_TYPE (new_var)); + new_var2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, new_var, zero); + ref2 = fold_build2_loc (clause_loc, NE_EXPR, boolean_type_node, + ref, zero); + } + x = fold_build2_loc (clause_loc, code, TREE_TYPE (new_var2), ref2, + new_var2); + if (is_truth_op) + x = fold_convert (TREE_TYPE (new_var), x); + x = build2 (OMP_ATOMIC, void_type_node, addr, x); + OMP_ATOMIC_MEMORY_ORDER (x) = OMP_MEMORY_ORDER_RELAXED; + gimplify_and_add (x, stmt_seqp); + return; + } + else if (TREE_CODE (OMP_CLAUSE_DECL (c)) == MEM_REF) + { + tree d = OMP_CLAUSE_DECL (c); + tree type = TREE_TYPE (d); + tree v = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree i = create_tmp_var (TREE_TYPE (v)); + tree ptype = build_pointer_type (TREE_TYPE (type)); + tree bias = TREE_OPERAND (d, 1); + d = TREE_OPERAND (d, 0); + if (TREE_CODE (d) == POINTER_PLUS_EXPR) + { + tree b = TREE_OPERAND (d, 1); + b = maybe_lookup_decl (b, ctx); + if (b == NULL) + { + b = TREE_OPERAND (d, 1); + b = maybe_lookup_decl_in_outer_ctx (b, ctx); + } + if (integer_zerop (bias)) + bias = b; + else + { + bias = fold_convert_loc (clause_loc, TREE_TYPE (b), bias); + bias = fold_build2_loc (clause_loc, PLUS_EXPR, + TREE_TYPE (b), b, bias); + } + d = TREE_OPERAND (d, 0); + } + /* For ref build_outer_var_ref already performs this, so + only new_var needs a dereference. */ + if (TREE_CODE (d) == INDIRECT_REF) + { + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + gcc_assert (omp_privatize_by_reference (var) + && var == orig_var); + } + else if (TREE_CODE (d) == ADDR_EXPR) + { + if (orig_var == var) + { + new_var = build_fold_addr_expr (new_var); + ref = build_fold_addr_expr (ref); + } + } + else + { + gcc_assert (orig_var == var); + if (omp_privatize_by_reference (var)) + ref = build_fold_addr_expr (ref); + } + if (DECL_P (v)) + { + tree t = maybe_lookup_decl (v, ctx); + if (t) + v = t; + else + v = maybe_lookup_decl_in_outer_ctx (v, ctx); + gimplify_expr (&v, stmt_seqp, NULL, is_gimple_val, fb_rvalue); + } + if (!integer_zerop (bias)) + { + bias = fold_convert_loc (clause_loc, sizetype, bias); + new_var = fold_build2_loc (clause_loc, POINTER_PLUS_EXPR, + TREE_TYPE (new_var), new_var, + unshare_expr (bias)); + ref = fold_build2_loc (clause_loc, POINTER_PLUS_EXPR, + TREE_TYPE (ref), ref, bias); + } + new_var = fold_convert_loc (clause_loc, ptype, new_var); + ref = fold_convert_loc (clause_loc, ptype, ref); + tree m = create_tmp_var (ptype); + gimplify_assign (m, new_var, stmt_seqp); + new_var = m; + m = create_tmp_var (ptype); + gimplify_assign (m, ref, stmt_seqp); + ref = m; + gimplify_assign (i, build_int_cst (TREE_TYPE (v), 0), stmt_seqp); + tree body = create_artificial_label (UNKNOWN_LOCATION); + tree end = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (&sub_seq, gimple_build_label (body)); + tree priv = build_simple_mem_ref_loc (clause_loc, new_var); + tree out = build_simple_mem_ref_loc (clause_loc, ref); + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + tree decl_placeholder + = OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c); + SET_DECL_VALUE_EXPR (placeholder, out); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + SET_DECL_VALUE_EXPR (decl_placeholder, priv); + DECL_HAS_VALUE_EXPR_P (decl_placeholder) = 1; + lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c), ctx); + gimple_seq_add_seq (&sub_seq, + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c)); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL; + OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c) = NULL; + } + else + { + tree out2 = out; + tree priv2 = priv; + if (is_truth_op) + { + tree zero = build_zero_cst (TREE_TYPE (out)); + out2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, out, zero); + priv2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, priv, zero); + } + x = build2 (code, TREE_TYPE (out2), out2, priv2); + if (is_truth_op) + x = fold_convert (TREE_TYPE (out), x); + out = unshare_expr (out); + gimplify_assign (out, x, &sub_seq); + } + gimple *g = gimple_build_assign (new_var, POINTER_PLUS_EXPR, new_var, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (&sub_seq, g); + g = gimple_build_assign (ref, POINTER_PLUS_EXPR, ref, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (&sub_seq, g); + g = gimple_build_assign (i, PLUS_EXPR, i, + build_int_cst (TREE_TYPE (i), 1)); + gimple_seq_add_stmt (&sub_seq, g); + g = gimple_build_cond (LE_EXPR, i, v, body, end); + gimple_seq_add_stmt (&sub_seq, g); + gimple_seq_add_stmt (&sub_seq, gimple_build_label (end)); + } + else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + + if (omp_privatize_by_reference (var) + && !useless_type_conversion_p (TREE_TYPE (placeholder), + TREE_TYPE (ref))) + ref = build_fold_addr_expr_loc (clause_loc, ref); + SET_DECL_VALUE_EXPR (placeholder, ref); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c), ctx); + gimple_seq_add_seq (&sub_seq, OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c)); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL; + } + else + { + tree new_var2 = new_var; + tree ref2 = ref; + if (is_truth_op) + { + tree zero = build_zero_cst (TREE_TYPE (new_var)); + new_var2 = fold_build2_loc (clause_loc, NE_EXPR, + boolean_type_node, new_var, zero); + ref2 = fold_build2_loc (clause_loc, NE_EXPR, boolean_type_node, + ref, zero); + } + x = build2 (code, TREE_TYPE (ref), ref2, new_var2); + if (is_truth_op) + x = fold_convert (TREE_TYPE (new_var), x); + ref = build_outer_var_ref (var, ctx); + gimplify_assign (ref, x, &sub_seq); + } + } + + stmt = gimple_build_call (builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_START), + 0); + gimple_seq_add_stmt (stmt_seqp, stmt); + + gimple_seq_add_seq (stmt_seqp, sub_seq); + + if (clist) + { + gimple_seq_add_seq (stmt_seqp, *clist); + *clist = NULL; + } + + stmt = gimple_build_call (builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_END), + 0); + gimple_seq_add_stmt (stmt_seqp, stmt); +} + + +/* Generate code to implement the COPYPRIVATE clauses. */ + +static void +lower_copyprivate_clauses (tree clauses, gimple_seq *slist, gimple_seq *rlist, + omp_context *ctx) +{ + tree c; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree var, new_var, ref, x; + bool by_ref; + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_COPYPRIVATE) + continue; + + var = OMP_CLAUSE_DECL (c); + by_ref = use_pointer_for_field (var, NULL); + + ref = build_sender_ref (var, ctx); + x = new_var = lookup_decl_in_outer_ctx (var, ctx); + if (by_ref) + { + x = build_fold_addr_expr_loc (clause_loc, new_var); + x = fold_convert_loc (clause_loc, TREE_TYPE (ref), x); + } + gimplify_assign (ref, x, slist); + + ref = build_receiver_ref (var, false, ctx); + if (by_ref) + { + ref = fold_convert_loc (clause_loc, + build_pointer_type (TREE_TYPE (new_var)), + ref); + ref = build_fold_indirect_ref_loc (clause_loc, ref); + } + if (omp_privatize_by_reference (var)) + { + ref = fold_convert_loc (clause_loc, TREE_TYPE (new_var), ref); + ref = build_simple_mem_ref_loc (clause_loc, ref); + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + } + x = lang_hooks.decls.omp_clause_assign_op (c, new_var, ref); + gimplify_and_add (x, rlist); + } +} + + +/* Generate code to implement the clauses, FIRSTPRIVATE, COPYIN, LASTPRIVATE, + and REDUCTION from the sender (aka parent) side. */ + +static void +lower_send_clauses (tree clauses, gimple_seq *ilist, gimple_seq *olist, + omp_context *ctx) +{ + tree c, t; + int ignored_looptemp = 0; + bool is_taskloop = false; + + /* For taskloop, ignore first two _looptemp_ clauses, those are initialized + by GOMP_taskloop. */ + if (is_task_ctx (ctx) && gimple_omp_task_taskloop_p (ctx->stmt)) + { + ignored_looptemp = 2; + is_taskloop = true; + } + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree val, ref, x, var; + bool by_ref, do_in = false, do_out = false; + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + if (OMP_CLAUSE_PRIVATE_OUTER_REF (c)) + break; + continue; + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_IN_REDUCTION: + case OMP_CLAUSE__REDUCTEMP_: + break; + case OMP_CLAUSE_REDUCTION: + if (is_task_ctx (ctx) || OMP_CLAUSE_REDUCTION_TASK (c)) + continue; + break; + case OMP_CLAUSE_SHARED: + if (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + break; + continue; + case OMP_CLAUSE__LOOPTEMP_: + if (ignored_looptemp) + { + ignored_looptemp--; + continue; + } + break; + default: + continue; + } + + val = OMP_CLAUSE_DECL (c); + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) + && TREE_CODE (val) == MEM_REF) + { + val = TREE_OPERAND (val, 0); + if (TREE_CODE (val) == POINTER_PLUS_EXPR) + val = TREE_OPERAND (val, 0); + if (TREE_CODE (val) == INDIRECT_REF + || TREE_CODE (val) == ADDR_EXPR) + val = TREE_OPERAND (val, 0); + if (is_variable_sized (val)) + continue; + } + + /* For OMP_CLAUSE_SHARED_FIRSTPRIVATE, look beyond the + outer taskloop region. */ + omp_context *ctx_for_o = ctx; + if (is_taskloop + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED + && OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + ctx_for_o = ctx->outer; + + var = lookup_decl_in_outer_ctx (val, ctx_for_o); + + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_COPYIN + && is_global_var (var) + && (val == OMP_CLAUSE_DECL (c) + || !is_task_ctx (ctx) + || (TREE_CODE (TREE_TYPE (val)) != POINTER_TYPE + && (TREE_CODE (TREE_TYPE (val)) != REFERENCE_TYPE + || (TREE_CODE (TREE_TYPE (TREE_TYPE (val))) + != POINTER_TYPE))))) + continue; + + t = omp_member_access_dummy_var (var); + if (t) + { + var = DECL_VALUE_EXPR (var); + tree o = maybe_lookup_decl_in_outer_ctx (t, ctx_for_o); + if (o != t) + var = unshare_and_remap (var, t, o); + else + var = unshare_expr (var); + } + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED) + { + /* Handle taskloop firstprivate/lastprivate, where the + lastprivate on GIMPLE_OMP_TASK is represented as + OMP_CLAUSE_SHARED_FIRSTPRIVATE. */ + tree f = lookup_sfield ((splay_tree_key) &DECL_UID (val), ctx); + x = omp_build_component_ref (ctx->sender_decl, f); + if (use_pointer_for_field (val, ctx)) + var = build_fold_addr_expr (var); + gimplify_assign (x, var, ilist); + DECL_ABSTRACT_ORIGIN (f) = NULL; + continue; + } + + if (((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IN_REDUCTION) + || val == OMP_CLAUSE_DECL (c)) + && is_variable_sized (val)) + continue; + by_ref = use_pointer_for_field (val, NULL); + + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_FIRSTPRIVATE: + if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c) + && !by_ref + && is_task_ctx (ctx)) + suppress_warning (var); + do_in = true; + break; + + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE__LOOPTEMP_: + case OMP_CLAUSE__REDUCTEMP_: + do_in = true; + break; + + case OMP_CLAUSE_LASTPRIVATE: + if (by_ref || omp_privatize_by_reference (val)) + { + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + continue; + do_in = true; + } + else + { + do_out = true; + if (lang_hooks.decls.omp_private_outer_ref (val)) + do_in = true; + } + break; + + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + do_in = true; + if (val == OMP_CLAUSE_DECL (c)) + { + if (is_task_ctx (ctx)) + by_ref = use_pointer_for_field (val, ctx); + else + do_out = !(by_ref || omp_privatize_by_reference (val)); + } + else + by_ref = TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE; + break; + + default: + gcc_unreachable (); + } + + if (do_in) + { + ref = build_sender_ref (val, ctx); + x = by_ref ? build_fold_addr_expr_loc (clause_loc, var) : var; + gimplify_assign (ref, x, ilist); + if (is_task_ctx (ctx)) + DECL_ABSTRACT_ORIGIN (TREE_OPERAND (ref, 1)) = NULL; + } + + if (do_out) + { + ref = build_sender_ref (val, ctx); + gimplify_assign (var, ref, olist); + } + } +} + +/* Generate code to implement SHARED from the sender (aka parent) + side. This is trickier, since GIMPLE_OMP_PARALLEL_CLAUSES doesn't + list things that got automatically shared. */ + +static void +lower_send_shared_vars (gimple_seq *ilist, gimple_seq *olist, omp_context *ctx) +{ + tree var, ovar, nvar, t, f, x, record_type; + + if (ctx->record_type == NULL) + return; + + record_type = ctx->srecord_type ? ctx->srecord_type : ctx->record_type; + for (f = TYPE_FIELDS (record_type); f ; f = DECL_CHAIN (f)) + { + ovar = DECL_ABSTRACT_ORIGIN (f); + if (!ovar || TREE_CODE (ovar) == FIELD_DECL) + continue; + + nvar = maybe_lookup_decl (ovar, ctx); + if (!nvar + || !DECL_HAS_VALUE_EXPR_P (nvar) + || (ctx->allocate_map + && ctx->allocate_map->get (ovar))) + continue; + + /* If CTX is a nested parallel directive. Find the immediately + enclosing parallel or workshare construct that contains a + mapping for OVAR. */ + var = lookup_decl_in_outer_ctx (ovar, ctx); + + t = omp_member_access_dummy_var (var); + if (t) + { + var = DECL_VALUE_EXPR (var); + tree o = maybe_lookup_decl_in_outer_ctx (t, ctx); + if (o != t) + var = unshare_and_remap (var, t, o); + else + var = unshare_expr (var); + } + + if (use_pointer_for_field (ovar, ctx)) + { + x = build_sender_ref (ovar, ctx); + if (TREE_CODE (TREE_TYPE (f)) == ARRAY_TYPE + && TREE_TYPE (f) == TREE_TYPE (ovar)) + { + gcc_assert (is_parallel_ctx (ctx) + && DECL_ARTIFICIAL (ovar)); + /* _condtemp_ clause. */ + var = build_constructor (TREE_TYPE (x), NULL); + } + else + var = build_fold_addr_expr (var); + gimplify_assign (x, var, ilist); + } + else + { + x = build_sender_ref (ovar, ctx); + gimplify_assign (x, var, ilist); + + if (!TREE_READONLY (var) + /* We don't need to receive a new reference to a result + or parm decl. In fact we may not store to it as we will + invalidate any pending RSO and generate wrong gimple + during inlining. */ + && !((TREE_CODE (var) == RESULT_DECL + || TREE_CODE (var) == PARM_DECL) + && DECL_BY_REFERENCE (var))) + { + x = build_sender_ref (ovar, ctx); + gimplify_assign (var, x, olist); + } + } + } +} + +/* Emit an OpenACC head marker call, encapulating the partitioning and + other information that must be processed by the target compiler. + Return the maximum number of dimensions the associated loop might + be partitioned over. */ + +static unsigned +lower_oacc_head_mark (location_t loc, tree ddvar, tree clauses, + gimple_seq *seq, omp_context *ctx) +{ + unsigned levels = 0; + unsigned tag = 0; + tree gang_static = NULL_TREE; + auto_vec<tree, 5> args; + + args.quick_push (build_int_cst + (integer_type_node, IFN_UNIQUE_OACC_HEAD_MARK)); + args.quick_push (ddvar); + for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_GANG: + tag |= OLF_DIM_GANG; + gang_static = OMP_CLAUSE_GANG_STATIC_EXPR (c); + /* static:* is represented by -1, and we can ignore it, as + scheduling is always static. */ + if (gang_static && integer_minus_onep (gang_static)) + gang_static = NULL_TREE; + levels++; + break; + + case OMP_CLAUSE_WORKER: + tag |= OLF_DIM_WORKER; + levels++; + break; + + case OMP_CLAUSE_VECTOR: + tag |= OLF_DIM_VECTOR; + levels++; + break; + + case OMP_CLAUSE_SEQ: + tag |= OLF_SEQ; + break; + + case OMP_CLAUSE_AUTO: + tag |= OLF_AUTO; + break; + + case OMP_CLAUSE_INDEPENDENT: + tag |= OLF_INDEPENDENT; + break; + + case OMP_CLAUSE_TILE: + tag |= OLF_TILE; + break; + + case OMP_CLAUSE_REDUCTION: + tag |= OLF_REDUCTION; + break; + + default: + continue; + } + } + + if (gang_static) + { + if (DECL_P (gang_static)) + gang_static = build_outer_var_ref (gang_static, ctx); + tag |= OLF_GANG_STATIC; + } + + omp_context *tgt = enclosing_target_ctx (ctx); + if (!tgt || is_oacc_parallel_or_serial (tgt)) + ; + else if (is_oacc_kernels (tgt)) + /* Not using this loops handling inside OpenACC 'kernels' regions. */ + gcc_unreachable (); + else if (is_oacc_kernels_decomposed_part (tgt)) + ; + else + gcc_unreachable (); + + /* In a parallel region, loops are implicitly INDEPENDENT. */ + if (!tgt || is_oacc_parallel_or_serial (tgt)) + tag |= OLF_INDEPENDENT; + + /* Loops inside OpenACC 'kernels' decomposed parts' regions are expected to + have an explicit 'seq' or 'independent' clause, and no 'auto' clause. */ + if (tgt && is_oacc_kernels_decomposed_part (tgt)) + { + gcc_assert (tag & (OLF_SEQ | OLF_INDEPENDENT)); + gcc_assert (!(tag & OLF_AUTO)); + } + + if (tag & OLF_TILE) + /* Tiling could use all 3 levels. */ + levels = 3; + else + { + /* A loop lacking SEQ, GANG, WORKER and/or VECTOR could be AUTO. + Ensure at least one level, or 2 for possible auto + partitioning */ + bool maybe_auto = !(tag & (((GOMP_DIM_MASK (GOMP_DIM_MAX) - 1) + << OLF_DIM_BASE) | OLF_SEQ)); + + if (levels < 1u + maybe_auto) + levels = 1u + maybe_auto; + } + + args.quick_push (build_int_cst (integer_type_node, levels)); + args.quick_push (build_int_cst (integer_type_node, tag)); + if (gang_static) + args.quick_push (gang_static); + + gcall *call = gimple_build_call_internal_vec (IFN_UNIQUE, args); + gimple_set_location (call, loc); + gimple_set_lhs (call, ddvar); + gimple_seq_add_stmt (seq, call); + + return levels; +} + +/* Emit an OpenACC lopp head or tail marker to SEQ. LEVEL is the + partitioning level of the enclosed region. */ + +static void +lower_oacc_loop_marker (location_t loc, tree ddvar, bool head, + tree tofollow, gimple_seq *seq) +{ + int marker_kind = (head ? IFN_UNIQUE_OACC_HEAD_MARK + : IFN_UNIQUE_OACC_TAIL_MARK); + tree marker = build_int_cst (integer_type_node, marker_kind); + int nargs = 2 + (tofollow != NULL_TREE); + gcall *call = gimple_build_call_internal (IFN_UNIQUE, nargs, + marker, ddvar, tofollow); + gimple_set_location (call, loc); + gimple_set_lhs (call, ddvar); + gimple_seq_add_stmt (seq, call); +} + +/* Generate the before and after OpenACC loop sequences. CLAUSES are + the loop clauses, from which we extract reductions. Initialize + HEAD and TAIL. */ + +static void +lower_oacc_head_tail (location_t loc, tree clauses, gcall *private_marker, + gimple_seq *head, gimple_seq *tail, omp_context *ctx) +{ + bool inner = false; + tree ddvar = create_tmp_var (integer_type_node, ".data_dep"); + gimple_seq_add_stmt (head, gimple_build_assign (ddvar, integer_zero_node)); + + unsigned count = lower_oacc_head_mark (loc, ddvar, clauses, head, ctx); + + if (private_marker) + { + gimple_set_location (private_marker, loc); + gimple_call_set_lhs (private_marker, ddvar); + gimple_call_set_arg (private_marker, 1, ddvar); + } + + tree fork_kind = build_int_cst (unsigned_type_node, IFN_UNIQUE_OACC_FORK); + tree join_kind = build_int_cst (unsigned_type_node, IFN_UNIQUE_OACC_JOIN); + + gcc_assert (count); + for (unsigned done = 1; count; count--, done++) + { + gimple_seq fork_seq = NULL; + gimple_seq join_seq = NULL; + + tree place = build_int_cst (integer_type_node, -1); + gcall *fork = gimple_build_call_internal (IFN_UNIQUE, 3, + fork_kind, ddvar, place); + gimple_set_location (fork, loc); + gimple_set_lhs (fork, ddvar); + + gcall *join = gimple_build_call_internal (IFN_UNIQUE, 3, + join_kind, ddvar, place); + gimple_set_location (join, loc); + gimple_set_lhs (join, ddvar); + + /* Mark the beginning of this level sequence. */ + if (inner) + lower_oacc_loop_marker (loc, ddvar, true, + build_int_cst (integer_type_node, count), + &fork_seq); + lower_oacc_loop_marker (loc, ddvar, false, + build_int_cst (integer_type_node, done), + &join_seq); + + lower_oacc_reductions (loc, clauses, place, inner, + fork, (count == 1) ? private_marker : NULL, + join, &fork_seq, &join_seq, ctx); + + /* Append this level to head. */ + gimple_seq_add_seq (head, fork_seq); + /* Prepend it to tail. */ + gimple_seq_add_seq (&join_seq, *tail); + *tail = join_seq; + + inner = true; + } + + /* Mark the end of the sequence. */ + lower_oacc_loop_marker (loc, ddvar, true, NULL_TREE, head); + lower_oacc_loop_marker (loc, ddvar, false, NULL_TREE, tail); +} + +/* If exceptions are enabled, wrap the statements in BODY in a MUST_NOT_THROW + catch handler and return it. This prevents programs from violating the + structured block semantics with throws. */ + +static gimple_seq +maybe_catch_exception (gimple_seq body) +{ + gimple *g; + tree decl; + + if (!flag_exceptions) + return body; + + if (lang_hooks.eh_protect_cleanup_actions != NULL) + decl = lang_hooks.eh_protect_cleanup_actions (); + else + decl = builtin_decl_explicit (BUILT_IN_TRAP); + + g = gimple_build_eh_must_not_throw (decl); + g = gimple_build_try (body, gimple_seq_alloc_with_stmt (g), + GIMPLE_TRY_CATCH); + + return gimple_seq_alloc_with_stmt (g); +} + + +/* Routines to lower OMP directives into OMP-GIMPLE. */ + +/* If ctx is a worksharing context inside of a cancellable parallel + region and it isn't nowait, add lhs to its GIMPLE_OMP_RETURN + and conditional branch to parallel's cancel_label to handle + cancellation in the implicit barrier. */ + +static void +maybe_add_implicit_barrier_cancel (omp_context *ctx, gimple *omp_return, + gimple_seq *body) +{ + gcc_assert (gimple_code (omp_return) == GIMPLE_OMP_RETURN); + if (gimple_omp_return_nowait_p (omp_return)) + return; + for (omp_context *outer = ctx->outer; outer; outer = outer->outer) + if (gimple_code (outer->stmt) == GIMPLE_OMP_PARALLEL + && outer->cancellable) + { + tree fndecl = builtin_decl_explicit (BUILT_IN_GOMP_CANCEL); + tree c_bool_type = TREE_TYPE (TREE_TYPE (fndecl)); + tree lhs = create_tmp_var (c_bool_type); + gimple_omp_return_set_lhs (omp_return, lhs); + tree fallthru_label = create_artificial_label (UNKNOWN_LOCATION); + gimple *g = gimple_build_cond (NE_EXPR, lhs, + fold_convert (c_bool_type, + boolean_false_node), + outer->cancel_label, fallthru_label); + gimple_seq_add_stmt (body, g); + gimple_seq_add_stmt (body, gimple_build_label (fallthru_label)); + } + else if (gimple_code (outer->stmt) != GIMPLE_OMP_TASKGROUP + && gimple_code (outer->stmt) != GIMPLE_OMP_SCOPE) + return; +} + +/* Find the first task_reduction or reduction clause or return NULL + if there are none. */ + +static inline tree +omp_task_reductions_find_first (tree clauses, enum tree_code code, + enum omp_clause_code ccode) +{ + while (1) + { + clauses = omp_find_clause (clauses, ccode); + if (clauses == NULL_TREE) + return NULL_TREE; + if (ccode != OMP_CLAUSE_REDUCTION + || code == OMP_TASKLOOP + || OMP_CLAUSE_REDUCTION_TASK (clauses)) + return clauses; + clauses = OMP_CLAUSE_CHAIN (clauses); + } +} + +static void lower_omp_task_reductions (omp_context *, enum tree_code, tree, + gimple_seq *, gimple_seq *); + +/* Lower the OpenMP sections directive in the current statement in GSI_P. + CTX is the enclosing OMP context for the current statement. */ + +static void +lower_omp_sections (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block, control; + gimple_stmt_iterator tgsi; + gomp_sections *stmt; + gimple *t; + gbind *new_stmt, *bind; + gimple_seq ilist, dlist, olist, tred_dlist = NULL, clist = NULL, new_body; + + stmt = as_a <gomp_sections *> (gsi_stmt (*gsi_p)); + + push_gimplify_context (); + + dlist = NULL; + ilist = NULL; + + tree rclauses + = omp_task_reductions_find_first (gimple_omp_sections_clauses (stmt), + OMP_SECTIONS, OMP_CLAUSE_REDUCTION); + tree rtmp = NULL_TREE; + if (rclauses) + { + tree type = build_pointer_type (pointer_sized_int_node); + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__REDUCTEMP_); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_sections_clauses (stmt); + gimple_omp_sections_set_clauses (stmt, c); + lower_omp_task_reductions (ctx, OMP_SECTIONS, + gimple_omp_sections_clauses (stmt), + &ilist, &tred_dlist); + rclauses = c; + rtmp = make_ssa_name (type); + gimple_seq_add_stmt (&ilist, gimple_build_assign (rtmp, temp)); + } + + tree *clauses_ptr = gimple_omp_sections_clauses_ptr (stmt); + lower_lastprivate_conditional_clauses (clauses_ptr, ctx); + + lower_rec_input_clauses (gimple_omp_sections_clauses (stmt), + &ilist, &dlist, ctx, NULL); + + control = create_tmp_var (unsigned_type_node, ".section"); + gimple_omp_sections_set_control (stmt, control); + + new_body = gimple_omp_body (stmt); + gimple_omp_set_body (stmt, NULL); + tgsi = gsi_start (new_body); + for (; !gsi_end_p (tgsi); gsi_next (&tgsi)) + { + omp_context *sctx; + gimple *sec_start; + + sec_start = gsi_stmt (tgsi); + sctx = maybe_lookup_ctx (sec_start); + gcc_assert (sctx); + + lower_omp (gimple_omp_body_ptr (sec_start), sctx); + gsi_insert_seq_after (&tgsi, gimple_omp_body (sec_start), + GSI_CONTINUE_LINKING); + gimple_omp_set_body (sec_start, NULL); + + if (gsi_one_before_end_p (tgsi)) + { + gimple_seq l = NULL; + lower_lastprivate_clauses (gimple_omp_sections_clauses (stmt), NULL, + &ilist, &l, &clist, ctx); + gsi_insert_seq_after (&tgsi, l, GSI_CONTINUE_LINKING); + gimple_omp_section_set_last (sec_start); + } + + gsi_insert_after (&tgsi, gimple_build_omp_return (false), + GSI_CONTINUE_LINKING); + } + + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, new_body, block); + + olist = NULL; + lower_reduction_clauses (gimple_omp_sections_clauses (stmt), &olist, + &clist, ctx); + if (clist) + { + tree fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_START); + gcall *g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&olist, g); + gimple_seq_add_seq (&olist, clist); + fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_END); + g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&olist, g); + } + + block = make_node (BLOCK); + new_stmt = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, new_stmt, true); + + pop_gimplify_context (new_stmt); + gimple_bind_append_vars (new_stmt, ctx->block_vars); + BLOCK_VARS (block) = gimple_bind_vars (bind); + if (BLOCK_VARS (block)) + TREE_USED (block) = 1; + + new_body = NULL; + gimple_seq_add_seq (&new_body, ilist); + gimple_seq_add_stmt (&new_body, stmt); + gimple_seq_add_stmt (&new_body, gimple_build_omp_sections_switch ()); + gimple_seq_add_stmt (&new_body, bind); + + t = gimple_build_omp_continue (control, control); + gimple_seq_add_stmt (&new_body, t); + + gimple_seq_add_seq (&new_body, olist); + if (ctx->cancellable) + gimple_seq_add_stmt (&new_body, gimple_build_label (ctx->cancel_label)); + gimple_seq_add_seq (&new_body, dlist); + + new_body = maybe_catch_exception (new_body); + + bool nowait = omp_find_clause (gimple_omp_sections_clauses (stmt), + OMP_CLAUSE_NOWAIT) != NULL_TREE; + t = gimple_build_omp_return (nowait); + gimple_seq_add_stmt (&new_body, t); + gimple_seq_add_seq (&new_body, tred_dlist); + maybe_add_implicit_barrier_cancel (ctx, t, &new_body); + + if (rclauses) + OMP_CLAUSE_DECL (rclauses) = rtmp; + + gimple_bind_set_body (new_stmt, new_body); +} + + +/* A subroutine of lower_omp_single. Expand the simple form of + a GIMPLE_OMP_SINGLE, without a copyprivate clause: + + if (GOMP_single_start ()) + BODY; + [ GOMP_barrier (); ] -> unless 'nowait' is present. + + FIXME. It may be better to delay expanding the logic of this until + pass_expand_omp. The expanded logic may make the job more difficult + to a synchronization analysis pass. */ + +static void +lower_omp_single_simple (gomp_single *single_stmt, gimple_seq *pre_p) +{ + location_t loc = gimple_location (single_stmt); + tree tlabel = create_artificial_label (loc); + tree flabel = create_artificial_label (loc); + gimple *call, *cond; + tree lhs, decl; + + decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_START); + lhs = create_tmp_var (TREE_TYPE (TREE_TYPE (decl))); + call = gimple_build_call (decl, 0); + gimple_call_set_lhs (call, lhs); + gimple_seq_add_stmt (pre_p, call); + + cond = gimple_build_cond (EQ_EXPR, lhs, + fold_convert_loc (loc, TREE_TYPE (lhs), + boolean_true_node), + tlabel, flabel); + gimple_seq_add_stmt (pre_p, cond); + gimple_seq_add_stmt (pre_p, gimple_build_label (tlabel)); + gimple_seq_add_seq (pre_p, gimple_omp_body (single_stmt)); + gimple_seq_add_stmt (pre_p, gimple_build_label (flabel)); +} + + +/* A subroutine of lower_omp_single. Expand the simple form of + a GIMPLE_OMP_SINGLE, with a copyprivate clause: + + #pragma omp single copyprivate (a, b, c) + + Create a new structure to hold copies of 'a', 'b' and 'c' and emit: + + { + if ((copyout_p = GOMP_single_copy_start ()) == NULL) + { + BODY; + copyout.a = a; + copyout.b = b; + copyout.c = c; + GOMP_single_copy_end (©out); + } + else + { + a = copyout_p->a; + b = copyout_p->b; + c = copyout_p->c; + } + GOMP_barrier (); + } + + FIXME. It may be better to delay expanding the logic of this until + pass_expand_omp. The expanded logic may make the job more difficult + to a synchronization analysis pass. */ + +static void +lower_omp_single_copy (gomp_single *single_stmt, gimple_seq *pre_p, + omp_context *ctx) +{ + tree ptr_type, t, l0, l1, l2, bfn_decl; + gimple_seq copyin_seq; + location_t loc = gimple_location (single_stmt); + + ctx->sender_decl = create_tmp_var (ctx->record_type, ".omp_copy_o"); + + ptr_type = build_pointer_type (ctx->record_type); + ctx->receiver_decl = create_tmp_var (ptr_type, ".omp_copy_i"); + + l0 = create_artificial_label (loc); + l1 = create_artificial_label (loc); + l2 = create_artificial_label (loc); + + bfn_decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_COPY_START); + t = build_call_expr_loc (loc, bfn_decl, 0); + t = fold_convert_loc (loc, ptr_type, t); + gimplify_assign (ctx->receiver_decl, t, pre_p); + + t = build2 (EQ_EXPR, boolean_type_node, ctx->receiver_decl, + build_int_cst (ptr_type, 0)); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l0), build_and_jump (&l1)); + gimplify_and_add (t, pre_p); + + gimple_seq_add_stmt (pre_p, gimple_build_label (l0)); + + gimple_seq_add_seq (pre_p, gimple_omp_body (single_stmt)); + + copyin_seq = NULL; + lower_copyprivate_clauses (gimple_omp_single_clauses (single_stmt), pre_p, + ©in_seq, ctx); + + t = build_fold_addr_expr_loc (loc, ctx->sender_decl); + bfn_decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_COPY_END); + t = build_call_expr_loc (loc, bfn_decl, 1, t); + gimplify_and_add (t, pre_p); + + t = build_and_jump (&l2); + gimplify_and_add (t, pre_p); + + gimple_seq_add_stmt (pre_p, gimple_build_label (l1)); + + gimple_seq_add_seq (pre_p, copyin_seq); + + gimple_seq_add_stmt (pre_p, gimple_build_label (l2)); +} + + +/* Expand code for an OpenMP single directive. */ + +static void +lower_omp_single (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block; + gomp_single *single_stmt = as_a <gomp_single *> (gsi_stmt (*gsi_p)); + gbind *bind; + gimple_seq bind_body, bind_body_tail = NULL, dlist; + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + bind_body = NULL; + dlist = NULL; + lower_rec_input_clauses (gimple_omp_single_clauses (single_stmt), + &bind_body, &dlist, ctx, NULL); + lower_omp (gimple_omp_body_ptr (single_stmt), ctx); + + gimple_seq_add_stmt (&bind_body, single_stmt); + + if (ctx->record_type) + lower_omp_single_copy (single_stmt, &bind_body, ctx); + else + lower_omp_single_simple (single_stmt, &bind_body); + + gimple_omp_set_body (single_stmt, NULL); + + gimple_seq_add_seq (&bind_body, dlist); + + bind_body = maybe_catch_exception (bind_body); + + bool nowait = omp_find_clause (gimple_omp_single_clauses (single_stmt), + OMP_CLAUSE_NOWAIT) != NULL_TREE; + gimple *g = gimple_build_omp_return (nowait); + gimple_seq_add_stmt (&bind_body_tail, g); + maybe_add_implicit_barrier_cancel (ctx, g, &bind_body_tail); + if (ctx->record_type) + { + gimple_stmt_iterator gsi = gsi_start (bind_body_tail); + tree clobber = build_clobber (ctx->record_type); + gsi_insert_after (&gsi, gimple_build_assign (ctx->sender_decl, + clobber), GSI_SAME_STMT); + } + gimple_seq_add_seq (&bind_body, bind_body_tail); + gimple_bind_set_body (bind, bind_body); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = ctx->block_vars; + if (BLOCK_VARS (block)) + TREE_USED (block) = 1; +} + + +/* Lower code for an OMP scope directive. */ + +static void +lower_omp_scope (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block; + gimple *scope_stmt = gsi_stmt (*gsi_p); + gbind *bind; + gimple_seq bind_body, bind_body_tail = NULL, dlist; + gimple_seq tred_dlist = NULL; + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + bind_body = NULL; + dlist = NULL; + + tree rclauses + = omp_task_reductions_find_first (gimple_omp_scope_clauses (scope_stmt), + OMP_SCOPE, OMP_CLAUSE_REDUCTION); + if (rclauses) + { + tree type = build_pointer_type (pointer_sized_int_node); + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__REDUCTEMP_); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_scope_clauses (scope_stmt); + gimple_omp_scope_set_clauses (scope_stmt, c); + lower_omp_task_reductions (ctx, OMP_SCOPE, + gimple_omp_scope_clauses (scope_stmt), + &bind_body, &tred_dlist); + rclauses = c; + tree fndecl = builtin_decl_explicit (BUILT_IN_GOMP_SCOPE_START); + gimple *stmt = gimple_build_call (fndecl, 1, temp); + gimple_seq_add_stmt (&bind_body, stmt); + } + + lower_rec_input_clauses (gimple_omp_scope_clauses (scope_stmt), + &bind_body, &dlist, ctx, NULL); + lower_omp (gimple_omp_body_ptr (scope_stmt), ctx); + + gimple_seq_add_stmt (&bind_body, scope_stmt); + + gimple_seq_add_seq (&bind_body, gimple_omp_body (scope_stmt)); + + gimple_omp_set_body (scope_stmt, NULL); + + gimple_seq clist = NULL; + lower_reduction_clauses (gimple_omp_scope_clauses (scope_stmt), + &bind_body, &clist, ctx); + if (clist) + { + tree fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_START); + gcall *g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&bind_body, g); + gimple_seq_add_seq (&bind_body, clist); + fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_END); + g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&bind_body, g); + } + + gimple_seq_add_seq (&bind_body, dlist); + + bind_body = maybe_catch_exception (bind_body); + + bool nowait = omp_find_clause (gimple_omp_scope_clauses (scope_stmt), + OMP_CLAUSE_NOWAIT) != NULL_TREE; + gimple *g = gimple_build_omp_return (nowait); + gimple_seq_add_stmt (&bind_body_tail, g); + gimple_seq_add_seq (&bind_body_tail, tred_dlist); + maybe_add_implicit_barrier_cancel (ctx, g, &bind_body_tail); + if (ctx->record_type) + { + gimple_stmt_iterator gsi = gsi_start (bind_body_tail); + tree clobber = build_clobber (ctx->record_type); + gsi_insert_after (&gsi, gimple_build_assign (ctx->sender_decl, + clobber), GSI_SAME_STMT); + } + gimple_seq_add_seq (&bind_body, bind_body_tail); + + gimple_bind_set_body (bind, bind_body); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = ctx->block_vars; + if (BLOCK_VARS (block)) + TREE_USED (block) = 1; +} +/* Expand code for an OpenMP master or masked directive. */ + +static void +lower_omp_master (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block, lab = NULL, x, bfn_decl; + gimple *stmt = gsi_stmt (*gsi_p); + gbind *bind; + location_t loc = gimple_location (stmt); + gimple_seq tseq; + tree filter = integer_zero_node; + + push_gimplify_context (); + + if (gimple_code (stmt) == GIMPLE_OMP_MASKED) + { + filter = omp_find_clause (gimple_omp_masked_clauses (stmt), + OMP_CLAUSE_FILTER); + if (filter) + filter = fold_convert (integer_type_node, + OMP_CLAUSE_FILTER_EXPR (filter)); + else + filter = integer_zero_node; + } + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + gimple_bind_add_stmt (bind, stmt); + + bfn_decl = builtin_decl_explicit (BUILT_IN_OMP_GET_THREAD_NUM); + x = build_call_expr_loc (loc, bfn_decl, 0); + x = build2 (EQ_EXPR, boolean_type_node, x, filter); + x = build3 (COND_EXPR, void_type_node, x, NULL, build_and_jump (&lab)); + tseq = NULL; + gimplify_and_add (x, &tseq); + gimple_bind_add_seq (bind, tseq); + + lower_omp (gimple_omp_body_ptr (stmt), ctx); + gimple_omp_set_body (stmt, maybe_catch_exception (gimple_omp_body (stmt))); + gimple_bind_add_seq (bind, gimple_omp_body (stmt)); + gimple_omp_set_body (stmt, NULL); + + gimple_bind_add_stmt (bind, gimple_build_label (lab)); + + gimple_bind_add_stmt (bind, gimple_build_omp_return (true)); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = ctx->block_vars; +} + +/* Helper function for lower_omp_task_reductions. For a specific PASS + find out the current clause it should be processed, or return false + if all have been processed already. */ + +static inline bool +omp_task_reduction_iterate (int pass, enum tree_code code, + enum omp_clause_code ccode, tree *c, tree *decl, + tree *type, tree *next) +{ + for (; *c; *c = omp_find_clause (OMP_CLAUSE_CHAIN (*c), ccode)) + { + if (ccode == OMP_CLAUSE_REDUCTION + && code != OMP_TASKLOOP + && !OMP_CLAUSE_REDUCTION_TASK (*c)) + continue; + *decl = OMP_CLAUSE_DECL (*c); + *type = TREE_TYPE (*decl); + if (TREE_CODE (*decl) == MEM_REF) + { + if (pass != 1) + continue; + } + else + { + if (omp_privatize_by_reference (*decl)) + *type = TREE_TYPE (*type); + if (pass != (!TREE_CONSTANT (TYPE_SIZE_UNIT (*type)))) + continue; + } + *next = omp_find_clause (OMP_CLAUSE_CHAIN (*c), ccode); + return true; + } + *decl = NULL_TREE; + *type = NULL_TREE; + *next = NULL_TREE; + return false; +} + +/* Lower task_reduction and reduction clauses (the latter unless CODE is + OMP_TASKGROUP only with task modifier). Register mapping of those in + START sequence and reducing them and unregister them in the END sequence. */ + +static void +lower_omp_task_reductions (omp_context *ctx, enum tree_code code, tree clauses, + gimple_seq *start, gimple_seq *end) +{ + enum omp_clause_code ccode + = (code == OMP_TASKGROUP + ? OMP_CLAUSE_TASK_REDUCTION : OMP_CLAUSE_REDUCTION); + tree cancellable = NULL_TREE; + clauses = omp_task_reductions_find_first (clauses, code, ccode); + if (clauses == NULL_TREE) + return; + if (code == OMP_FOR || code == OMP_SECTIONS || code == OMP_SCOPE) + { + for (omp_context *outer = ctx->outer; outer; outer = outer->outer) + if (gimple_code (outer->stmt) == GIMPLE_OMP_PARALLEL + && outer->cancellable) + { + cancellable = error_mark_node; + break; + } + else if (gimple_code (outer->stmt) != GIMPLE_OMP_TASKGROUP + && gimple_code (outer->stmt) != GIMPLE_OMP_SCOPE) + break; + } + tree record_type = lang_hooks.types.make_type (RECORD_TYPE); + tree *last = &TYPE_FIELDS (record_type); + unsigned cnt = 0; + if (cancellable) + { + tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, + ptr_type_node); + tree ifield = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, + integer_type_node); + *last = field; + DECL_CHAIN (field) = ifield; + last = &DECL_CHAIN (ifield); + DECL_CONTEXT (field) = record_type; + if (TYPE_ALIGN (record_type) < DECL_ALIGN (field)) + SET_TYPE_ALIGN (record_type, DECL_ALIGN (field)); + DECL_CONTEXT (ifield) = record_type; + if (TYPE_ALIGN (record_type) < DECL_ALIGN (ifield)) + SET_TYPE_ALIGN (record_type, DECL_ALIGN (ifield)); + } + for (int pass = 0; pass < 2; pass++) + { + tree decl, type, next; + for (tree c = clauses; + omp_task_reduction_iterate (pass, code, ccode, + &c, &decl, &type, &next); c = next) + { + ++cnt; + tree new_type = type; + if (ctx->outer) + new_type = remap_type (type, &ctx->outer->cb); + tree field + = build_decl (OMP_CLAUSE_LOCATION (c), FIELD_DECL, + DECL_P (decl) ? DECL_NAME (decl) : NULL_TREE, + new_type); + if (DECL_P (decl) && type == TREE_TYPE (decl)) + { + SET_DECL_ALIGN (field, DECL_ALIGN (decl)); + DECL_USER_ALIGN (field) = DECL_USER_ALIGN (decl); + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (decl); + } + else + SET_DECL_ALIGN (field, TYPE_ALIGN (type)); + DECL_CONTEXT (field) = record_type; + if (TYPE_ALIGN (record_type) < DECL_ALIGN (field)) + SET_TYPE_ALIGN (record_type, DECL_ALIGN (field)); + *last = field; + last = &DECL_CHAIN (field); + tree bfield + = build_decl (OMP_CLAUSE_LOCATION (c), FIELD_DECL, NULL_TREE, + boolean_type_node); + DECL_CONTEXT (bfield) = record_type; + if (TYPE_ALIGN (record_type) < DECL_ALIGN (bfield)) + SET_TYPE_ALIGN (record_type, DECL_ALIGN (bfield)); + *last = bfield; + last = &DECL_CHAIN (bfield); + } + } + *last = NULL_TREE; + layout_type (record_type); + + /* Build up an array which registers with the runtime all the reductions + and deregisters them at the end. Format documented in libgomp/task.c. */ + tree atype = build_array_type_nelts (pointer_sized_int_node, 7 + cnt * 3); + tree avar = create_tmp_var_raw (atype); + gimple_add_tmp_var (avar); + TREE_ADDRESSABLE (avar) = 1; + tree r = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_zero_node, + NULL_TREE, NULL_TREE); + tree t = build_int_cst (pointer_sized_int_node, cnt); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + gimple_seq seq = NULL; + tree sz = fold_convert (pointer_sized_int_node, + TYPE_SIZE_UNIT (record_type)); + int cachesz = 64; + sz = fold_build2 (PLUS_EXPR, pointer_sized_int_node, sz, + build_int_cst (pointer_sized_int_node, cachesz - 1)); + sz = fold_build2 (BIT_AND_EXPR, pointer_sized_int_node, sz, + build_int_cst (pointer_sized_int_node, ~(cachesz - 1))); + ctx->task_reductions.create (1 + cnt); + ctx->task_reduction_map = new hash_map<tree, unsigned>; + ctx->task_reductions.quick_push (TREE_CODE (sz) == INTEGER_CST + ? sz : NULL_TREE); + sz = force_gimple_operand (sz, &seq, true, NULL_TREE); + gimple_seq_add_seq (start, seq); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_one_node, + NULL_TREE, NULL_TREE); + gimple_seq_add_stmt (start, gimple_build_assign (r, sz)); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_int (2), + NULL_TREE, NULL_TREE); + t = build_int_cst (pointer_sized_int_node, + MAX (TYPE_ALIGN_UNIT (record_type), (unsigned) cachesz)); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_int (3), + NULL_TREE, NULL_TREE); + t = build_int_cst (pointer_sized_int_node, -1); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_int (4), + NULL_TREE, NULL_TREE); + t = build_int_cst (pointer_sized_int_node, 0); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + + /* In end, build a loop that iterates from 0 to < omp_get_num_threads () + and for each task reduction checks a bool right after the private variable + within that thread's chunk; if the bool is clear, it hasn't been + initialized and thus isn't going to be reduced nor destructed, otherwise + reduce and destruct it. */ + tree idx = create_tmp_var (size_type_node); + gimple_seq_add_stmt (end, gimple_build_assign (idx, size_zero_node)); + tree num_thr_sz = create_tmp_var (size_type_node); + tree lab1 = create_artificial_label (UNKNOWN_LOCATION); + tree lab2 = create_artificial_label (UNKNOWN_LOCATION); + tree lab3 = NULL_TREE, lab7 = NULL_TREE; + gimple *g; + if (code == OMP_FOR || code == OMP_SECTIONS || code == OMP_SCOPE) + { + /* For worksharing constructs or scope, only perform it in the master + thread, with the exception of cancelled implicit barriers - then only + handle the current thread. */ + tree lab4 = create_artificial_label (UNKNOWN_LOCATION); + t = builtin_decl_explicit (BUILT_IN_OMP_GET_THREAD_NUM); + tree thr_num = create_tmp_var (integer_type_node); + g = gimple_build_call (t, 0); + gimple_call_set_lhs (g, thr_num); + gimple_seq_add_stmt (end, g); + if (cancellable) + { + tree c; + tree lab5 = create_artificial_label (UNKNOWN_LOCATION); + tree lab6 = create_artificial_label (UNKNOWN_LOCATION); + lab3 = create_artificial_label (UNKNOWN_LOCATION); + if (code == OMP_FOR) + c = gimple_omp_for_clauses (ctx->stmt); + else if (code == OMP_SECTIONS) + c = gimple_omp_sections_clauses (ctx->stmt); + else /* if (code == OMP_SCOPE) */ + c = gimple_omp_scope_clauses (ctx->stmt); + c = OMP_CLAUSE_DECL (omp_find_clause (c, OMP_CLAUSE__REDUCTEMP_)); + cancellable = c; + g = gimple_build_cond (NE_EXPR, c, build_zero_cst (TREE_TYPE (c)), + lab5, lab6); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab5)); + g = gimple_build_assign (idx, NOP_EXPR, thr_num); + gimple_seq_add_stmt (end, g); + g = gimple_build_assign (num_thr_sz, PLUS_EXPR, idx, + build_one_cst (TREE_TYPE (idx))); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_goto (lab3)); + gimple_seq_add_stmt (end, gimple_build_label (lab6)); + } + g = gimple_build_cond (NE_EXPR, thr_num, integer_zero_node, lab2, lab4); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab4)); + } + if (code != OMP_PARALLEL) + { + t = builtin_decl_explicit (BUILT_IN_OMP_GET_NUM_THREADS); + tree num_thr = create_tmp_var (integer_type_node); + g = gimple_build_call (t, 0); + gimple_call_set_lhs (g, num_thr); + gimple_seq_add_stmt (end, g); + g = gimple_build_assign (num_thr_sz, NOP_EXPR, num_thr); + gimple_seq_add_stmt (end, g); + if (cancellable) + gimple_seq_add_stmt (end, gimple_build_label (lab3)); + } + else + { + tree c = omp_find_clause (gimple_omp_parallel_clauses (ctx->stmt), + OMP_CLAUSE__REDUCTEMP_); + t = fold_convert (pointer_sized_int_node, OMP_CLAUSE_DECL (c)); + t = fold_convert (size_type_node, t); + gimplify_assign (num_thr_sz, t, end); + } + t = build4 (ARRAY_REF, pointer_sized_int_node, avar, size_int (2), + NULL_TREE, NULL_TREE); + tree data = create_tmp_var (pointer_sized_int_node); + gimple_seq_add_stmt (end, gimple_build_assign (data, t)); + if (code == OMP_TASKLOOP) + { + lab7 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, data, + build_zero_cst (pointer_sized_int_node), + lab1, lab7); + gimple_seq_add_stmt (end, g); + } + gimple_seq_add_stmt (end, gimple_build_label (lab1)); + tree ptr; + if (TREE_CODE (TYPE_SIZE_UNIT (record_type)) == INTEGER_CST) + ptr = create_tmp_var (build_pointer_type (record_type)); + else + ptr = create_tmp_var (ptr_type_node); + gimple_seq_add_stmt (end, gimple_build_assign (ptr, NOP_EXPR, data)); + + tree field = TYPE_FIELDS (record_type); + cnt = 0; + if (cancellable) + field = DECL_CHAIN (DECL_CHAIN (field)); + for (int pass = 0; pass < 2; pass++) + { + tree decl, type, next; + for (tree c = clauses; + omp_task_reduction_iterate (pass, code, ccode, + &c, &decl, &type, &next); c = next) + { + tree var = decl, ref; + if (TREE_CODE (decl) == MEM_REF) + { + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + tree v = var; + if (TREE_CODE (var) == ADDR_EXPR) + var = TREE_OPERAND (var, 0); + else if (TREE_CODE (var) == INDIRECT_REF) + var = TREE_OPERAND (var, 0); + tree orig_var = var; + if (is_variable_sized (var)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (var)); + var = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (var) == INDIRECT_REF); + var = TREE_OPERAND (var, 0); + gcc_assert (DECL_P (var)); + } + t = ref = maybe_lookup_decl_in_outer_ctx (var, ctx); + if (orig_var != var) + gcc_assert (TREE_CODE (v) == ADDR_EXPR); + else if (TREE_CODE (v) == ADDR_EXPR) + t = build_fold_addr_expr (t); + else if (TREE_CODE (v) == INDIRECT_REF) + t = build_fold_indirect_ref (t); + if (TREE_CODE (TREE_OPERAND (decl, 0)) == POINTER_PLUS_EXPR) + { + tree b = TREE_OPERAND (TREE_OPERAND (decl, 0), 1); + b = maybe_lookup_decl_in_outer_ctx (b, ctx); + t = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, b); + } + if (!integer_zerop (TREE_OPERAND (decl, 1))) + t = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, + fold_convert (size_type_node, + TREE_OPERAND (decl, 1))); + } + else + { + t = ref = maybe_lookup_decl_in_outer_ctx (var, ctx); + if (!omp_privatize_by_reference (decl)) + t = build_fold_addr_expr (t); + } + t = fold_convert (pointer_sized_int_node, t); + seq = NULL; + t = force_gimple_operand (t, &seq, true, NULL_TREE); + gimple_seq_add_seq (start, seq); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, + size_int (7 + cnt * 3), NULL_TREE, NULL_TREE); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + t = unshare_expr (byte_position (field)); + t = fold_convert (pointer_sized_int_node, t); + ctx->task_reduction_map->put (c, cnt); + ctx->task_reductions.quick_push (TREE_CODE (t) == INTEGER_CST + ? t : NULL_TREE); + seq = NULL; + t = force_gimple_operand (t, &seq, true, NULL_TREE); + gimple_seq_add_seq (start, seq); + r = build4 (ARRAY_REF, pointer_sized_int_node, avar, + size_int (7 + cnt * 3 + 1), NULL_TREE, NULL_TREE); + gimple_seq_add_stmt (start, gimple_build_assign (r, t)); + + tree bfield = DECL_CHAIN (field); + tree cond; + if (code == OMP_PARALLEL + || code == OMP_FOR + || code == OMP_SECTIONS + || code == OMP_SCOPE) + /* In parallel, worksharing or scope all threads unconditionally + initialize all their task reduction private variables. */ + cond = boolean_true_node; + else if (TREE_TYPE (ptr) == ptr_type_node) + { + cond = build2 (POINTER_PLUS_EXPR, ptr_type_node, ptr, + unshare_expr (byte_position (bfield))); + seq = NULL; + cond = force_gimple_operand (cond, &seq, true, NULL_TREE); + gimple_seq_add_seq (end, seq); + tree pbool = build_pointer_type (TREE_TYPE (bfield)); + cond = build2 (MEM_REF, TREE_TYPE (bfield), cond, + build_int_cst (pbool, 0)); + } + else + cond = build3 (COMPONENT_REF, TREE_TYPE (bfield), + build_simple_mem_ref (ptr), bfield, NULL_TREE); + tree lab3 = create_artificial_label (UNKNOWN_LOCATION); + tree lab4 = create_artificial_label (UNKNOWN_LOCATION); + tree condv = create_tmp_var (boolean_type_node); + gimple_seq_add_stmt (end, gimple_build_assign (condv, cond)); + g = gimple_build_cond (NE_EXPR, condv, boolean_false_node, + lab3, lab4); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab3)); + if (cancellable && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == NULL_TREE) + { + /* If this reduction doesn't need destruction and parallel + has been cancelled, there is nothing to do for this + reduction, so jump around the merge operation. */ + tree lab5 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, cancellable, + build_zero_cst (TREE_TYPE (cancellable)), + lab4, lab5); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab5)); + } + + tree new_var; + if (TREE_TYPE (ptr) == ptr_type_node) + { + new_var = build2 (POINTER_PLUS_EXPR, ptr_type_node, ptr, + unshare_expr (byte_position (field))); + seq = NULL; + new_var = force_gimple_operand (new_var, &seq, true, NULL_TREE); + gimple_seq_add_seq (end, seq); + tree pbool = build_pointer_type (TREE_TYPE (field)); + new_var = build2 (MEM_REF, TREE_TYPE (field), new_var, + build_int_cst (pbool, 0)); + } + else + new_var = build3 (COMPONENT_REF, TREE_TYPE (field), + build_simple_mem_ref (ptr), field, NULL_TREE); + + enum tree_code rcode = OMP_CLAUSE_REDUCTION_CODE (c); + if (TREE_CODE (decl) != MEM_REF + && omp_privatize_by_reference (decl)) + ref = build_simple_mem_ref (ref); + /* reduction(-:var) sums up the partial results, so it acts + identically to reduction(+:var). */ + if (rcode == MINUS_EXPR) + rcode = PLUS_EXPR; + if (TREE_CODE (decl) == MEM_REF) + { + tree type = TREE_TYPE (new_var); + tree v = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree i = create_tmp_var (TREE_TYPE (v)); + tree ptype = build_pointer_type (TREE_TYPE (type)); + if (DECL_P (v)) + { + v = maybe_lookup_decl_in_outer_ctx (v, ctx); + tree vv = create_tmp_var (TREE_TYPE (v)); + gimplify_assign (vv, v, start); + v = vv; + } + ref = build4 (ARRAY_REF, pointer_sized_int_node, avar, + size_int (7 + cnt * 3), NULL_TREE, NULL_TREE); + new_var = build_fold_addr_expr (new_var); + new_var = fold_convert (ptype, new_var); + ref = fold_convert (ptype, ref); + tree m = create_tmp_var (ptype); + gimplify_assign (m, new_var, end); + new_var = m; + m = create_tmp_var (ptype); + gimplify_assign (m, ref, end); + ref = m; + gimplify_assign (i, build_int_cst (TREE_TYPE (v), 0), end); + tree body = create_artificial_label (UNKNOWN_LOCATION); + tree endl = create_artificial_label (UNKNOWN_LOCATION); + gimple_seq_add_stmt (end, gimple_build_label (body)); + tree priv = build_simple_mem_ref (new_var); + tree out = build_simple_mem_ref (ref); + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + tree decl_placeholder + = OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c); + tree lab6 = NULL_TREE; + if (cancellable) + { + /* If this reduction needs destruction and parallel + has been cancelled, jump around the merge operation + to the destruction. */ + tree lab5 = create_artificial_label (UNKNOWN_LOCATION); + lab6 = create_artificial_label (UNKNOWN_LOCATION); + tree zero = build_zero_cst (TREE_TYPE (cancellable)); + g = gimple_build_cond (NE_EXPR, cancellable, zero, + lab6, lab5); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab5)); + } + SET_DECL_VALUE_EXPR (placeholder, out); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + SET_DECL_VALUE_EXPR (decl_placeholder, priv); + DECL_HAS_VALUE_EXPR_P (decl_placeholder) = 1; + lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c), ctx); + gimple_seq_add_seq (end, + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c)); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION) + { + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL; + OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c) = NULL; + } + if (cancellable) + gimple_seq_add_stmt (end, gimple_build_label (lab6)); + tree x = lang_hooks.decls.omp_clause_dtor (c, priv); + if (x) + { + gimple_seq tseq = NULL; + gimplify_stmt (&x, &tseq); + gimple_seq_add_seq (end, tseq); + } + } + else + { + tree x = build2 (rcode, TREE_TYPE (out), out, priv); + out = unshare_expr (out); + gimplify_assign (out, x, end); + } + gimple *g + = gimple_build_assign (new_var, POINTER_PLUS_EXPR, new_var, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (end, g); + g = gimple_build_assign (ref, POINTER_PLUS_EXPR, ref, + TYPE_SIZE_UNIT (TREE_TYPE (type))); + gimple_seq_add_stmt (end, g); + g = gimple_build_assign (i, PLUS_EXPR, i, + build_int_cst (TREE_TYPE (i), 1)); + gimple_seq_add_stmt (end, g); + g = gimple_build_cond (LE_EXPR, i, v, body, endl); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (endl)); + } + else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + tree oldv = NULL_TREE; + tree lab6 = NULL_TREE; + if (cancellable) + { + /* If this reduction needs destruction and parallel + has been cancelled, jump around the merge operation + to the destruction. */ + tree lab5 = create_artificial_label (UNKNOWN_LOCATION); + lab6 = create_artificial_label (UNKNOWN_LOCATION); + tree zero = build_zero_cst (TREE_TYPE (cancellable)); + g = gimple_build_cond (NE_EXPR, cancellable, zero, + lab6, lab5); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab5)); + } + if (omp_privatize_by_reference (decl) + && !useless_type_conversion_p (TREE_TYPE (placeholder), + TREE_TYPE (ref))) + ref = build_fold_addr_expr_loc (OMP_CLAUSE_LOCATION (c), ref); + ref = build_fold_addr_expr_loc (OMP_CLAUSE_LOCATION (c), ref); + tree refv = create_tmp_var (TREE_TYPE (ref)); + gimplify_assign (refv, ref, end); + ref = build_simple_mem_ref_loc (OMP_CLAUSE_LOCATION (c), refv); + SET_DECL_VALUE_EXPR (placeholder, ref); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + tree d = maybe_lookup_decl (decl, ctx); + gcc_assert (d); + if (DECL_HAS_VALUE_EXPR_P (d)) + oldv = DECL_VALUE_EXPR (d); + if (omp_privatize_by_reference (var)) + { + tree v = fold_convert (TREE_TYPE (d), + build_fold_addr_expr (new_var)); + SET_DECL_VALUE_EXPR (d, v); + } + else + SET_DECL_VALUE_EXPR (d, new_var); + DECL_HAS_VALUE_EXPR_P (d) = 1; + lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c), ctx); + if (oldv) + SET_DECL_VALUE_EXPR (d, oldv); + else + { + SET_DECL_VALUE_EXPR (d, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (d) = 0; + } + gimple_seq_add_seq (end, OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c)); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION) + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL; + if (cancellable) + gimple_seq_add_stmt (end, gimple_build_label (lab6)); + tree x = lang_hooks.decls.omp_clause_dtor (c, new_var); + if (x) + { + gimple_seq tseq = NULL; + gimplify_stmt (&x, &tseq); + gimple_seq_add_seq (end, tseq); + } + } + else + { + tree x = build2 (rcode, TREE_TYPE (ref), ref, new_var); + ref = unshare_expr (ref); + gimplify_assign (ref, x, end); + } + gimple_seq_add_stmt (end, gimple_build_label (lab4)); + ++cnt; + field = DECL_CHAIN (bfield); + } + } + + if (code == OMP_TASKGROUP) + { + t = builtin_decl_explicit (BUILT_IN_GOMP_TASKGROUP_REDUCTION_REGISTER); + g = gimple_build_call (t, 1, build_fold_addr_expr (avar)); + gimple_seq_add_stmt (start, g); + } + else + { + tree c; + if (code == OMP_FOR) + c = gimple_omp_for_clauses (ctx->stmt); + else if (code == OMP_SECTIONS) + c = gimple_omp_sections_clauses (ctx->stmt); + else if (code == OMP_SCOPE) + c = gimple_omp_scope_clauses (ctx->stmt); + else + c = gimple_omp_taskreg_clauses (ctx->stmt); + c = omp_find_clause (c, OMP_CLAUSE__REDUCTEMP_); + t = fold_convert (TREE_TYPE (OMP_CLAUSE_DECL (c)), + build_fold_addr_expr (avar)); + gimplify_assign (OMP_CLAUSE_DECL (c), t, start); + } + + gimple_seq_add_stmt (end, gimple_build_assign (data, PLUS_EXPR, data, sz)); + gimple_seq_add_stmt (end, gimple_build_assign (idx, PLUS_EXPR, idx, + size_one_node)); + g = gimple_build_cond (NE_EXPR, idx, num_thr_sz, lab1, lab2); + gimple_seq_add_stmt (end, g); + gimple_seq_add_stmt (end, gimple_build_label (lab2)); + if (code == OMP_FOR || code == OMP_SECTIONS || code == OMP_SCOPE) + { + enum built_in_function bfn + = BUILT_IN_GOMP_WORKSHARE_TASK_REDUCTION_UNREGISTER; + t = builtin_decl_explicit (bfn); + tree c_bool_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (t))); + tree arg; + if (cancellable) + { + arg = create_tmp_var (c_bool_type); + gimple_seq_add_stmt (end, gimple_build_assign (arg, NOP_EXPR, + cancellable)); + } + else + arg = build_int_cst (c_bool_type, 0); + g = gimple_build_call (t, 1, arg); + } + else + { + t = builtin_decl_explicit (BUILT_IN_GOMP_TASKGROUP_REDUCTION_UNREGISTER); + g = gimple_build_call (t, 1, build_fold_addr_expr (avar)); + } + gimple_seq_add_stmt (end, g); + if (lab7) + gimple_seq_add_stmt (end, gimple_build_label (lab7)); + t = build_constructor (atype, NULL); + TREE_THIS_VOLATILE (t) = 1; + gimple_seq_add_stmt (end, gimple_build_assign (avar, t)); +} + +/* Expand code for an OpenMP taskgroup directive. */ + +static void +lower_omp_taskgroup (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + gimple *stmt = gsi_stmt (*gsi_p); + gcall *x; + gbind *bind; + gimple_seq dseq = NULL; + tree block = make_node (BLOCK); + + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + gimple_bind_add_stmt (bind, stmt); + + push_gimplify_context (); + + x = gimple_build_call (builtin_decl_explicit (BUILT_IN_GOMP_TASKGROUP_START), + 0); + gimple_bind_add_stmt (bind, x); + + lower_omp_task_reductions (ctx, OMP_TASKGROUP, + gimple_omp_taskgroup_clauses (stmt), + gimple_bind_body_ptr (bind), &dseq); + + lower_omp (gimple_omp_body_ptr (stmt), ctx); + gimple_bind_add_seq (bind, gimple_omp_body (stmt)); + gimple_omp_set_body (stmt, NULL); + + gimple_bind_add_stmt (bind, gimple_build_omp_return (true)); + gimple_bind_add_seq (bind, dseq); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = ctx->block_vars; +} + + +/* Fold the OMP_ORDERED_CLAUSES for the OMP_ORDERED in STMT if possible. */ + +static void +lower_omp_ordered_clauses (gimple_stmt_iterator *gsi_p, gomp_ordered *ord_stmt, + omp_context *ctx) +{ + struct omp_for_data fd; + if (!ctx->outer || gimple_code (ctx->outer->stmt) != GIMPLE_OMP_FOR) + return; + + unsigned int len = gimple_omp_for_collapse (ctx->outer->stmt); + struct omp_for_data_loop *loops = XALLOCAVEC (struct omp_for_data_loop, len); + omp_extract_for_data (as_a <gomp_for *> (ctx->outer->stmt), &fd, loops); + if (!fd.ordered) + return; + + tree *list_p = gimple_omp_ordered_clauses_ptr (ord_stmt); + tree c = gimple_omp_ordered_clauses (ord_stmt); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND + && OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SINK) + { + /* Merge depend clauses from multiple adjacent + #pragma omp ordered depend(sink:...) constructs + into one #pragma omp ordered depend(sink:...), so that + we can optimize them together. */ + gimple_stmt_iterator gsi = *gsi_p; + gsi_next (&gsi); + while (!gsi_end_p (gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (is_gimple_debug (stmt) + || gimple_code (stmt) == GIMPLE_NOP) + { + gsi_next (&gsi); + continue; + } + if (gimple_code (stmt) != GIMPLE_OMP_ORDERED) + break; + gomp_ordered *ord_stmt2 = as_a <gomp_ordered *> (stmt); + c = gimple_omp_ordered_clauses (ord_stmt2); + if (c == NULL_TREE + || OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND + || OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_SINK) + break; + while (*list_p) + list_p = &OMP_CLAUSE_CHAIN (*list_p); + *list_p = c; + gsi_remove (&gsi, true); + } + } + + /* Canonicalize sink dependence clauses into one folded clause if + possible. + + The basic algorithm is to create a sink vector whose first + element is the GCD of all the first elements, and whose remaining + elements are the minimum of the subsequent columns. + + We ignore dependence vectors whose first element is zero because + such dependencies are known to be executed by the same thread. + + We take into account the direction of the loop, so a minimum + becomes a maximum if the loop is iterating forwards. We also + ignore sink clauses where the loop direction is unknown, or where + the offsets are clearly invalid because they are not a multiple + of the loop increment. + + For example: + + #pragma omp for ordered(2) + for (i=0; i < N; ++i) + for (j=0; j < M; ++j) + { + #pragma omp ordered \ + depend(sink:i-8,j-2) \ + depend(sink:i,j-1) \ // Completely ignored because i+0. + depend(sink:i-4,j-3) \ + depend(sink:i-6,j-4) + #pragma omp ordered depend(source) + } + + Folded clause is: + + depend(sink:-gcd(8,4,6),-min(2,3,4)) + -or- + depend(sink:-2,-2) + */ + + /* FIXME: Computing GCD's where the first element is zero is + non-trivial in the presence of collapsed loops. Do this later. */ + if (fd.collapse > 1) + return; + + wide_int *folded_deps = XALLOCAVEC (wide_int, 2 * len - 1); + + /* wide_int is not a POD so it must be default-constructed. */ + for (unsigned i = 0; i != 2 * len - 1; ++i) + new (static_cast<void*>(folded_deps + i)) wide_int (); + + tree folded_dep = NULL_TREE; + /* TRUE if the first dimension's offset is negative. */ + bool neg_offset_p = false; + + list_p = gimple_omp_ordered_clauses_ptr (ord_stmt); + unsigned int i; + while ((c = *list_p) != NULL) + { + bool remove = false; + + gcc_assert (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND); + if (OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_SINK) + goto next_ordered_clause; + + tree vec; + for (vec = OMP_CLAUSE_DECL (c), i = 0; + vec && TREE_CODE (vec) == TREE_LIST; + vec = TREE_CHAIN (vec), ++i) + { + gcc_assert (i < len); + + /* omp_extract_for_data has canonicalized the condition. */ + gcc_assert (fd.loops[i].cond_code == LT_EXPR + || fd.loops[i].cond_code == GT_EXPR); + bool forward = fd.loops[i].cond_code == LT_EXPR; + bool maybe_lexically_later = true; + + /* While the committee makes up its mind, bail if we have any + non-constant steps. */ + if (TREE_CODE (fd.loops[i].step) != INTEGER_CST) + goto lower_omp_ordered_ret; + + tree itype = TREE_TYPE (TREE_VALUE (vec)); + if (POINTER_TYPE_P (itype)) + itype = sizetype; + wide_int offset = wide_int::from (wi::to_wide (TREE_PURPOSE (vec)), + TYPE_PRECISION (itype), + TYPE_SIGN (itype)); + + /* Ignore invalid offsets that are not multiples of the step. */ + if (!wi::multiple_of_p (wi::abs (offset), + wi::abs (wi::to_wide (fd.loops[i].step)), + UNSIGNED)) + { + warning_at (OMP_CLAUSE_LOCATION (c), 0, + "ignoring sink clause with offset that is not " + "a multiple of the loop step"); + remove = true; + goto next_ordered_clause; + } + + /* Calculate the first dimension. The first dimension of + the folded dependency vector is the GCD of the first + elements, while ignoring any first elements whose offset + is 0. */ + if (i == 0) + { + /* Ignore dependence vectors whose first dimension is 0. */ + if (offset == 0) + { + remove = true; + goto next_ordered_clause; + } + else + { + if (!TYPE_UNSIGNED (itype) && (forward ^ wi::neg_p (offset))) + { + error_at (OMP_CLAUSE_LOCATION (c), + "first offset must be in opposite direction " + "of loop iterations"); + goto lower_omp_ordered_ret; + } + if (forward) + offset = -offset; + neg_offset_p = forward; + /* Initialize the first time around. */ + if (folded_dep == NULL_TREE) + { + folded_dep = c; + folded_deps[0] = offset; + } + else + folded_deps[0] = wi::gcd (folded_deps[0], + offset, UNSIGNED); + } + } + /* Calculate minimum for the remaining dimensions. */ + else + { + folded_deps[len + i - 1] = offset; + if (folded_dep == c) + folded_deps[i] = offset; + else if (maybe_lexically_later + && !wi::eq_p (folded_deps[i], offset)) + { + if (forward ^ wi::gts_p (folded_deps[i], offset)) + { + unsigned int j; + folded_dep = c; + for (j = 1; j <= i; j++) + folded_deps[j] = folded_deps[len + j - 1]; + } + else + maybe_lexically_later = false; + } + } + } + gcc_assert (i == len); + + remove = true; + + next_ordered_clause: + if (remove) + *list_p = OMP_CLAUSE_CHAIN (c); + else + list_p = &OMP_CLAUSE_CHAIN (c); + } + + if (folded_dep) + { + if (neg_offset_p) + folded_deps[0] = -folded_deps[0]; + + tree itype = TREE_TYPE (TREE_VALUE (OMP_CLAUSE_DECL (folded_dep))); + if (POINTER_TYPE_P (itype)) + itype = sizetype; + + TREE_PURPOSE (OMP_CLAUSE_DECL (folded_dep)) + = wide_int_to_tree (itype, folded_deps[0]); + OMP_CLAUSE_CHAIN (folded_dep) = gimple_omp_ordered_clauses (ord_stmt); + *gimple_omp_ordered_clauses_ptr (ord_stmt) = folded_dep; + } + + lower_omp_ordered_ret: + + /* Ordered without clauses is #pragma omp threads, while we want + a nop instead if we remove all clauses. */ + if (gimple_omp_ordered_clauses (ord_stmt) == NULL_TREE) + gsi_replace (gsi_p, gimple_build_nop (), true); +} + + +/* Expand code for an OpenMP ordered directive. */ + +static void +lower_omp_ordered (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block; + gimple *stmt = gsi_stmt (*gsi_p), *g; + gomp_ordered *ord_stmt = as_a <gomp_ordered *> (stmt); + gcall *x; + gbind *bind; + bool simd = omp_find_clause (gimple_omp_ordered_clauses (ord_stmt), + OMP_CLAUSE_SIMD); + /* FIXME: this should check presence of OMP_CLAUSE__SIMT_ on the enclosing + loop. */ + bool maybe_simt + = simd && omp_maybe_offloaded_ctx (ctx) && omp_max_simt_vf () > 1; + bool threads = omp_find_clause (gimple_omp_ordered_clauses (ord_stmt), + OMP_CLAUSE_THREADS); + + if (omp_find_clause (gimple_omp_ordered_clauses (ord_stmt), + OMP_CLAUSE_DEPEND)) + { + /* FIXME: This is needs to be moved to the expansion to verify various + conditions only testable on cfg with dominators computed, and also + all the depend clauses to be merged still might need to be available + for the runtime checks. */ + if (0) + lower_omp_ordered_clauses (gsi_p, ord_stmt, ctx); + return; + } + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + gimple_bind_add_stmt (bind, stmt); + + if (simd) + { + x = gimple_build_call_internal (IFN_GOMP_SIMD_ORDERED_START, 1, + build_int_cst (NULL_TREE, threads)); + cfun->has_simduid_loops = true; + } + else + x = gimple_build_call (builtin_decl_explicit (BUILT_IN_GOMP_ORDERED_START), + 0); + gimple_bind_add_stmt (bind, x); + + tree counter = NULL_TREE, test = NULL_TREE, body = NULL_TREE; + if (maybe_simt) + { + counter = create_tmp_var (integer_type_node); + g = gimple_build_call_internal (IFN_GOMP_SIMT_LANE, 0); + gimple_call_set_lhs (g, counter); + gimple_bind_add_stmt (bind, g); + + body = create_artificial_label (UNKNOWN_LOCATION); + test = create_artificial_label (UNKNOWN_LOCATION); + gimple_bind_add_stmt (bind, gimple_build_label (body)); + + tree simt_pred = create_tmp_var (integer_type_node); + g = gimple_build_call_internal (IFN_GOMP_SIMT_ORDERED_PRED, 1, counter); + gimple_call_set_lhs (g, simt_pred); + gimple_bind_add_stmt (bind, g); + + tree t = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (EQ_EXPR, simt_pred, integer_zero_node, t, test); + gimple_bind_add_stmt (bind, g); + + gimple_bind_add_stmt (bind, gimple_build_label (t)); + } + lower_omp (gimple_omp_body_ptr (stmt), ctx); + gimple_omp_set_body (stmt, maybe_catch_exception (gimple_omp_body (stmt))); + gimple_bind_add_seq (bind, gimple_omp_body (stmt)); + gimple_omp_set_body (stmt, NULL); + + if (maybe_simt) + { + gimple_bind_add_stmt (bind, gimple_build_label (test)); + g = gimple_build_assign (counter, MINUS_EXPR, counter, integer_one_node); + gimple_bind_add_stmt (bind, g); + + tree c = build2 (GE_EXPR, boolean_type_node, counter, integer_zero_node); + tree nonneg = create_tmp_var (integer_type_node); + gimple_seq tseq = NULL; + gimplify_assign (nonneg, fold_convert (integer_type_node, c), &tseq); + gimple_bind_add_seq (bind, tseq); + + g = gimple_build_call_internal (IFN_GOMP_SIMT_VOTE_ANY, 1, nonneg); + gimple_call_set_lhs (g, nonneg); + gimple_bind_add_stmt (bind, g); + + tree end = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (NE_EXPR, nonneg, integer_zero_node, body, end); + gimple_bind_add_stmt (bind, g); + + gimple_bind_add_stmt (bind, gimple_build_label (end)); + } + if (simd) + x = gimple_build_call_internal (IFN_GOMP_SIMD_ORDERED_END, 1, + build_int_cst (NULL_TREE, threads)); + else + x = gimple_build_call (builtin_decl_explicit (BUILT_IN_GOMP_ORDERED_END), + 0); + gimple_bind_add_stmt (bind, x); + + gimple_bind_add_stmt (bind, gimple_build_omp_return (true)); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = gimple_bind_vars (bind); +} + + +/* Expand code for an OpenMP scan directive and the structured block + before the scan directive. */ + +static void +lower_omp_scan (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + gimple *stmt = gsi_stmt (*gsi_p); + bool has_clauses + = gimple_omp_scan_clauses (as_a <gomp_scan *> (stmt)) != NULL; + tree lane = NULL_TREE; + gimple_seq before = NULL; + omp_context *octx = ctx->outer; + gcc_assert (octx); + if (octx->scan_exclusive && !has_clauses) + { + gimple_stmt_iterator gsi2 = *gsi_p; + gsi_next (&gsi2); + gimple *stmt2 = gsi_stmt (gsi2); + /* For exclusive scan, swap GIMPLE_OMP_SCAN without clauses + with following GIMPLE_OMP_SCAN with clauses, so that input_phase, + the one with exclusive clause(s), comes first. */ + if (stmt2 + && gimple_code (stmt2) == GIMPLE_OMP_SCAN + && gimple_omp_scan_clauses (as_a <gomp_scan *> (stmt2)) != NULL) + { + gsi_remove (gsi_p, false); + gsi_insert_after (gsi_p, stmt, GSI_SAME_STMT); + ctx = maybe_lookup_ctx (stmt2); + gcc_assert (ctx); + lower_omp_scan (gsi_p, ctx); + return; + } + } + + bool input_phase = has_clauses ^ octx->scan_inclusive; + bool is_simd = (gimple_code (octx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (octx->stmt) == GF_OMP_FOR_KIND_SIMD); + bool is_for = (gimple_code (octx->stmt) == GIMPLE_OMP_FOR + && gimple_omp_for_kind (octx->stmt) == GF_OMP_FOR_KIND_FOR + && !gimple_omp_for_combined_p (octx->stmt)); + bool is_for_simd = is_simd && gimple_omp_for_combined_into_p (octx->stmt); + if (is_for_simd && octx->for_simd_scan_phase) + is_simd = false; + if (is_simd) + if (tree c = omp_find_clause (gimple_omp_for_clauses (octx->stmt), + OMP_CLAUSE__SIMDUID_)) + { + tree uid = OMP_CLAUSE__SIMDUID__DECL (c); + lane = create_tmp_var (unsigned_type_node); + tree t = build_int_cst (integer_type_node, + input_phase ? 1 + : octx->scan_inclusive ? 2 : 3); + gimple *g + = gimple_build_call_internal (IFN_GOMP_SIMD_LANE, 2, uid, t); + gimple_call_set_lhs (g, lane); + gimple_seq_add_stmt (&before, g); + } + + if (is_simd || is_for) + { + for (tree c = gimple_omp_for_clauses (octx->stmt); + c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + tree var = OMP_CLAUSE_DECL (c); + tree new_var = lookup_decl (var, octx); + tree val = new_var; + tree var2 = NULL_TREE; + tree var3 = NULL_TREE; + tree var4 = NULL_TREE; + tree lane0 = NULL_TREE; + tree new_vard = new_var; + if (omp_privatize_by_reference (var)) + { + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + val = new_var; + } + if (DECL_HAS_VALUE_EXPR_P (new_vard)) + { + val = DECL_VALUE_EXPR (new_vard); + if (new_vard != new_var) + { + gcc_assert (TREE_CODE (val) == ADDR_EXPR); + val = TREE_OPERAND (val, 0); + } + if (TREE_CODE (val) == ARRAY_REF + && VAR_P (TREE_OPERAND (val, 0))) + { + tree v = TREE_OPERAND (val, 0); + if (lookup_attribute ("omp simd array", + DECL_ATTRIBUTES (v))) + { + val = unshare_expr (val); + lane0 = TREE_OPERAND (val, 1); + TREE_OPERAND (val, 1) = lane; + var2 = lookup_decl (v, octx); + if (octx->scan_exclusive) + var4 = lookup_decl (var2, octx); + if (input_phase + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + var3 = maybe_lookup_decl (var4 ? var4 : var2, octx); + if (!input_phase) + { + var2 = build4 (ARRAY_REF, TREE_TYPE (val), + var2, lane, NULL_TREE, NULL_TREE); + TREE_THIS_NOTRAP (var2) = 1; + if (octx->scan_exclusive) + { + var4 = build4 (ARRAY_REF, TREE_TYPE (val), + var4, lane, NULL_TREE, + NULL_TREE); + TREE_THIS_NOTRAP (var4) = 1; + } + } + else + var2 = val; + } + } + gcc_assert (var2); + } + else + { + var2 = build_outer_var_ref (var, octx); + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + var3 = maybe_lookup_decl (new_vard, octx); + if (var3 == new_vard || var3 == NULL_TREE) + var3 = NULL_TREE; + else if (is_simd && octx->scan_exclusive && !input_phase) + { + var4 = maybe_lookup_decl (var3, octx); + if (var4 == var3 || var4 == NULL_TREE) + { + if (TREE_ADDRESSABLE (TREE_TYPE (new_var))) + { + var4 = var3; + var3 = NULL_TREE; + } + else + var4 = NULL_TREE; + } + } + } + if (is_simd + && octx->scan_exclusive + && !input_phase + && var4 == NULL_TREE) + var4 = create_tmp_var (TREE_TYPE (val)); + } + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + if (input_phase) + { + if (var3) + { + /* If we've added a separate identity element + variable, copy it over into val. */ + tree x = lang_hooks.decls.omp_clause_assign_op (c, val, + var3); + gimplify_and_add (x, &before); + } + else if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + /* Otherwise, assign to it the identity element. */ + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + if (is_for) + tseq = copy_gimple_seq_and_replace_locals (tseq); + tree ref = build_outer_var_ref (var, octx); + tree x = (DECL_HAS_VALUE_EXPR_P (new_vard) + ? DECL_VALUE_EXPR (new_vard) : NULL_TREE); + if (x) + { + if (new_vard != new_var) + val = build_fold_addr_expr_loc (clause_loc, val); + SET_DECL_VALUE_EXPR (new_vard, val); + } + SET_DECL_VALUE_EXPR (placeholder, ref); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&tseq, octx); + if (x) + SET_DECL_VALUE_EXPR (new_vard, x); + SET_DECL_VALUE_EXPR (placeholder, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + gimple_seq_add_seq (&before, tseq); + if (is_simd) + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + } + } + else if (is_simd) + { + tree x; + if (octx->scan_exclusive) + { + tree v4 = unshare_expr (var4); + tree v2 = unshare_expr (var2); + x = lang_hooks.decls.omp_clause_assign_op (c, v4, v2); + gimplify_and_add (x, &before); + } + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + x = (DECL_HAS_VALUE_EXPR_P (new_vard) + ? DECL_VALUE_EXPR (new_vard) : NULL_TREE); + tree vexpr = val; + if (x && new_vard != new_var) + vexpr = build_fold_addr_expr_loc (clause_loc, val); + if (x) + SET_DECL_VALUE_EXPR (new_vard, vexpr); + SET_DECL_VALUE_EXPR (placeholder, var2); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&tseq, octx); + gimple_seq_add_seq (&before, tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + if (x) + SET_DECL_VALUE_EXPR (new_vard, x); + SET_DECL_VALUE_EXPR (placeholder, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (octx->scan_inclusive) + { + x = lang_hooks.decls.omp_clause_assign_op (c, val, + var2); + gimplify_and_add (x, &before); + } + else if (lane0 == NULL_TREE) + { + x = lang_hooks.decls.omp_clause_assign_op (c, val, + var4); + gimplify_and_add (x, &before); + } + } + } + else + { + if (input_phase) + { + /* input phase. Set val to initializer before + the body. */ + tree x = omp_reduction_init (c, TREE_TYPE (new_var)); + gimplify_assign (val, x, &before); + } + else if (is_simd) + { + /* scan phase. */ + enum tree_code code = OMP_CLAUSE_REDUCTION_CODE (c); + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + tree x = build2 (code, TREE_TYPE (var2), + unshare_expr (var2), unshare_expr (val)); + if (octx->scan_inclusive) + { + gimplify_assign (unshare_expr (var2), x, &before); + gimplify_assign (val, var2, &before); + } + else + { + gimplify_assign (unshare_expr (var4), + unshare_expr (var2), &before); + gimplify_assign (var2, x, &before); + if (lane0 == NULL_TREE) + gimplify_assign (val, var4, &before); + } + } + } + if (octx->scan_exclusive && !input_phase && lane0) + { + tree vexpr = unshare_expr (var4); + TREE_OPERAND (vexpr, 1) = lane0; + if (new_vard != new_var) + vexpr = build_fold_addr_expr_loc (clause_loc, vexpr); + SET_DECL_VALUE_EXPR (new_vard, vexpr); + } + } + } + if (is_simd && !is_for_simd) + { + gsi_insert_seq_after (gsi_p, gimple_omp_body (stmt), GSI_SAME_STMT); + gsi_insert_seq_after (gsi_p, before, GSI_SAME_STMT); + gsi_replace (gsi_p, gimple_build_nop (), true); + return; + } + lower_omp (gimple_omp_body_ptr (stmt), octx); + if (before) + { + gimple_stmt_iterator gsi = gsi_start_1 (gimple_omp_body_ptr (stmt)); + gsi_insert_seq_before (&gsi, before, GSI_SAME_STMT); + } +} + + +/* Gimplify a GIMPLE_OMP_CRITICAL statement. This is a relatively simple + substitution of a couple of function calls. But in the NAMED case, + requires that languages coordinate a symbol name. It is therefore + best put here in common code. */ + +static GTY(()) hash_map<tree, tree> *critical_name_mutexes; + +static void +lower_omp_critical (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree block; + tree name, lock, unlock; + gomp_critical *stmt = as_a <gomp_critical *> (gsi_stmt (*gsi_p)); + gbind *bind; + location_t loc = gimple_location (stmt); + gimple_seq tbody; + + name = gimple_omp_critical_name (stmt); + if (name) + { + tree decl; + + if (!critical_name_mutexes) + critical_name_mutexes = hash_map<tree, tree>::create_ggc (10); + + tree *n = critical_name_mutexes->get (name); + if (n == NULL) + { + char *new_str; + + decl = create_tmp_var_raw (ptr_type_node); + + new_str = ACONCAT ((".gomp_critical_user_", + IDENTIFIER_POINTER (name), NULL)); + DECL_NAME (decl) = get_identifier (new_str); + TREE_PUBLIC (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_COMMON (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + + varpool_node::finalize_decl (decl); + + critical_name_mutexes->put (name, decl); + } + else + decl = *n; + + /* If '#pragma omp critical' is inside offloaded region or + inside function marked as offloadable, the symbol must be + marked as offloadable too. */ + omp_context *octx; + if (cgraph_node::get (current_function_decl)->offloadable) + varpool_node::get_create (decl)->offloadable = 1; + else + for (octx = ctx->outer; octx; octx = octx->outer) + if (is_gimple_omp_offloaded (octx->stmt)) + { + varpool_node::get_create (decl)->offloadable = 1; + break; + } + + lock = builtin_decl_explicit (BUILT_IN_GOMP_CRITICAL_NAME_START); + lock = build_call_expr_loc (loc, lock, 1, + build_fold_addr_expr_loc (loc, decl)); + + unlock = builtin_decl_explicit (BUILT_IN_GOMP_CRITICAL_NAME_END); + unlock = build_call_expr_loc (loc, unlock, 1, + build_fold_addr_expr_loc (loc, decl)); + } + else + { + lock = builtin_decl_explicit (BUILT_IN_GOMP_CRITICAL_START); + lock = build_call_expr_loc (loc, lock, 0); + + unlock = builtin_decl_explicit (BUILT_IN_GOMP_CRITICAL_END); + unlock = build_call_expr_loc (loc, unlock, 0); + } + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + gimple_bind_add_stmt (bind, stmt); + + tbody = gimple_bind_body (bind); + gimplify_and_add (lock, &tbody); + gimple_bind_set_body (bind, tbody); + + lower_omp (gimple_omp_body_ptr (stmt), ctx); + gimple_omp_set_body (stmt, maybe_catch_exception (gimple_omp_body (stmt))); + gimple_bind_add_seq (bind, gimple_omp_body (stmt)); + gimple_omp_set_body (stmt, NULL); + + tbody = gimple_bind_body (bind); + gimplify_and_add (unlock, &tbody); + gimple_bind_set_body (bind, tbody); + + gimple_bind_add_stmt (bind, gimple_build_omp_return (true)); + + pop_gimplify_context (bind); + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = gimple_bind_vars (bind); +} + +/* A subroutine of lower_omp_for. Generate code to emit the predicate + for a lastprivate clause. Given a loop control predicate of (V + cond N2), we gate the clause on (!(V cond N2)). The lowered form + is appended to *DLIST, iterator initialization is appended to + *BODY_P. *CLIST is for lastprivate(conditional:) code that needs + to be emitted in a critical section. */ + +static void +lower_omp_for_lastprivate (struct omp_for_data *fd, gimple_seq *body_p, + gimple_seq *dlist, gimple_seq *clist, + struct omp_context *ctx) +{ + tree clauses, cond, vinit; + enum tree_code cond_code; + gimple_seq stmts; + + cond_code = fd->loop.cond_code; + cond_code = cond_code == LT_EXPR ? GE_EXPR : LE_EXPR; + + /* When possible, use a strict equality expression. This can let VRP + type optimizations deduce the value and remove a copy. */ + if (tree_fits_shwi_p (fd->loop.step)) + { + HOST_WIDE_INT step = tree_to_shwi (fd->loop.step); + if (step == 1 || step == -1) + cond_code = EQ_EXPR; + } + + tree n2 = fd->loop.n2; + if (fd->collapse > 1 + && TREE_CODE (n2) != INTEGER_CST + && gimple_omp_for_combined_into_p (fd->for_stmt)) + { + struct omp_context *taskreg_ctx = NULL; + if (gimple_code (ctx->outer->stmt) == GIMPLE_OMP_FOR) + { + gomp_for *gfor = as_a <gomp_for *> (ctx->outer->stmt); + if (gimple_omp_for_kind (gfor) == GF_OMP_FOR_KIND_FOR + || gimple_omp_for_kind (gfor) == GF_OMP_FOR_KIND_DISTRIBUTE) + { + if (gimple_omp_for_combined_into_p (gfor)) + { + gcc_assert (ctx->outer->outer + && is_parallel_ctx (ctx->outer->outer)); + taskreg_ctx = ctx->outer->outer; + } + else + { + struct omp_for_data outer_fd; + omp_extract_for_data (gfor, &outer_fd, NULL); + n2 = fold_convert (TREE_TYPE (n2), outer_fd.loop.n2); + } + } + else if (gimple_omp_for_kind (gfor) == GF_OMP_FOR_KIND_TASKLOOP) + taskreg_ctx = ctx->outer->outer; + } + else if (is_taskreg_ctx (ctx->outer)) + taskreg_ctx = ctx->outer; + if (taskreg_ctx) + { + int i; + tree taskreg_clauses + = gimple_omp_taskreg_clauses (taskreg_ctx->stmt); + tree innerc = omp_find_clause (taskreg_clauses, + OMP_CLAUSE__LOOPTEMP_); + gcc_assert (innerc); + int count = fd->collapse; + if (fd->non_rect + && fd->last_nonrect == fd->first_nonrect + 1) + if (tree v = gimple_omp_for_index (fd->for_stmt, fd->last_nonrect)) + if (!TYPE_UNSIGNED (TREE_TYPE (v))) + count += 4; + for (i = 0; i < count; i++) + { + innerc = omp_find_clause (OMP_CLAUSE_CHAIN (innerc), + OMP_CLAUSE__LOOPTEMP_); + gcc_assert (innerc); + } + innerc = omp_find_clause (OMP_CLAUSE_CHAIN (innerc), + OMP_CLAUSE__LOOPTEMP_); + if (innerc) + n2 = fold_convert (TREE_TYPE (n2), + lookup_decl (OMP_CLAUSE_DECL (innerc), + taskreg_ctx)); + } + } + cond = build2 (cond_code, boolean_type_node, fd->loop.v, n2); + + clauses = gimple_omp_for_clauses (fd->for_stmt); + stmts = NULL; + lower_lastprivate_clauses (clauses, cond, body_p, &stmts, clist, ctx); + if (!gimple_seq_empty_p (stmts)) + { + gimple_seq_add_seq (&stmts, *dlist); + *dlist = stmts; + + /* Optimize: v = 0; is usually cheaper than v = some_other_constant. */ + vinit = fd->loop.n1; + if (cond_code == EQ_EXPR + && tree_fits_shwi_p (fd->loop.n2) + && ! integer_zerop (fd->loop.n2)) + vinit = build_int_cst (TREE_TYPE (fd->loop.v), 0); + else + vinit = unshare_expr (vinit); + + /* Initialize the iterator variable, so that threads that don't execute + any iterations don't execute the lastprivate clauses by accident. */ + gimplify_assign (fd->loop.v, vinit, body_p); + } +} + +/* OpenACC privatization. + + Or, in other words, *sharing* at the respective OpenACC level of + parallelism. + + From a correctness perspective, a non-addressable variable can't be accessed + outside the current thread, so it can go in a (faster than shared memory) + register -- though that register may need to be broadcast in some + circumstances. A variable can only meaningfully be "shared" across workers + or vector lanes if its address is taken, e.g. by a call to an atomic + builtin. + + From an optimisation perspective, the answer might be fuzzier: maybe + sometimes, using shared memory directly would be faster than + broadcasting. */ + +static void +oacc_privatization_begin_diagnose_var (const dump_flags_t l_dump_flags, + const location_t loc, const tree c, + const tree decl) +{ + const dump_user_location_t d_u_loc + = dump_user_location_t::from_location_t (loc); +/* PR100695 "Format decoder, quoting in 'dump_printf' etc." */ +#if __GNUC__ >= 10 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" +#endif + dump_printf_loc (l_dump_flags, d_u_loc, + "variable %<%T%> ", decl); +#if __GNUC__ >= 10 +# pragma GCC diagnostic pop +#endif + if (c) + dump_printf (l_dump_flags, + "in %qs clause ", + omp_clause_code_name[OMP_CLAUSE_CODE (c)]); + else + dump_printf (l_dump_flags, + "declared in block "); +} + +static bool +oacc_privatization_candidate_p (const location_t loc, const tree c, + const tree decl) +{ + dump_flags_t l_dump_flags = get_openacc_privatization_dump_flags (); + + /* There is some differentiation depending on block vs. clause. */ + bool block = !c; + + bool res = true; + + if (res && !VAR_P (decl)) + { + res = false; + + if (dump_enabled_p ()) + { + oacc_privatization_begin_diagnose_var (l_dump_flags, loc, c, decl); + dump_printf (l_dump_flags, + "potentially has improper OpenACC privatization level: %qs\n", + get_tree_code_name (TREE_CODE (decl))); + } + } + + if (res && block && TREE_STATIC (decl)) + { + res = false; + + if (dump_enabled_p ()) + { + oacc_privatization_begin_diagnose_var (l_dump_flags, loc, c, decl); + dump_printf (l_dump_flags, + "isn%'t candidate for adjusting OpenACC privatization level: %s\n", + "static"); + } + } + + if (res && block && DECL_EXTERNAL (decl)) + { + res = false; + + if (dump_enabled_p ()) + { + oacc_privatization_begin_diagnose_var (l_dump_flags, loc, c, decl); + dump_printf (l_dump_flags, + "isn%'t candidate for adjusting OpenACC privatization level: %s\n", + "external"); + } + } + + if (res && !TREE_ADDRESSABLE (decl)) + { + res = false; + + if (dump_enabled_p ()) + { + oacc_privatization_begin_diagnose_var (l_dump_flags, loc, c, decl); + dump_printf (l_dump_flags, + "isn%'t candidate for adjusting OpenACC privatization level: %s\n", + "not addressable"); + } + } + + if (res) + { + if (dump_enabled_p ()) + { + oacc_privatization_begin_diagnose_var (l_dump_flags, loc, c, decl); + dump_printf (l_dump_flags, + "is candidate for adjusting OpenACC privatization level\n"); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_decl (dump_file, decl, dump_flags); + fprintf (dump_file, "\n"); + } + + return res; +} + +/* Scan CLAUSES for candidates for adjusting OpenACC privatization level in + CTX. */ + +static void +oacc_privatization_scan_clause_chain (omp_context *ctx, tree clauses) +{ + for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE) + { + tree decl = OMP_CLAUSE_DECL (c); + + if (!oacc_privatization_candidate_p (OMP_CLAUSE_LOCATION (c), c, decl)) + continue; + + gcc_checking_assert (!ctx->oacc_privatization_candidates.contains (decl)); + ctx->oacc_privatization_candidates.safe_push (decl); + } +} + +/* Scan DECLS for candidates for adjusting OpenACC privatization level in + CTX. */ + +static void +oacc_privatization_scan_decl_chain (omp_context *ctx, tree decls) +{ + for (tree decl = decls; decl; decl = DECL_CHAIN (decl)) + { + if (!oacc_privatization_candidate_p (gimple_location (ctx->stmt), NULL, decl)) + continue; + + gcc_checking_assert (!ctx->oacc_privatization_candidates.contains (decl)); + ctx->oacc_privatization_candidates.safe_push (decl); + } +} + +/* Callback for walk_gimple_seq. Find #pragma omp scan statement. */ + +static tree +omp_find_scan (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + switch (gimple_code (stmt)) + { + WALK_SUBSTMTS; + + case GIMPLE_OMP_FOR: + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_SIMD + && gimple_omp_for_combined_into_p (stmt)) + *handled_ops_p = false; + break; + + case GIMPLE_OMP_SCAN: + *(gimple_stmt_iterator *) (wi->info) = *gsi_p; + return integer_zero_node; + default: + break; + } + return NULL; +} + +/* Helper function for lower_omp_for, add transformations for a worksharing + loop with scan directives inside of it. + For worksharing loop not combined with simd, transform: + #pragma omp for reduction(inscan,+:r) private(i) + for (i = 0; i < n; i = i + 1) + { + { + update (r); + } + #pragma omp scan inclusive(r) + { + use (r); + } + } + + into two worksharing loops + code to merge results: + + num_threads = omp_get_num_threads (); + thread_num = omp_get_thread_num (); + if (thread_num == 0) goto <D.2099>; else goto <D.2100>; + <D.2099>: + var2 = r; + goto <D.2101>; + <D.2100>: + // For UDRs this is UDR init, or if ctors are needed, copy from + // var3 that has been constructed to contain the neutral element. + var2 = 0; + <D.2101>: + ivar = 0; + // The _scantemp_ clauses will arrange for rpriva to be initialized to + // a shared array with num_threads elements and rprivb to a local array + // number of elements equal to the number of (contiguous) iterations the + // current thread will perform. controlb and controlp variables are + // temporaries to handle deallocation of rprivb at the end of second + // GOMP_FOR. + #pragma omp for _scantemp_(rpriva) _scantemp_(rprivb) _scantemp_(controlb) \ + _scantemp_(controlp) reduction(inscan,+:r) private(i) nowait + for (i = 0; i < n; i = i + 1) + { + { + // For UDRs this is UDR init or copy from var3. + r = 0; + // This is the input phase from user code. + update (r); + } + { + // For UDRs this is UDR merge. + var2 = var2 + r; + // Rather than handing it over to the user, save to local thread's + // array. + rprivb[ivar] = var2; + // For exclusive scan, the above two statements are swapped. + ivar = ivar + 1; + } + } + // And remember the final value from this thread's into the shared + // rpriva array. + rpriva[(sizetype) thread_num] = var2; + // If more than one thread, compute using Work-Efficient prefix sum + // the inclusive parallel scan of the rpriva array. + if (num_threads > 1) goto <D.2102>; else goto <D.2103>; + <D.2102>: + GOMP_barrier (); + down = 0; + k = 1; + num_threadsu = (unsigned int) num_threads; + thread_numup1 = (unsigned int) thread_num + 1; + <D.2108>: + twok = k << 1; + if (twok > num_threadsu) goto <D.2110>; else goto <D.2111>; + <D.2110>: + down = 4294967295; + k = k >> 1; + if (k == num_threadsu) goto <D.2112>; else goto <D.2111>; + <D.2112>: + k = k >> 1; + <D.2111>: + twok = k << 1; + cplx = .MUL_OVERFLOW (thread_nump1, twok); + mul = REALPART_EXPR <cplx>; + ovf = IMAGPART_EXPR <cplx>; + if (ovf == 0) goto <D.2116>; else goto <D.2117>; + <D.2116>: + andv = k & down; + andvm1 = andv + 4294967295; + l = mul + andvm1; + if (l < num_threadsu) goto <D.2120>; else goto <D.2117>; + <D.2120>: + // For UDRs this is UDR merge, performed using var2 variable as temporary, + // i.e. var2 = rpriva[l - k]; UDR merge (var2, rpriva[l]); rpriva[l] = var2; + rpriva[l] = rpriva[l - k] + rpriva[l]; + <D.2117>: + if (down == 0) goto <D.2121>; else goto <D.2122>; + <D.2121>: + k = k << 1; + goto <D.2123>; + <D.2122>: + k = k >> 1; + <D.2123>: + GOMP_barrier (); + if (k != 0) goto <D.2108>; else goto <D.2103>; + <D.2103>: + if (thread_num == 0) goto <D.2124>; else goto <D.2125>; + <D.2124>: + // For UDRs this is UDR init or copy from var3. + var2 = 0; + goto <D.2126>; + <D.2125>: + var2 = rpriva[thread_num - 1]; + <D.2126>: + ivar = 0; + #pragma omp for _scantemp_(controlb) _scantemp_(controlp) \ + reduction(inscan,+:r) private(i) + for (i = 0; i < n; i = i + 1) + { + { + // For UDRs, this is r = var2; UDR merge (r, rprivb[ivar]); + r = var2 + rprivb[ivar]; + } + { + // This is the scan phase from user code. + use (r); + // Plus a bump of the iterator. + ivar = ivar + 1; + } + } */ + +static void +lower_omp_for_scan (gimple_seq *body_p, gimple_seq *dlist, gomp_for *stmt, + struct omp_for_data *fd, omp_context *ctx) +{ + bool is_for_simd = gimple_omp_for_combined_p (stmt); + gcc_assert (ctx->scan_inclusive || ctx->scan_exclusive); + + gimple_seq body = gimple_omp_body (stmt); + gimple_stmt_iterator input1_gsi = gsi_none (); + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input1_gsi; + walk_gimple_seq_mod (&body, omp_find_scan, NULL, &wi); + gcc_assert (!gsi_end_p (input1_gsi)); + + gimple *input_stmt1 = gsi_stmt (input1_gsi); + gimple_stmt_iterator gsi = input1_gsi; + gsi_next (&gsi); + gimple_stmt_iterator scan1_gsi = gsi; + gimple *scan_stmt1 = gsi_stmt (gsi); + gcc_assert (scan_stmt1 && gimple_code (scan_stmt1) == GIMPLE_OMP_SCAN); + + gimple_seq input_body = gimple_omp_body (input_stmt1); + gimple_seq scan_body = gimple_omp_body (scan_stmt1); + gimple_omp_set_body (input_stmt1, NULL); + gimple_omp_set_body (scan_stmt1, NULL); + gimple_omp_set_body (stmt, NULL); + + gomp_for *new_stmt = as_a <gomp_for *> (gimple_copy (stmt)); + gimple_seq new_body = copy_gimple_seq_and_replace_locals (body); + gimple_omp_set_body (stmt, body); + gimple_omp_set_body (input_stmt1, input_body); + + gimple_stmt_iterator input2_gsi = gsi_none (); + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input2_gsi; + walk_gimple_seq_mod (&new_body, omp_find_scan, NULL, &wi); + gcc_assert (!gsi_end_p (input2_gsi)); + + gimple *input_stmt2 = gsi_stmt (input2_gsi); + gsi = input2_gsi; + gsi_next (&gsi); + gimple_stmt_iterator scan2_gsi = gsi; + gimple *scan_stmt2 = gsi_stmt (gsi); + gcc_assert (scan_stmt2 && gimple_code (scan_stmt2) == GIMPLE_OMP_SCAN); + gimple_omp_set_body (scan_stmt2, scan_body); + + gimple_stmt_iterator input3_gsi = gsi_none (); + gimple_stmt_iterator scan3_gsi = gsi_none (); + gimple_stmt_iterator input4_gsi = gsi_none (); + gimple_stmt_iterator scan4_gsi = gsi_none (); + gimple *input_stmt3 = NULL, *scan_stmt3 = NULL; + gimple *input_stmt4 = NULL, *scan_stmt4 = NULL; + omp_context *input_simd_ctx = NULL, *scan_simd_ctx = NULL; + if (is_for_simd) + { + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input3_gsi; + walk_gimple_seq_mod (&input_body, omp_find_scan, NULL, &wi); + gcc_assert (!gsi_end_p (input3_gsi)); + + input_stmt3 = gsi_stmt (input3_gsi); + gsi = input3_gsi; + gsi_next (&gsi); + scan3_gsi = gsi; + scan_stmt3 = gsi_stmt (gsi); + gcc_assert (scan_stmt3 && gimple_code (scan_stmt3) == GIMPLE_OMP_SCAN); + + memset (&wi, 0, sizeof (wi)); + wi.val_only = true; + wi.info = (void *) &input4_gsi; + walk_gimple_seq_mod (&scan_body, omp_find_scan, NULL, &wi); + gcc_assert (!gsi_end_p (input4_gsi)); + + input_stmt4 = gsi_stmt (input4_gsi); + gsi = input4_gsi; + gsi_next (&gsi); + scan4_gsi = gsi; + scan_stmt4 = gsi_stmt (gsi); + gcc_assert (scan_stmt4 && gimple_code (scan_stmt4) == GIMPLE_OMP_SCAN); + + input_simd_ctx = maybe_lookup_ctx (input_stmt3)->outer; + scan_simd_ctx = maybe_lookup_ctx (input_stmt4)->outer; + } + + tree num_threads = create_tmp_var (integer_type_node); + tree thread_num = create_tmp_var (integer_type_node); + tree nthreads_decl = builtin_decl_explicit (BUILT_IN_OMP_GET_NUM_THREADS); + tree threadnum_decl = builtin_decl_explicit (BUILT_IN_OMP_GET_THREAD_NUM); + gimple *g = gimple_build_call (nthreads_decl, 0); + gimple_call_set_lhs (g, num_threads); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_call (threadnum_decl, 0); + gimple_call_set_lhs (g, thread_num); + gimple_seq_add_stmt (body_p, g); + + tree ivar = create_tmp_var (sizetype); + tree new_clauses1 = NULL_TREE, new_clauses2 = NULL_TREE; + tree *cp1 = &new_clauses1, *cp2 = &new_clauses2; + tree k = create_tmp_var (unsigned_type_node); + tree l = create_tmp_var (unsigned_type_node); + + gimple_seq clist = NULL, mdlist = NULL; + gimple_seq thr01_list = NULL, thrn1_list = NULL; + gimple_seq thr02_list = NULL, thrn2_list = NULL; + gimple_seq scan1_list = NULL, input2_list = NULL; + gimple_seq last_list = NULL, reduc_list = NULL; + for (tree c = gimple_omp_for_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (c)) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + tree var = OMP_CLAUSE_DECL (c); + tree new_var = lookup_decl (var, ctx); + tree var3 = NULL_TREE; + tree new_vard = new_var; + if (omp_privatize_by_reference (var)) + new_var = build_simple_mem_ref_loc (clause_loc, new_var); + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + var3 = maybe_lookup_decl (new_vard, ctx); + if (var3 == new_vard) + var3 = NULL_TREE; + } + + tree ptype = build_pointer_type (TREE_TYPE (new_var)); + tree rpriva = create_tmp_var (ptype); + tree nc = build_omp_clause (clause_loc, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = rpriva; + *cp1 = nc; + cp1 = &OMP_CLAUSE_CHAIN (nc); + + tree rprivb = create_tmp_var (ptype); + nc = build_omp_clause (clause_loc, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = rprivb; + OMP_CLAUSE__SCANTEMP__ALLOC (nc) = 1; + *cp1 = nc; + cp1 = &OMP_CLAUSE_CHAIN (nc); + + tree var2 = create_tmp_var_raw (TREE_TYPE (new_var)); + if (new_vard != new_var) + TREE_ADDRESSABLE (var2) = 1; + gimple_add_tmp_var (var2); + + tree x = fold_convert_loc (clause_loc, sizetype, thread_num); + x = fold_build2_loc (clause_loc, MULT_EXPR, sizetype, x, + TYPE_SIZE_UNIT (TREE_TYPE (ptype))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (rpriva), rpriva, x); + tree rpriva_ref = build_simple_mem_ref_loc (clause_loc, x); + + x = fold_build2_loc (clause_loc, PLUS_EXPR, integer_type_node, + thread_num, integer_minus_one_node); + x = fold_convert_loc (clause_loc, sizetype, x); + x = fold_build2_loc (clause_loc, MULT_EXPR, sizetype, x, + TYPE_SIZE_UNIT (TREE_TYPE (ptype))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (rpriva), rpriva, x); + tree rprivam1_ref = build_simple_mem_ref_loc (clause_loc, x); + + x = fold_convert_loc (clause_loc, sizetype, l); + x = fold_build2_loc (clause_loc, MULT_EXPR, sizetype, x, + TYPE_SIZE_UNIT (TREE_TYPE (ptype))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (rpriva), rpriva, x); + tree rprival_ref = build_simple_mem_ref_loc (clause_loc, x); + + x = fold_build2_loc (clause_loc, MINUS_EXPR, unsigned_type_node, l, k); + x = fold_convert_loc (clause_loc, sizetype, x); + x = fold_build2_loc (clause_loc, MULT_EXPR, sizetype, x, + TYPE_SIZE_UNIT (TREE_TYPE (ptype))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (rpriva), rpriva, x); + tree rprivalmk_ref = build_simple_mem_ref_loc (clause_loc, x); + + x = fold_build2_loc (clause_loc, MULT_EXPR, sizetype, ivar, + TYPE_SIZE_UNIT (TREE_TYPE (ptype))); + x = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (rprivb), rprivb, x); + tree rprivb_ref = build_simple_mem_ref_loc (clause_loc, x); + + tree var4 = is_for_simd ? new_var : var2; + tree var5 = NULL_TREE, var6 = NULL_TREE; + if (is_for_simd) + { + var5 = lookup_decl (var, input_simd_ctx); + var6 = lookup_decl (var, scan_simd_ctx); + if (new_vard != new_var) + { + var5 = build_simple_mem_ref_loc (clause_loc, var5); + var6 = build_simple_mem_ref_loc (clause_loc, var6); + } + } + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + tree val = var2; + + x = lang_hooks.decls.omp_clause_default_ctor + (c, var2, build_outer_var_ref (var, ctx)); + if (x) + gimplify_and_add (x, &clist); + + x = build_outer_var_ref (var, ctx); + x = lang_hooks.decls.omp_clause_assign_op (c, unshare_expr (var4), + x); + gimplify_and_add (x, &thr01_list); + + tree y = (DECL_HAS_VALUE_EXPR_P (new_vard) + ? DECL_VALUE_EXPR (new_vard) : NULL_TREE); + if (var3) + { + x = unshare_expr (var4); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var3); + gimplify_and_add (x, &thrn1_list); + x = unshare_expr (var4); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var3); + gimplify_and_add (x, &thr02_list); + } + else if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + /* Otherwise, assign to it the identity element. */ + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + tseq = copy_gimple_seq_and_replace_locals (tseq); + if (!is_for_simd) + { + if (new_vard != new_var) + val = build_fold_addr_expr_loc (clause_loc, val); + SET_DECL_VALUE_EXPR (new_vard, val); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + SET_DECL_VALUE_EXPR (placeholder, error_mark_node); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&thrn1_list, tseq); + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&thr02_list, tseq); + SET_DECL_VALUE_EXPR (placeholder, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + if (y) + SET_DECL_VALUE_EXPR (new_vard, y); + else + { + DECL_HAS_VALUE_EXPR_P (new_vard) = 0; + SET_DECL_VALUE_EXPR (new_vard, NULL_TREE); + } + } + + x = unshare_expr (var4); + x = lang_hooks.decls.omp_clause_assign_op (c, x, rprivam1_ref); + gimplify_and_add (x, &thrn2_list); + + if (is_for_simd) + { + x = unshare_expr (rprivb_ref); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var5); + gimplify_and_add (x, &scan1_list); + } + else + { + if (ctx->scan_exclusive) + { + x = unshare_expr (rprivb_ref); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var2); + gimplify_and_add (x, &scan1_list); + } + + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + tseq = copy_gimple_seq_and_replace_locals (tseq); + SET_DECL_VALUE_EXPR (placeholder, var2); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&scan1_list, tseq); + + if (ctx->scan_inclusive) + { + x = unshare_expr (rprivb_ref); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var2); + gimplify_and_add (x, &scan1_list); + } + } + + x = unshare_expr (rpriva_ref); + x = lang_hooks.decls.omp_clause_assign_op (c, x, + unshare_expr (var4)); + gimplify_and_add (x, &mdlist); + + x = unshare_expr (is_for_simd ? var6 : new_var); + x = lang_hooks.decls.omp_clause_assign_op (c, x, var4); + gimplify_and_add (x, &input2_list); + + val = rprivb_ref; + if (new_vard != new_var) + val = build_fold_addr_expr_loc (clause_loc, val); + + gimple_seq tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + tseq = copy_gimple_seq_and_replace_locals (tseq); + SET_DECL_VALUE_EXPR (new_vard, val); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + if (is_for_simd) + { + SET_DECL_VALUE_EXPR (placeholder, var6); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + } + else + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + lower_omp (&tseq, ctx); + if (y) + SET_DECL_VALUE_EXPR (new_vard, y); + else + { + DECL_HAS_VALUE_EXPR_P (new_vard) = 0; + SET_DECL_VALUE_EXPR (new_vard, NULL_TREE); + } + if (!is_for_simd) + { + SET_DECL_VALUE_EXPR (placeholder, new_var); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + lower_omp (&tseq, ctx); + } + gimple_seq_add_seq (&input2_list, tseq); + + x = build_outer_var_ref (var, ctx); + x = lang_hooks.decls.omp_clause_assign_op (c, x, rpriva_ref); + gimplify_and_add (x, &last_list); + + x = lang_hooks.decls.omp_clause_assign_op (c, var2, rprivalmk_ref); + gimplify_and_add (x, &reduc_list); + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + tseq = copy_gimple_seq_and_replace_locals (tseq); + val = rprival_ref; + if (new_vard != new_var) + val = build_fold_addr_expr_loc (clause_loc, val); + SET_DECL_VALUE_EXPR (new_vard, val); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + SET_DECL_VALUE_EXPR (placeholder, var2); + lower_omp (&tseq, ctx); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + SET_DECL_VALUE_EXPR (placeholder, NULL_TREE); + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (y) + SET_DECL_VALUE_EXPR (new_vard, y); + else + { + DECL_HAS_VALUE_EXPR_P (new_vard) = 0; + SET_DECL_VALUE_EXPR (new_vard, NULL_TREE); + } + gimple_seq_add_seq (&reduc_list, tseq); + x = lang_hooks.decls.omp_clause_assign_op (c, rprival_ref, var2); + gimplify_and_add (x, &reduc_list); + + x = lang_hooks.decls.omp_clause_dtor (c, var2); + if (x) + gimplify_and_add (x, dlist); + } + else + { + x = build_outer_var_ref (var, ctx); + gimplify_assign (unshare_expr (var4), x, &thr01_list); + + x = omp_reduction_init (c, TREE_TYPE (new_var)); + gimplify_assign (unshare_expr (var4), unshare_expr (x), + &thrn1_list); + gimplify_assign (unshare_expr (var4), x, &thr02_list); + + gimplify_assign (unshare_expr (var4), rprivam1_ref, &thrn2_list); + + enum tree_code code = OMP_CLAUSE_REDUCTION_CODE (c); + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + if (is_for_simd) + gimplify_assign (unshare_expr (rprivb_ref), var5, &scan1_list); + else + { + if (ctx->scan_exclusive) + gimplify_assign (unshare_expr (rprivb_ref), var2, + &scan1_list); + x = build2 (code, TREE_TYPE (new_var), var2, new_var); + gimplify_assign (var2, x, &scan1_list); + if (ctx->scan_inclusive) + gimplify_assign (unshare_expr (rprivb_ref), var2, + &scan1_list); + } + + gimplify_assign (unshare_expr (rpriva_ref), unshare_expr (var4), + &mdlist); + + x = build2 (code, TREE_TYPE (new_var), var4, rprivb_ref); + gimplify_assign (is_for_simd ? var6 : new_var, x, &input2_list); + + gimplify_assign (build_outer_var_ref (var, ctx), rpriva_ref, + &last_list); + + x = build2 (code, TREE_TYPE (new_var), rprivalmk_ref, + unshare_expr (rprival_ref)); + gimplify_assign (rprival_ref, x, &reduc_list); + } + } + + g = gimple_build_assign (ivar, PLUS_EXPR, ivar, size_one_node); + gimple_seq_add_stmt (&scan1_list, g); + g = gimple_build_assign (ivar, PLUS_EXPR, ivar, size_one_node); + gimple_seq_add_stmt (gimple_omp_body_ptr (is_for_simd + ? scan_stmt4 : scan_stmt2), g); + + tree controlb = create_tmp_var (boolean_type_node); + tree controlp = create_tmp_var (ptr_type_node); + tree nc = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = controlb; + OMP_CLAUSE__SCANTEMP__CONTROL (nc) = 1; + *cp1 = nc; + cp1 = &OMP_CLAUSE_CHAIN (nc); + nc = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = controlp; + OMP_CLAUSE__SCANTEMP__CONTROL (nc) = 1; + *cp1 = nc; + cp1 = &OMP_CLAUSE_CHAIN (nc); + nc = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = controlb; + OMP_CLAUSE__SCANTEMP__CONTROL (nc) = 1; + *cp2 = nc; + cp2 = &OMP_CLAUSE_CHAIN (nc); + nc = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SCANTEMP_); + OMP_CLAUSE_DECL (nc) = controlp; + OMP_CLAUSE__SCANTEMP__CONTROL (nc) = 1; + *cp2 = nc; + cp2 = &OMP_CLAUSE_CHAIN (nc); + + *cp1 = gimple_omp_for_clauses (stmt); + gimple_omp_for_set_clauses (stmt, new_clauses1); + *cp2 = gimple_omp_for_clauses (new_stmt); + gimple_omp_for_set_clauses (new_stmt, new_clauses2); + + if (is_for_simd) + { + gimple_seq_add_seq (gimple_omp_body_ptr (scan_stmt3), scan1_list); + gimple_seq_add_seq (gimple_omp_body_ptr (input_stmt4), input2_list); + + gsi_insert_seq_after (&input3_gsi, gimple_omp_body (input_stmt3), + GSI_SAME_STMT); + gsi_remove (&input3_gsi, true); + gsi_insert_seq_after (&scan3_gsi, gimple_omp_body (scan_stmt3), + GSI_SAME_STMT); + gsi_remove (&scan3_gsi, true); + gsi_insert_seq_after (&input4_gsi, gimple_omp_body (input_stmt4), + GSI_SAME_STMT); + gsi_remove (&input4_gsi, true); + gsi_insert_seq_after (&scan4_gsi, gimple_omp_body (scan_stmt4), + GSI_SAME_STMT); + gsi_remove (&scan4_gsi, true); + } + else + { + gimple_omp_set_body (scan_stmt1, scan1_list); + gimple_omp_set_body (input_stmt2, input2_list); + } + + gsi_insert_seq_after (&input1_gsi, gimple_omp_body (input_stmt1), + GSI_SAME_STMT); + gsi_remove (&input1_gsi, true); + gsi_insert_seq_after (&scan1_gsi, gimple_omp_body (scan_stmt1), + GSI_SAME_STMT); + gsi_remove (&scan1_gsi, true); + gsi_insert_seq_after (&input2_gsi, gimple_omp_body (input_stmt2), + GSI_SAME_STMT); + gsi_remove (&input2_gsi, true); + gsi_insert_seq_after (&scan2_gsi, gimple_omp_body (scan_stmt2), + GSI_SAME_STMT); + gsi_remove (&scan2_gsi, true); + + gimple_seq_add_seq (body_p, clist); + + tree lab1 = create_artificial_label (UNKNOWN_LOCATION); + tree lab2 = create_artificial_label (UNKNOWN_LOCATION); + tree lab3 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (EQ_EXPR, thread_num, integer_zero_node, lab1, lab2); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab1); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, thr01_list); + g = gimple_build_goto (lab3); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab2); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, thrn1_list); + g = gimple_build_label (lab3); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (ivar, size_zero_node); + gimple_seq_add_stmt (body_p, g); + + gimple_seq_add_stmt (body_p, stmt); + gimple_seq_add_seq (body_p, body); + gimple_seq_add_stmt (body_p, gimple_build_omp_continue (fd->loop.v, + fd->loop.v)); + + g = gimple_build_omp_return (true); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, mdlist); + + lab1 = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (GT_EXPR, num_threads, integer_one_node, lab1, lab2); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab1); + gimple_seq_add_stmt (body_p, g); + + g = omp_build_barrier (NULL); + gimple_seq_add_stmt (body_p, g); + + tree down = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (down, build_zero_cst (unsigned_type_node)); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (k, build_one_cst (unsigned_type_node)); + gimple_seq_add_stmt (body_p, g); + + tree num_threadsu = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (num_threadsu, NOP_EXPR, num_threads); + gimple_seq_add_stmt (body_p, g); + + tree thread_numu = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (thread_numu, NOP_EXPR, thread_num); + gimple_seq_add_stmt (body_p, g); + + tree thread_nump1 = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (thread_nump1, PLUS_EXPR, thread_numu, + build_int_cst (unsigned_type_node, 1)); + gimple_seq_add_stmt (body_p, g); + + lab3 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_label (lab3); + gimple_seq_add_stmt (body_p, g); + + tree twok = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (twok, LSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + + tree lab4 = create_artificial_label (UNKNOWN_LOCATION); + tree lab5 = create_artificial_label (UNKNOWN_LOCATION); + tree lab6 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (GT_EXPR, twok, num_threadsu, lab4, lab5); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab4); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_assign (down, build_all_ones_cst (unsigned_type_node)); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_assign (k, RSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_cond (EQ_EXPR, k, num_threadsu, lab6, lab5); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab6); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (k, RSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_label (lab5); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (twok, LSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + + tree cplx = create_tmp_var (build_complex_type (unsigned_type_node, false)); + g = gimple_build_call_internal (IFN_MUL_OVERFLOW, 2, thread_nump1, twok); + gimple_call_set_lhs (g, cplx); + gimple_seq_add_stmt (body_p, g); + tree mul = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (mul, REALPART_EXPR, + build1 (REALPART_EXPR, unsigned_type_node, cplx)); + gimple_seq_add_stmt (body_p, g); + tree ovf = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (ovf, IMAGPART_EXPR, + build1 (IMAGPART_EXPR, unsigned_type_node, cplx)); + gimple_seq_add_stmt (body_p, g); + + tree lab7 = create_artificial_label (UNKNOWN_LOCATION); + tree lab8 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (EQ_EXPR, ovf, build_zero_cst (unsigned_type_node), + lab7, lab8); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab7); + gimple_seq_add_stmt (body_p, g); + + tree andv = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (andv, BIT_AND_EXPR, k, down); + gimple_seq_add_stmt (body_p, g); + tree andvm1 = create_tmp_var (unsigned_type_node); + g = gimple_build_assign (andvm1, PLUS_EXPR, andv, + build_minus_one_cst (unsigned_type_node)); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (l, PLUS_EXPR, mul, andvm1); + gimple_seq_add_stmt (body_p, g); + + tree lab9 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (LT_EXPR, l, num_threadsu, lab9, lab8); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab9); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, reduc_list); + g = gimple_build_label (lab8); + gimple_seq_add_stmt (body_p, g); + + tree lab10 = create_artificial_label (UNKNOWN_LOCATION); + tree lab11 = create_artificial_label (UNKNOWN_LOCATION); + tree lab12 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (EQ_EXPR, down, build_zero_cst (unsigned_type_node), + lab10, lab11); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab10); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_assign (k, LSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_goto (lab12); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab11); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_assign (k, RSHIFT_EXPR, k, integer_one_node); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab12); + gimple_seq_add_stmt (body_p, g); + + g = omp_build_barrier (NULL); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_cond (NE_EXPR, k, build_zero_cst (unsigned_type_node), + lab3, lab2); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_label (lab2); + gimple_seq_add_stmt (body_p, g); + + lab1 = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + lab3 = create_artificial_label (UNKNOWN_LOCATION); + g = gimple_build_cond (EQ_EXPR, thread_num, integer_zero_node, lab1, lab2); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab1); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, thr02_list); + g = gimple_build_goto (lab3); + gimple_seq_add_stmt (body_p, g); + g = gimple_build_label (lab2); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_seq (body_p, thrn2_list); + g = gimple_build_label (lab3); + gimple_seq_add_stmt (body_p, g); + + g = gimple_build_assign (ivar, size_zero_node); + gimple_seq_add_stmt (body_p, g); + gimple_seq_add_stmt (body_p, new_stmt); + gimple_seq_add_seq (body_p, new_body); + + gimple_seq new_dlist = NULL; + lab1 = create_artificial_label (UNKNOWN_LOCATION); + lab2 = create_artificial_label (UNKNOWN_LOCATION); + tree num_threadsm1 = create_tmp_var (integer_type_node); + g = gimple_build_assign (num_threadsm1, PLUS_EXPR, num_threads, + integer_minus_one_node); + gimple_seq_add_stmt (&new_dlist, g); + g = gimple_build_cond (EQ_EXPR, thread_num, num_threadsm1, lab1, lab2); + gimple_seq_add_stmt (&new_dlist, g); + g = gimple_build_label (lab1); + gimple_seq_add_stmt (&new_dlist, g); + gimple_seq_add_seq (&new_dlist, last_list); + g = gimple_build_label (lab2); + gimple_seq_add_stmt (&new_dlist, g); + gimple_seq_add_seq (&new_dlist, *dlist); + *dlist = new_dlist; +} + +/* Build an internal UNIQUE function with type IFN_UNIQUE_OACC_PRIVATE listing + the addresses of variables to be made private at the surrounding + parallelism level. Such functions appear in the gimple code stream in two + forms, e.g. for a partitioned loop: + + .data_dep.6 = .UNIQUE (OACC_HEAD_MARK, .data_dep.6, 1, 68); + .data_dep.6 = .UNIQUE (OACC_PRIVATE, .data_dep.6, -1, &w); + .data_dep.6 = .UNIQUE (OACC_FORK, .data_dep.6, -1); + .data_dep.6 = .UNIQUE (OACC_HEAD_MARK, .data_dep.6); + + or alternatively, OACC_PRIVATE can appear at the top level of a parallel, + not as part of a HEAD_MARK sequence: + + .UNIQUE (OACC_PRIVATE, 0, 0, &w); + + For such stand-alone appearances, the 3rd argument is always 0, denoting + gang partitioning. */ + +static gcall * +lower_oacc_private_marker (omp_context *ctx) +{ + if (ctx->oacc_privatization_candidates.length () == 0) + return NULL; + + auto_vec<tree, 5> args; + + args.quick_push (build_int_cst (integer_type_node, IFN_UNIQUE_OACC_PRIVATE)); + args.quick_push (integer_zero_node); + args.quick_push (integer_minus_one_node); + + int i; + tree decl; + FOR_EACH_VEC_ELT (ctx->oacc_privatization_candidates, i, decl) + { + for (omp_context *thisctx = ctx; thisctx; thisctx = thisctx->outer) + { + tree inner_decl = maybe_lookup_decl (decl, thisctx); + if (inner_decl) + { + decl = inner_decl; + break; + } + } + gcc_checking_assert (decl); + + tree addr = build_fold_addr_expr (decl); + args.safe_push (addr); + } + + return gimple_build_call_internal_vec (IFN_UNIQUE, args); +} + +/* Lower code for an OMP loop directive. */ + +static void +lower_omp_for (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree *rhs_p, block; + struct omp_for_data fd, *fdp = NULL; + gomp_for *stmt = as_a <gomp_for *> (gsi_stmt (*gsi_p)); + gbind *new_stmt; + gimple_seq omp_for_body, body, dlist, tred_ilist = NULL, tred_dlist = NULL; + gimple_seq cnt_list = NULL, clist = NULL; + gimple_seq oacc_head = NULL, oacc_tail = NULL; + size_t i; + + push_gimplify_context (); + + if (is_gimple_omp_oacc (ctx->stmt)) + oacc_privatization_scan_clause_chain (ctx, gimple_omp_for_clauses (stmt)); + + lower_omp (gimple_omp_for_pre_body_ptr (stmt), ctx); + + block = make_node (BLOCK); + new_stmt = gimple_build_bind (NULL, NULL, block); + /* Replace at gsi right away, so that 'stmt' is no member + of a sequence anymore as we're going to add to a different + one below. */ + gsi_replace (gsi_p, new_stmt, true); + + /* Move declaration of temporaries in the loop body before we make + it go away. */ + omp_for_body = gimple_omp_body (stmt); + if (!gimple_seq_empty_p (omp_for_body) + && gimple_code (gimple_seq_first_stmt (omp_for_body)) == GIMPLE_BIND) + { + gbind *inner_bind + = as_a <gbind *> (gimple_seq_first_stmt (omp_for_body)); + tree vars = gimple_bind_vars (inner_bind); + if (is_gimple_omp_oacc (ctx->stmt)) + oacc_privatization_scan_decl_chain (ctx, vars); + gimple_bind_append_vars (new_stmt, vars); + /* bind_vars/BLOCK_VARS are being moved to new_stmt/block, don't + keep them on the inner_bind and it's block. */ + gimple_bind_set_vars (inner_bind, NULL_TREE); + if (gimple_bind_block (inner_bind)) + BLOCK_VARS (gimple_bind_block (inner_bind)) = NULL_TREE; + } + + if (gimple_omp_for_combined_into_p (stmt)) + { + omp_extract_for_data (stmt, &fd, NULL); + fdp = &fd; + + /* We need two temporaries with fd.loop.v type (istart/iend) + and then (fd.collapse - 1) temporaries with the same + type for count2 ... countN-1 vars if not constant. */ + size_t count = 2; + tree type = fd.iter_type; + if (fd.collapse > 1 + && TREE_CODE (fd.loop.n2) != INTEGER_CST) + count += fd.collapse - 1; + size_t count2 = 0; + tree type2 = NULL_TREE; + bool taskreg_for + = (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_FOR + || gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_TASKLOOP); + tree outerc = NULL, *pc = gimple_omp_for_clauses_ptr (stmt); + tree simtc = NULL; + tree clauses = *pc; + if (fd.collapse > 1 + && fd.non_rect + && fd.last_nonrect == fd.first_nonrect + 1 + && TREE_CODE (fd.loop.n2) != INTEGER_CST) + if (tree v = gimple_omp_for_index (stmt, fd.last_nonrect)) + if (!TYPE_UNSIGNED (TREE_TYPE (v))) + { + v = gimple_omp_for_index (stmt, fd.first_nonrect); + type2 = TREE_TYPE (v); + count++; + count2 = 3; + } + if (taskreg_for) + outerc + = omp_find_clause (gimple_omp_taskreg_clauses (ctx->outer->stmt), + OMP_CLAUSE__LOOPTEMP_); + if (ctx->simt_stmt) + simtc = omp_find_clause (gimple_omp_for_clauses (ctx->simt_stmt), + OMP_CLAUSE__LOOPTEMP_); + for (i = 0; i < count + count2; i++) + { + tree temp; + if (taskreg_for) + { + gcc_assert (outerc); + temp = lookup_decl (OMP_CLAUSE_DECL (outerc), ctx->outer); + outerc = omp_find_clause (OMP_CLAUSE_CHAIN (outerc), + OMP_CLAUSE__LOOPTEMP_); + } + else + { + /* If there are 2 adjacent SIMD stmts, one with _simt_ + clause, another without, make sure they have the same + decls in _looptemp_ clauses, because the outer stmt + they are combined into will look up just one inner_stmt. */ + if (ctx->simt_stmt) + temp = OMP_CLAUSE_DECL (simtc); + else + temp = create_tmp_var (i >= count ? type2 : type); + insert_decl_map (&ctx->outer->cb, temp, temp); + } + *pc = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__LOOPTEMP_); + OMP_CLAUSE_DECL (*pc) = temp; + pc = &OMP_CLAUSE_CHAIN (*pc); + if (ctx->simt_stmt) + simtc = omp_find_clause (OMP_CLAUSE_CHAIN (simtc), + OMP_CLAUSE__LOOPTEMP_); + } + *pc = clauses; + } + + /* The pre-body and input clauses go before the lowered GIMPLE_OMP_FOR. */ + dlist = NULL; + body = NULL; + tree rclauses + = omp_task_reductions_find_first (gimple_omp_for_clauses (stmt), OMP_FOR, + OMP_CLAUSE_REDUCTION); + tree rtmp = NULL_TREE; + if (rclauses) + { + tree type = build_pointer_type (pointer_sized_int_node); + tree temp = create_tmp_var (type); + tree c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__REDUCTEMP_); + OMP_CLAUSE_DECL (c) = temp; + OMP_CLAUSE_CHAIN (c) = gimple_omp_for_clauses (stmt); + gimple_omp_for_set_clauses (stmt, c); + lower_omp_task_reductions (ctx, OMP_FOR, + gimple_omp_for_clauses (stmt), + &tred_ilist, &tred_dlist); + rclauses = c; + rtmp = make_ssa_name (type); + gimple_seq_add_stmt (&body, gimple_build_assign (rtmp, temp)); + } + + lower_lastprivate_conditional_clauses (gimple_omp_for_clauses_ptr (stmt), + ctx); + + lower_rec_input_clauses (gimple_omp_for_clauses (stmt), &body, &dlist, ctx, + fdp); + gimple_seq_add_seq (rclauses ? &tred_ilist : &body, + gimple_omp_for_pre_body (stmt)); + + lower_omp (gimple_omp_body_ptr (stmt), ctx); + + gcall *private_marker = NULL; + if (is_gimple_omp_oacc (ctx->stmt) + && !gimple_seq_empty_p (omp_for_body)) + private_marker = lower_oacc_private_marker (ctx); + + /* Lower the header expressions. At this point, we can assume that + the header is of the form: + + #pragma omp for (V = VAL1; V {<|>|<=|>=} VAL2; V = V [+-] VAL3) + + We just need to make sure that VAL1, VAL2 and VAL3 are lowered + using the .omp_data_s mapping, if needed. */ + for (i = 0; i < gimple_omp_for_collapse (stmt); i++) + { + rhs_p = gimple_omp_for_initial_ptr (stmt, i); + if (TREE_CODE (*rhs_p) == TREE_VEC) + { + if (!is_gimple_min_invariant (TREE_VEC_ELT (*rhs_p, 1))) + TREE_VEC_ELT (*rhs_p, 1) + = get_formal_tmp_var (TREE_VEC_ELT (*rhs_p, 1), &cnt_list); + if (!is_gimple_min_invariant (TREE_VEC_ELT (*rhs_p, 2))) + TREE_VEC_ELT (*rhs_p, 2) + = get_formal_tmp_var (TREE_VEC_ELT (*rhs_p, 2), &cnt_list); + } + else if (!is_gimple_min_invariant (*rhs_p)) + *rhs_p = get_formal_tmp_var (*rhs_p, &cnt_list); + else if (TREE_CODE (*rhs_p) == ADDR_EXPR) + recompute_tree_invariant_for_addr_expr (*rhs_p); + + rhs_p = gimple_omp_for_final_ptr (stmt, i); + if (TREE_CODE (*rhs_p) == TREE_VEC) + { + if (!is_gimple_min_invariant (TREE_VEC_ELT (*rhs_p, 1))) + TREE_VEC_ELT (*rhs_p, 1) + = get_formal_tmp_var (TREE_VEC_ELT (*rhs_p, 1), &cnt_list); + if (!is_gimple_min_invariant (TREE_VEC_ELT (*rhs_p, 2))) + TREE_VEC_ELT (*rhs_p, 2) + = get_formal_tmp_var (TREE_VEC_ELT (*rhs_p, 2), &cnt_list); + } + else if (!is_gimple_min_invariant (*rhs_p)) + *rhs_p = get_formal_tmp_var (*rhs_p, &cnt_list); + else if (TREE_CODE (*rhs_p) == ADDR_EXPR) + recompute_tree_invariant_for_addr_expr (*rhs_p); + + rhs_p = &TREE_OPERAND (gimple_omp_for_incr (stmt, i), 1); + if (!is_gimple_min_invariant (*rhs_p)) + *rhs_p = get_formal_tmp_var (*rhs_p, &cnt_list); + } + if (rclauses) + gimple_seq_add_seq (&tred_ilist, cnt_list); + else + gimple_seq_add_seq (&body, cnt_list); + + /* Once lowered, extract the bounds and clauses. */ + omp_extract_for_data (stmt, &fd, NULL); + + if (is_gimple_omp_oacc (ctx->stmt) + && !ctx_in_oacc_kernels_region (ctx)) + lower_oacc_head_tail (gimple_location (stmt), + gimple_omp_for_clauses (stmt), private_marker, + &oacc_head, &oacc_tail, ctx); + + /* Add OpenACC partitioning and reduction markers just before the loop. */ + if (oacc_head) + gimple_seq_add_seq (&body, oacc_head); + + lower_omp_for_lastprivate (&fd, &body, &dlist, &clist, ctx); + + if (gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_FOR) + for (tree c = gimple_omp_for_clauses (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR + && !OMP_CLAUSE_LINEAR_NO_COPYIN (c)) + { + OMP_CLAUSE_DECL (c) = lookup_decl (OMP_CLAUSE_DECL (c), ctx); + if (DECL_P (OMP_CLAUSE_LINEAR_STEP (c))) + OMP_CLAUSE_LINEAR_STEP (c) + = maybe_lookup_decl_in_outer_ctx (OMP_CLAUSE_LINEAR_STEP (c), + ctx); + } + + if ((ctx->scan_inclusive || ctx->scan_exclusive) + && gimple_omp_for_kind (stmt) == GF_OMP_FOR_KIND_FOR) + lower_omp_for_scan (&body, &dlist, stmt, &fd, ctx); + else + { + gimple_seq_add_stmt (&body, stmt); + gimple_seq_add_seq (&body, gimple_omp_body (stmt)); + } + + gimple_seq_add_stmt (&body, gimple_build_omp_continue (fd.loop.v, + fd.loop.v)); + + /* After the loop, add exit clauses. */ + lower_reduction_clauses (gimple_omp_for_clauses (stmt), &body, &clist, ctx); + + if (clist) + { + tree fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_START); + gcall *g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&body, g); + gimple_seq_add_seq (&body, clist); + fndecl = builtin_decl_explicit (BUILT_IN_GOMP_ATOMIC_END); + g = gimple_build_call (fndecl, 0); + gimple_seq_add_stmt (&body, g); + } + + if (ctx->cancellable) + gimple_seq_add_stmt (&body, gimple_build_label (ctx->cancel_label)); + + gimple_seq_add_seq (&body, dlist); + + if (rclauses) + { + gimple_seq_add_seq (&tred_ilist, body); + body = tred_ilist; + } + + body = maybe_catch_exception (body); + + /* Region exit marker goes at the end of the loop body. */ + gimple *g = gimple_build_omp_return (fd.have_nowait); + gimple_seq_add_stmt (&body, g); + + gimple_seq_add_seq (&body, tred_dlist); + + maybe_add_implicit_barrier_cancel (ctx, g, &body); + + if (rclauses) + OMP_CLAUSE_DECL (rclauses) = rtmp; + + /* Add OpenACC joining and reduction markers just after the loop. */ + if (oacc_tail) + gimple_seq_add_seq (&body, oacc_tail); + + pop_gimplify_context (new_stmt); + + gimple_bind_append_vars (new_stmt, ctx->block_vars); + maybe_remove_omp_member_access_dummy_vars (new_stmt); + BLOCK_VARS (block) = gimple_bind_vars (new_stmt); + if (BLOCK_VARS (block)) + TREE_USED (block) = 1; + + gimple_bind_set_body (new_stmt, body); + gimple_omp_set_body (stmt, NULL); + gimple_omp_for_set_pre_body (stmt, NULL); +} + +/* Callback for walk_stmts. Check if the current statement only contains + GIMPLE_OMP_FOR or GIMPLE_OMP_SECTIONS. */ + +static tree +check_combined_parallel (gimple_stmt_iterator *gsi_p, + bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + int *info = (int *) wi->info; + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + switch (gimple_code (stmt)) + { + WALK_SUBSTMTS; + + case GIMPLE_DEBUG: + break; + case GIMPLE_OMP_FOR: + case GIMPLE_OMP_SECTIONS: + *info = *info == 0 ? 1 : -1; + break; + default: + *info = -1; + break; + } + return NULL; +} + +struct omp_taskcopy_context +{ + /* This field must be at the beginning, as we do "inheritance": Some + callback functions for tree-inline.c (e.g., omp_copy_decl) + receive a copy_body_data pointer that is up-casted to an + omp_context pointer. */ + copy_body_data cb; + omp_context *ctx; +}; + +static tree +task_copyfn_copy_decl (tree var, copy_body_data *cb) +{ + struct omp_taskcopy_context *tcctx = (struct omp_taskcopy_context *) cb; + + if (splay_tree_lookup (tcctx->ctx->sfield_map, (splay_tree_key) var)) + return create_tmp_var (TREE_TYPE (var)); + + return var; +} + +static tree +task_copyfn_remap_type (struct omp_taskcopy_context *tcctx, tree orig_type) +{ + tree name, new_fields = NULL, type, f; + + type = lang_hooks.types.make_type (RECORD_TYPE); + name = DECL_NAME (TYPE_NAME (orig_type)); + name = build_decl (gimple_location (tcctx->ctx->stmt), + TYPE_DECL, name, type); + TYPE_NAME (type) = name; + + for (f = TYPE_FIELDS (orig_type); f ; f = TREE_CHAIN (f)) + { + tree new_f = copy_node (f); + DECL_CONTEXT (new_f) = type; + TREE_TYPE (new_f) = remap_type (TREE_TYPE (f), &tcctx->cb); + TREE_CHAIN (new_f) = new_fields; + walk_tree (&DECL_SIZE (new_f), copy_tree_body_r, &tcctx->cb, NULL); + walk_tree (&DECL_SIZE_UNIT (new_f), copy_tree_body_r, &tcctx->cb, NULL); + walk_tree (&DECL_FIELD_OFFSET (new_f), copy_tree_body_r, + &tcctx->cb, NULL); + new_fields = new_f; + tcctx->cb.decl_map->put (f, new_f); + } + TYPE_FIELDS (type) = nreverse (new_fields); + layout_type (type); + return type; +} + +/* Create task copyfn. */ + +static void +create_task_copyfn (gomp_task *task_stmt, omp_context *ctx) +{ + struct function *child_cfun; + tree child_fn, t, c, src, dst, f, sf, arg, sarg, decl; + tree record_type, srecord_type, bind, list; + bool record_needs_remap = false, srecord_needs_remap = false; + splay_tree_node n; + struct omp_taskcopy_context tcctx; + location_t loc = gimple_location (task_stmt); + size_t looptempno = 0; + + child_fn = gimple_omp_task_copy_fn (task_stmt); + child_cfun = DECL_STRUCT_FUNCTION (child_fn); + gcc_assert (child_cfun->cfg == NULL); + DECL_SAVED_TREE (child_fn) = alloc_stmt_list (); + + /* Reset DECL_CONTEXT on function arguments. */ + for (t = DECL_ARGUMENTS (child_fn); t; t = DECL_CHAIN (t)) + DECL_CONTEXT (t) = child_fn; + + /* Populate the function. */ + push_gimplify_context (); + push_cfun (child_cfun); + + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); + TREE_SIDE_EFFECTS (bind) = 1; + list = NULL; + DECL_SAVED_TREE (child_fn) = bind; + DECL_SOURCE_LOCATION (child_fn) = gimple_location (task_stmt); + + /* Remap src and dst argument types if needed. */ + record_type = ctx->record_type; + srecord_type = ctx->srecord_type; + for (f = TYPE_FIELDS (record_type); f ; f = DECL_CHAIN (f)) + if (variably_modified_type_p (TREE_TYPE (f), ctx->cb.src_fn)) + { + record_needs_remap = true; + break; + } + for (f = TYPE_FIELDS (srecord_type); f ; f = DECL_CHAIN (f)) + if (variably_modified_type_p (TREE_TYPE (f), ctx->cb.src_fn)) + { + srecord_needs_remap = true; + break; + } + + if (record_needs_remap || srecord_needs_remap) + { + memset (&tcctx, '\0', sizeof (tcctx)); + tcctx.cb.src_fn = ctx->cb.src_fn; + tcctx.cb.dst_fn = child_fn; + tcctx.cb.src_node = cgraph_node::get (tcctx.cb.src_fn); + gcc_checking_assert (tcctx.cb.src_node); + tcctx.cb.dst_node = tcctx.cb.src_node; + tcctx.cb.src_cfun = ctx->cb.src_cfun; + tcctx.cb.copy_decl = task_copyfn_copy_decl; + tcctx.cb.eh_lp_nr = 0; + tcctx.cb.transform_call_graph_edges = CB_CGE_MOVE; + tcctx.cb.decl_map = new hash_map<tree, tree>; + tcctx.ctx = ctx; + + if (record_needs_remap) + record_type = task_copyfn_remap_type (&tcctx, record_type); + if (srecord_needs_remap) + srecord_type = task_copyfn_remap_type (&tcctx, srecord_type); + } + else + tcctx.cb.decl_map = NULL; + + arg = DECL_ARGUMENTS (child_fn); + TREE_TYPE (arg) = build_pointer_type (record_type); + sarg = DECL_CHAIN (arg); + TREE_TYPE (sarg) = build_pointer_type (srecord_type); + + /* First pass: initialize temporaries used in record_type and srecord_type + sizes and field offsets. */ + if (tcctx.cb.decl_map) + for (c = gimple_omp_task_clauses (task_stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + tree *p; + + decl = OMP_CLAUSE_DECL (c); + p = tcctx.cb.decl_map->get (decl); + if (p == NULL) + continue; + n = splay_tree_lookup (ctx->sfield_map, (splay_tree_key) decl); + sf = (tree) n->value; + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + t = build2 (MODIFY_EXPR, TREE_TYPE (*p), *p, src); + append_to_statement_list (t, &list); + } + + /* Second pass: copy shared var pointers and copy construct non-VLA + firstprivate vars. */ + for (c = gimple_omp_task_clauses (task_stmt); c; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + splay_tree_key key; + case OMP_CLAUSE_SHARED: + decl = OMP_CLAUSE_DECL (c); + key = (splay_tree_key) decl; + if (OMP_CLAUSE_SHARED_FIRSTPRIVATE (c)) + key = (splay_tree_key) &DECL_UID (decl); + n = splay_tree_lookup (ctx->field_map, key); + if (n == NULL) + break; + f = (tree) n->value; + if (tcctx.cb.decl_map) + f = *tcctx.cb.decl_map->get (f); + n = splay_tree_lookup (ctx->sfield_map, key); + sf = (tree) n->value; + if (tcctx.cb.decl_map) + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + dst = build_simple_mem_ref_loc (loc, arg); + dst = omp_build_component_ref (dst, f); + t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src); + append_to_statement_list (t, &list); + break; + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_IN_REDUCTION: + decl = OMP_CLAUSE_DECL (c); + if (TREE_CODE (decl) == MEM_REF) + { + decl = TREE_OPERAND (decl, 0); + if (TREE_CODE (decl) == POINTER_PLUS_EXPR) + decl = TREE_OPERAND (decl, 0); + if (TREE_CODE (decl) == INDIRECT_REF + || TREE_CODE (decl) == ADDR_EXPR) + decl = TREE_OPERAND (decl, 0); + } + key = (splay_tree_key) decl; + n = splay_tree_lookup (ctx->field_map, key); + if (n == NULL) + break; + f = (tree) n->value; + if (tcctx.cb.decl_map) + f = *tcctx.cb.decl_map->get (f); + n = splay_tree_lookup (ctx->sfield_map, key); + sf = (tree) n->value; + if (tcctx.cb.decl_map) + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + if (decl != OMP_CLAUSE_DECL (c) + && TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE + && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == POINTER_TYPE) + src = build_simple_mem_ref_loc (loc, src); + dst = build_simple_mem_ref_loc (loc, arg); + dst = omp_build_component_ref (dst, f); + t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src); + append_to_statement_list (t, &list); + break; + case OMP_CLAUSE__LOOPTEMP_: + /* Fields for first two _looptemp_ clauses are initialized by + GOMP_taskloop*, the rest are handled like firstprivate. */ + if (looptempno < 2) + { + looptempno++; + break; + } + /* FALLTHRU */ + case OMP_CLAUSE__REDUCTEMP_: + case OMP_CLAUSE_FIRSTPRIVATE: + decl = OMP_CLAUSE_DECL (c); + if (is_variable_sized (decl)) + break; + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) decl); + if (n == NULL) + break; + f = (tree) n->value; + if (tcctx.cb.decl_map) + f = *tcctx.cb.decl_map->get (f); + n = splay_tree_lookup (ctx->sfield_map, (splay_tree_key) decl); + if (n != NULL) + { + sf = (tree) n->value; + if (tcctx.cb.decl_map) + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + if (use_pointer_for_field (decl, NULL) + || omp_privatize_by_reference (decl)) + src = build_simple_mem_ref_loc (loc, src); + } + else + src = decl; + dst = build_simple_mem_ref_loc (loc, arg); + dst = omp_build_component_ref (dst, f); + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_FIRSTPRIVATE) + t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src); + else + { + if (ctx->allocate_map) + if (tree *allocatorp = ctx->allocate_map->get (decl)) + { + tree allocator = *allocatorp; + HOST_WIDE_INT ialign = 0; + if (TREE_CODE (allocator) == TREE_LIST) + { + ialign = tree_to_uhwi (TREE_VALUE (allocator)); + allocator = TREE_PURPOSE (allocator); + } + if (TREE_CODE (allocator) != INTEGER_CST) + { + n = splay_tree_lookup (ctx->sfield_map, + (splay_tree_key) allocator); + allocator = (tree) n->value; + if (tcctx.cb.decl_map) + allocator = *tcctx.cb.decl_map->get (allocator); + tree a = build_simple_mem_ref_loc (loc, sarg); + allocator = omp_build_component_ref (a, allocator); + } + allocator = fold_convert (pointer_sized_int_node, allocator); + tree a = builtin_decl_explicit (BUILT_IN_GOMP_ALLOC); + tree align = build_int_cst (size_type_node, + MAX (ialign, + DECL_ALIGN_UNIT (decl))); + tree sz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (dst))); + tree ptr = build_call_expr_loc (loc, a, 3, align, sz, + allocator); + ptr = fold_convert (TREE_TYPE (dst), ptr); + t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, ptr); + append_to_statement_list (t, &list); + dst = build_simple_mem_ref_loc (loc, dst); + } + t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src); + } + append_to_statement_list (t, &list); + break; + case OMP_CLAUSE_PRIVATE: + if (! OMP_CLAUSE_PRIVATE_OUTER_REF (c)) + break; + decl = OMP_CLAUSE_DECL (c); + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) decl); + f = (tree) n->value; + if (tcctx.cb.decl_map) + f = *tcctx.cb.decl_map->get (f); + n = splay_tree_lookup (ctx->sfield_map, (splay_tree_key) decl); + if (n != NULL) + { + sf = (tree) n->value; + if (tcctx.cb.decl_map) + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + if (use_pointer_for_field (decl, NULL)) + src = build_simple_mem_ref_loc (loc, src); + } + else + src = decl; + dst = build_simple_mem_ref_loc (loc, arg); + dst = omp_build_component_ref (dst, f); + t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src); + append_to_statement_list (t, &list); + break; + default: + break; + } + + /* Last pass: handle VLA firstprivates. */ + if (tcctx.cb.decl_map) + for (c = gimple_omp_task_clauses (task_stmt); c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + tree ind, ptr, df; + + decl = OMP_CLAUSE_DECL (c); + if (!is_variable_sized (decl)) + continue; + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) decl); + if (n == NULL) + continue; + f = (tree) n->value; + f = *tcctx.cb.decl_map->get (f); + gcc_assert (DECL_HAS_VALUE_EXPR_P (decl)); + ind = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (ind) == INDIRECT_REF); + gcc_assert (DECL_P (TREE_OPERAND (ind, 0))); + n = splay_tree_lookup (ctx->sfield_map, + (splay_tree_key) TREE_OPERAND (ind, 0)); + sf = (tree) n->value; + sf = *tcctx.cb.decl_map->get (sf); + src = build_simple_mem_ref_loc (loc, sarg); + src = omp_build_component_ref (src, sf); + src = build_simple_mem_ref_loc (loc, src); + dst = build_simple_mem_ref_loc (loc, arg); + dst = omp_build_component_ref (dst, f); + t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src); + append_to_statement_list (t, &list); + n = splay_tree_lookup (ctx->field_map, + (splay_tree_key) TREE_OPERAND (ind, 0)); + df = (tree) n->value; + df = *tcctx.cb.decl_map->get (df); + ptr = build_simple_mem_ref_loc (loc, arg); + ptr = omp_build_component_ref (ptr, df); + t = build2 (MODIFY_EXPR, TREE_TYPE (ptr), ptr, + build_fold_addr_expr_loc (loc, dst)); + append_to_statement_list (t, &list); + } + + t = build1 (RETURN_EXPR, void_type_node, NULL); + append_to_statement_list (t, &list); + + if (tcctx.cb.decl_map) + delete tcctx.cb.decl_map; + pop_gimplify_context (NULL); + BIND_EXPR_BODY (bind) = list; + pop_cfun (); +} + +static void +lower_depend_clauses (tree *pclauses, gimple_seq *iseq, gimple_seq *oseq) +{ + tree c, clauses; + gimple *g; + size_t cnt[4] = { 0, 0, 0, 0 }, idx = 2, i; + + clauses = omp_find_clause (*pclauses, OMP_CLAUSE_DEPEND); + gcc_assert (clauses); + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND) + switch (OMP_CLAUSE_DEPEND_KIND (c)) + { + case OMP_CLAUSE_DEPEND_LAST: + /* Lowering already done at gimplification. */ + return; + case OMP_CLAUSE_DEPEND_IN: + cnt[2]++; + break; + case OMP_CLAUSE_DEPEND_OUT: + case OMP_CLAUSE_DEPEND_INOUT: + cnt[0]++; + break; + case OMP_CLAUSE_DEPEND_MUTEXINOUTSET: + cnt[1]++; + break; + case OMP_CLAUSE_DEPEND_DEPOBJ: + cnt[3]++; + break; + case OMP_CLAUSE_DEPEND_SOURCE: + case OMP_CLAUSE_DEPEND_SINK: + /* FALLTHRU */ + default: + gcc_unreachable (); + } + if (cnt[1] || cnt[3]) + idx = 5; + size_t total = cnt[0] + cnt[1] + cnt[2] + cnt[3]; + tree type = build_array_type_nelts (ptr_type_node, total + idx); + tree array = create_tmp_var (type); + TREE_ADDRESSABLE (array) = 1; + tree r = build4 (ARRAY_REF, ptr_type_node, array, size_int (0), NULL_TREE, + NULL_TREE); + if (idx == 5) + { + g = gimple_build_assign (r, build_int_cst (ptr_type_node, 0)); + gimple_seq_add_stmt (iseq, g); + r = build4 (ARRAY_REF, ptr_type_node, array, size_int (1), NULL_TREE, + NULL_TREE); + } + g = gimple_build_assign (r, build_int_cst (ptr_type_node, total)); + gimple_seq_add_stmt (iseq, g); + for (i = 0; i < (idx == 5 ? 3 : 1); i++) + { + r = build4 (ARRAY_REF, ptr_type_node, array, + size_int (i + 1 + (idx == 5)), NULL_TREE, NULL_TREE); + g = gimple_build_assign (r, build_int_cst (ptr_type_node, cnt[i])); + gimple_seq_add_stmt (iseq, g); + } + for (i = 0; i < 4; i++) + { + if (cnt[i] == 0) + continue; + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND) + continue; + else + { + switch (OMP_CLAUSE_DEPEND_KIND (c)) + { + case OMP_CLAUSE_DEPEND_IN: + if (i != 2) + continue; + break; + case OMP_CLAUSE_DEPEND_OUT: + case OMP_CLAUSE_DEPEND_INOUT: + if (i != 0) + continue; + break; + case OMP_CLAUSE_DEPEND_MUTEXINOUTSET: + if (i != 1) + continue; + break; + case OMP_CLAUSE_DEPEND_DEPOBJ: + if (i != 3) + continue; + break; + default: + gcc_unreachable (); + } + tree t = OMP_CLAUSE_DECL (c); + t = fold_convert (ptr_type_node, t); + gimplify_expr (&t, iseq, NULL, is_gimple_val, fb_rvalue); + r = build4 (ARRAY_REF, ptr_type_node, array, size_int (idx++), + NULL_TREE, NULL_TREE); + g = gimple_build_assign (r, t); + gimple_seq_add_stmt (iseq, g); + } + } + c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE_DEPEND); + OMP_CLAUSE_DEPEND_KIND (c) = OMP_CLAUSE_DEPEND_LAST; + OMP_CLAUSE_DECL (c) = build_fold_addr_expr (array); + OMP_CLAUSE_CHAIN (c) = *pclauses; + *pclauses = c; + tree clobber = build_clobber (type); + g = gimple_build_assign (array, clobber); + gimple_seq_add_stmt (oseq, g); +} + +/* Lower the OpenMP parallel or task directive in the current statement + in GSI_P. CTX holds context information for the directive. */ + +static void +lower_omp_taskreg (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree clauses; + tree child_fn, t; + gimple *stmt = gsi_stmt (*gsi_p); + gbind *par_bind, *bind, *dep_bind = NULL; + gimple_seq par_body; + location_t loc = gimple_location (stmt); + + clauses = gimple_omp_taskreg_clauses (stmt); + if (gimple_code (stmt) == GIMPLE_OMP_TASK + && gimple_omp_task_taskwait_p (stmt)) + { + par_bind = NULL; + par_body = NULL; + } + else + { + par_bind + = as_a <gbind *> (gimple_seq_first_stmt (gimple_omp_body (stmt))); + par_body = gimple_bind_body (par_bind); + } + child_fn = ctx->cb.dst_fn; + if (gimple_code (stmt) == GIMPLE_OMP_PARALLEL + && !gimple_omp_parallel_combined_p (stmt)) + { + struct walk_stmt_info wi; + int ws_num = 0; + + memset (&wi, 0, sizeof (wi)); + wi.info = &ws_num; + wi.val_only = true; + walk_gimple_seq (par_body, check_combined_parallel, NULL, &wi); + if (ws_num == 1) + gimple_omp_parallel_set_combined_p (stmt, true); + } + gimple_seq dep_ilist = NULL; + gimple_seq dep_olist = NULL; + if (gimple_code (stmt) == GIMPLE_OMP_TASK + && omp_find_clause (clauses, OMP_CLAUSE_DEPEND)) + { + push_gimplify_context (); + dep_bind = gimple_build_bind (NULL, NULL, make_node (BLOCK)); + lower_depend_clauses (gimple_omp_task_clauses_ptr (stmt), + &dep_ilist, &dep_olist); + } + + if (gimple_code (stmt) == GIMPLE_OMP_TASK + && gimple_omp_task_taskwait_p (stmt)) + { + if (dep_bind) + { + gsi_replace (gsi_p, dep_bind, true); + gimple_bind_add_seq (dep_bind, dep_ilist); + gimple_bind_add_stmt (dep_bind, stmt); + gimple_bind_add_seq (dep_bind, dep_olist); + pop_gimplify_context (dep_bind); + } + return; + } + + if (ctx->srecord_type) + create_task_copyfn (as_a <gomp_task *> (stmt), ctx); + + gimple_seq tskred_ilist = NULL; + gimple_seq tskred_olist = NULL; + if ((is_task_ctx (ctx) + && gimple_omp_task_taskloop_p (ctx->stmt) + && omp_find_clause (gimple_omp_task_clauses (ctx->stmt), + OMP_CLAUSE_REDUCTION)) + || (is_parallel_ctx (ctx) + && omp_find_clause (gimple_omp_parallel_clauses (stmt), + OMP_CLAUSE__REDUCTEMP_))) + { + if (dep_bind == NULL) + { + push_gimplify_context (); + dep_bind = gimple_build_bind (NULL, NULL, make_node (BLOCK)); + } + lower_omp_task_reductions (ctx, is_task_ctx (ctx) ? OMP_TASKLOOP + : OMP_PARALLEL, + gimple_omp_taskreg_clauses (ctx->stmt), + &tskred_ilist, &tskred_olist); + } + + push_gimplify_context (); + + gimple_seq par_olist = NULL; + gimple_seq par_ilist = NULL; + gimple_seq par_rlist = NULL; + lower_rec_input_clauses (clauses, &par_ilist, &par_olist, ctx, NULL); + lower_omp (&par_body, ctx); + if (gimple_code (stmt) != GIMPLE_OMP_TASK) + lower_reduction_clauses (clauses, &par_rlist, NULL, ctx); + + /* Declare all the variables created by mapping and the variables + declared in the scope of the parallel body. */ + record_vars_into (ctx->block_vars, child_fn); + maybe_remove_omp_member_access_dummy_vars (par_bind); + record_vars_into (gimple_bind_vars (par_bind), child_fn); + + if (ctx->record_type) + { + ctx->sender_decl + = create_tmp_var (ctx->srecord_type ? ctx->srecord_type + : ctx->record_type, ".omp_data_o"); + DECL_NAMELESS (ctx->sender_decl) = 1; + TREE_ADDRESSABLE (ctx->sender_decl) = 1; + gimple_omp_taskreg_set_data_arg (stmt, ctx->sender_decl); + } + + gimple_seq olist = NULL; + gimple_seq ilist = NULL; + lower_send_clauses (clauses, &ilist, &olist, ctx); + lower_send_shared_vars (&ilist, &olist, ctx); + + if (ctx->record_type) + { + tree clobber = build_clobber (TREE_TYPE (ctx->sender_decl)); + gimple_seq_add_stmt (&olist, gimple_build_assign (ctx->sender_decl, + clobber)); + } + + /* Once all the expansions are done, sequence all the different + fragments inside gimple_omp_body. */ + + gimple_seq new_body = NULL; + + if (ctx->record_type) + { + t = build_fold_addr_expr_loc (loc, ctx->sender_decl); + /* fixup_child_record_type might have changed receiver_decl's type. */ + t = fold_convert_loc (loc, TREE_TYPE (ctx->receiver_decl), t); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (ctx->receiver_decl, t)); + } + + gimple_seq_add_seq (&new_body, par_ilist); + gimple_seq_add_seq (&new_body, par_body); + gimple_seq_add_seq (&new_body, par_rlist); + if (ctx->cancellable) + gimple_seq_add_stmt (&new_body, gimple_build_label (ctx->cancel_label)); + gimple_seq_add_seq (&new_body, par_olist); + new_body = maybe_catch_exception (new_body); + if (gimple_code (stmt) == GIMPLE_OMP_TASK) + gimple_seq_add_stmt (&new_body, + gimple_build_omp_continue (integer_zero_node, + integer_zero_node)); + gimple_seq_add_stmt (&new_body, gimple_build_omp_return (false)); + gimple_omp_set_body (stmt, new_body); + + if (dep_bind && gimple_bind_block (par_bind) == NULL_TREE) + bind = gimple_build_bind (NULL, NULL, make_node (BLOCK)); + else + bind = gimple_build_bind (NULL, NULL, gimple_bind_block (par_bind)); + gsi_replace (gsi_p, dep_bind ? dep_bind : bind, true); + gimple_bind_add_seq (bind, ilist); + gimple_bind_add_stmt (bind, stmt); + gimple_bind_add_seq (bind, olist); + + pop_gimplify_context (NULL); + + if (dep_bind) + { + gimple_bind_add_seq (dep_bind, dep_ilist); + gimple_bind_add_seq (dep_bind, tskred_ilist); + gimple_bind_add_stmt (dep_bind, bind); + gimple_bind_add_seq (dep_bind, tskred_olist); + gimple_bind_add_seq (dep_bind, dep_olist); + pop_gimplify_context (dep_bind); + } +} + +/* Lower the GIMPLE_OMP_TARGET in the current statement + in GSI_P. CTX holds context information for the directive. */ + +static void +lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + tree clauses; + tree child_fn, t, c; + gomp_target *stmt = as_a <gomp_target *> (gsi_stmt (*gsi_p)); + gbind *tgt_bind, *bind, *dep_bind = NULL; + gimple_seq tgt_body, olist, ilist, fplist, new_body; + location_t loc = gimple_location (stmt); + bool offloaded, data_region; + unsigned int map_cnt = 0; + tree in_reduction_clauses = NULL_TREE; + + offloaded = is_gimple_omp_offloaded (stmt); + switch (gimple_omp_target_kind (stmt)) + { + case GF_OMP_TARGET_KIND_REGION: + tree *p, *q; + q = &in_reduction_clauses; + for (p = gimple_omp_target_clauses_ptr (stmt); *p; ) + if (OMP_CLAUSE_CODE (*p) == OMP_CLAUSE_IN_REDUCTION) + { + *q = *p; + q = &OMP_CLAUSE_CHAIN (*q); + *p = OMP_CLAUSE_CHAIN (*p); + } + else + p = &OMP_CLAUSE_CHAIN (*p); + *q = NULL_TREE; + *p = in_reduction_clauses; + /* FALLTHRU */ + case GF_OMP_TARGET_KIND_UPDATE: + case GF_OMP_TARGET_KIND_ENTER_DATA: + case GF_OMP_TARGET_KIND_EXIT_DATA: + case GF_OMP_TARGET_KIND_OACC_PARALLEL: + case GF_OMP_TARGET_KIND_OACC_KERNELS: + case GF_OMP_TARGET_KIND_OACC_SERIAL: + case GF_OMP_TARGET_KIND_OACC_UPDATE: + case GF_OMP_TARGET_KIND_OACC_ENTER_DATA: + case GF_OMP_TARGET_KIND_OACC_EXIT_DATA: + case GF_OMP_TARGET_KIND_OACC_DECLARE: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_PARALLELIZED: + case GF_OMP_TARGET_KIND_OACC_PARALLEL_KERNELS_GANG_SINGLE: + data_region = false; + break; + case GF_OMP_TARGET_KIND_DATA: + case GF_OMP_TARGET_KIND_OACC_DATA: + case GF_OMP_TARGET_KIND_OACC_HOST_DATA: + case GF_OMP_TARGET_KIND_OACC_DATA_KERNELS: + data_region = true; + break; + default: + gcc_unreachable (); + } + + clauses = gimple_omp_target_clauses (stmt); + + gimple_seq dep_ilist = NULL; + gimple_seq dep_olist = NULL; + bool has_depend = omp_find_clause (clauses, OMP_CLAUSE_DEPEND) != NULL_TREE; + if (has_depend || in_reduction_clauses) + { + push_gimplify_context (); + dep_bind = gimple_build_bind (NULL, NULL, make_node (BLOCK)); + if (has_depend) + lower_depend_clauses (gimple_omp_target_clauses_ptr (stmt), + &dep_ilist, &dep_olist); + if (in_reduction_clauses) + lower_rec_input_clauses (in_reduction_clauses, &dep_ilist, &dep_olist, + ctx, NULL); + } + + tgt_bind = NULL; + tgt_body = NULL; + if (offloaded) + { + tgt_bind = gimple_seq_first_stmt_as_a_bind (gimple_omp_body (stmt)); + tgt_body = gimple_bind_body (tgt_bind); + } + else if (data_region) + tgt_body = gimple_omp_body (stmt); + child_fn = ctx->cb.dst_fn; + + push_gimplify_context (); + fplist = NULL; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + tree var, x; + + default: + break; + case OMP_CLAUSE_MAP: +#if CHECKING_P + /* First check what we're prepared to handle in the following. */ + switch (OMP_CLAUSE_MAP_KIND (c)) + { + case GOMP_MAP_ALLOC: + case GOMP_MAP_TO: + case GOMP_MAP_FROM: + case GOMP_MAP_TOFROM: + case GOMP_MAP_POINTER: + case GOMP_MAP_TO_PSET: + case GOMP_MAP_DELETE: + case GOMP_MAP_RELEASE: + case GOMP_MAP_ALWAYS_TO: + case GOMP_MAP_ALWAYS_FROM: + case GOMP_MAP_ALWAYS_TOFROM: + case GOMP_MAP_FIRSTPRIVATE_POINTER: + case GOMP_MAP_FIRSTPRIVATE_REFERENCE: + case GOMP_MAP_STRUCT: + case GOMP_MAP_ALWAYS_POINTER: + case GOMP_MAP_ATTACH: + case GOMP_MAP_DETACH: + case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION: + case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION: + break; + case GOMP_MAP_IF_PRESENT: + case GOMP_MAP_FORCE_ALLOC: + case GOMP_MAP_FORCE_TO: + case GOMP_MAP_FORCE_FROM: + case GOMP_MAP_FORCE_TOFROM: + case GOMP_MAP_FORCE_PRESENT: + case GOMP_MAP_FORCE_DEVICEPTR: + case GOMP_MAP_DEVICE_RESIDENT: + case GOMP_MAP_LINK: + case GOMP_MAP_FORCE_DETACH: + gcc_assert (is_gimple_omp_oacc (stmt)); + break; + default: + gcc_unreachable (); + } +#endif + /* FALLTHRU */ + case OMP_CLAUSE_TO: + case OMP_CLAUSE_FROM: + oacc_firstprivate: + var = OMP_CLAUSE_DECL (c); + if (!DECL_P (var)) + { + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP + || (!OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c) + && (OMP_CLAUSE_MAP_KIND (c) + != GOMP_MAP_FIRSTPRIVATE_POINTER))) + map_cnt++; + continue; + } + + if (DECL_SIZE (var) + && TREE_CODE (DECL_SIZE (var)) != INTEGER_CST) + { + tree var2 = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (var2) == INDIRECT_REF); + var2 = TREE_OPERAND (var2, 0); + gcc_assert (DECL_P (var2)); + var = var2; + } + + if (offloaded + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_REFERENCE)) + { + if (TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE) + { + if (is_global_var (maybe_lookup_decl_in_outer_ctx (var, ctx)) + && varpool_node::get_create (var)->offloadable) + continue; + + tree type = build_pointer_type (TREE_TYPE (var)); + tree new_var = lookup_decl (var, ctx); + x = create_tmp_var_raw (type, get_name (new_var)); + gimple_add_tmp_var (x); + x = build_simple_mem_ref (x); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + continue; + } + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH) + && is_omp_target (stmt)) + { + gcc_assert (maybe_lookup_field (c, ctx)); + map_cnt++; + continue; + } + + if (!maybe_lookup_field (var, ctx)) + continue; + + /* Don't remap compute constructs' reduction variables, because the + intermediate result must be local to each gang. */ + if (offloaded && !(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && is_gimple_omp_oacc (ctx->stmt) + && OMP_CLAUSE_MAP_IN_REDUCTION (c))) + { + x = build_receiver_ref (var, true, ctx); + tree new_var = lookup_decl (var, ctx); + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER + && !OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c) + && TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE) + x = build_simple_mem_ref (x); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + gcc_assert (is_gimple_omp_oacc (ctx->stmt)); + if (omp_privatize_by_reference (new_var) + && (TREE_CODE (TREE_TYPE (new_var)) != POINTER_TYPE + || DECL_BY_REFERENCE (var))) + { + /* Create a local object to hold the instance + value. */ + tree type = TREE_TYPE (TREE_TYPE (new_var)); + const char *id = IDENTIFIER_POINTER (DECL_NAME (new_var)); + tree inst = create_tmp_var (type, id); + gimplify_assign (inst, fold_indirect_ref (x), &fplist); + x = build_fold_addr_expr (inst); + } + gimplify_assign (new_var, x, &fplist); + } + else if (DECL_P (new_var)) + { + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + else + gcc_unreachable (); + } + map_cnt++; + break; + + case OMP_CLAUSE_FIRSTPRIVATE: + gcc_checking_assert (offloaded); + if (is_gimple_omp_oacc (ctx->stmt)) + { + /* No 'firstprivate' clauses on OpenACC 'kernels'. */ + gcc_checking_assert (!is_oacc_kernels (ctx)); + /* Likewise, on OpenACC 'kernels' decomposed parts. */ + gcc_checking_assert (!is_oacc_kernels_decomposed_part (ctx)); + + goto oacc_firstprivate; + } + map_cnt++; + var = OMP_CLAUSE_DECL (c); + if (!omp_privatize_by_reference (var) + && !is_gimple_reg_type (TREE_TYPE (var))) + { + tree new_var = lookup_decl (var, ctx); + if (is_variable_sized (var)) + { + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + tree new_pvar = lookup_decl (pvar, ctx); + x = build_fold_indirect_ref (new_pvar); + TREE_THIS_NOTRAP (x) = 1; + } + else + x = build_receiver_ref (var, true, ctx); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + break; + + case OMP_CLAUSE_PRIVATE: + gcc_checking_assert (offloaded); + if (is_gimple_omp_oacc (ctx->stmt)) + { + /* No 'private' clauses on OpenACC 'kernels'. */ + gcc_checking_assert (!is_oacc_kernels (ctx)); + /* Likewise, on OpenACC 'kernels' decomposed parts. */ + gcc_checking_assert (!is_oacc_kernels_decomposed_part (ctx)); + + break; + } + var = OMP_CLAUSE_DECL (c); + if (is_variable_sized (var)) + { + tree new_var = lookup_decl (var, ctx); + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + tree new_pvar = lookup_decl (pvar, ctx); + x = build_fold_indirect_ref (new_pvar); + TREE_THIS_NOTRAP (x) = 1; + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + break; + + case OMP_CLAUSE_USE_DEVICE_PTR: + case OMP_CLAUSE_USE_DEVICE_ADDR: + case OMP_CLAUSE_IS_DEVICE_PTR: + var = OMP_CLAUSE_DECL (c); + map_cnt++; + if (is_variable_sized (var)) + { + tree new_var = lookup_decl (var, ctx); + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + tree new_pvar = lookup_decl (pvar, ctx); + x = build_fold_indirect_ref (new_pvar); + TREE_THIS_NOTRAP (x) = 1; + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR + && !omp_privatize_by_reference (var) + && !omp_is_allocatable_or_ptr (var) + && !lang_hooks.decls.omp_array_data (var, true)) + || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE) + { + tree new_var = lookup_decl (var, ctx); + tree type = build_pointer_type (TREE_TYPE (var)); + x = create_tmp_var_raw (type, get_name (new_var)); + gimple_add_tmp_var (x); + x = build_simple_mem_ref (x); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + else + { + tree new_var = lookup_decl (var, ctx); + x = create_tmp_var_raw (TREE_TYPE (new_var), get_name (new_var)); + gimple_add_tmp_var (x); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } + break; + } + + if (offloaded) + { + target_nesting_level++; + lower_omp (&tgt_body, ctx); + target_nesting_level--; + } + else if (data_region) + lower_omp (&tgt_body, ctx); + + if (offloaded) + { + /* Declare all the variables created by mapping and the variables + declared in the scope of the target body. */ + record_vars_into (ctx->block_vars, child_fn); + maybe_remove_omp_member_access_dummy_vars (tgt_bind); + record_vars_into (gimple_bind_vars (tgt_bind), child_fn); + } + + olist = NULL; + ilist = NULL; + if (ctx->record_type) + { + ctx->sender_decl + = create_tmp_var (ctx->record_type, ".omp_data_arr"); + DECL_NAMELESS (ctx->sender_decl) = 1; + TREE_ADDRESSABLE (ctx->sender_decl) = 1; + t = make_tree_vec (3); + TREE_VEC_ELT (t, 0) = ctx->sender_decl; + TREE_VEC_ELT (t, 1) + = create_tmp_var (build_array_type_nelts (size_type_node, map_cnt), + ".omp_data_sizes"); + DECL_NAMELESS (TREE_VEC_ELT (t, 1)) = 1; + TREE_ADDRESSABLE (TREE_VEC_ELT (t, 1)) = 1; + TREE_STATIC (TREE_VEC_ELT (t, 1)) = 1; + tree tkind_type = short_unsigned_type_node; + int talign_shift = 8; + TREE_VEC_ELT (t, 2) + = create_tmp_var (build_array_type_nelts (tkind_type, map_cnt), + ".omp_data_kinds"); + DECL_NAMELESS (TREE_VEC_ELT (t, 2)) = 1; + TREE_ADDRESSABLE (TREE_VEC_ELT (t, 2)) = 1; + TREE_STATIC (TREE_VEC_ELT (t, 2)) = 1; + gimple_omp_target_set_data_arg (stmt, t); + + vec<constructor_elt, va_gc> *vsize; + vec<constructor_elt, va_gc> *vkind; + vec_alloc (vsize, map_cnt); + vec_alloc (vkind, map_cnt); + unsigned int map_idx = 0; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + tree ovar, nc, s, purpose, var, x, type; + unsigned int talign; + + default: + break; + + case OMP_CLAUSE_MAP: + case OMP_CLAUSE_TO: + case OMP_CLAUSE_FROM: + oacc_firstprivate_map: + nc = c; + ovar = OMP_CLAUSE_DECL (c); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER + || (OMP_CLAUSE_MAP_KIND (c) + == GOMP_MAP_FIRSTPRIVATE_REFERENCE))) + break; + if (!DECL_P (ovar)) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c)) + { + nc = OMP_CLAUSE_CHAIN (c); + gcc_checking_assert (OMP_CLAUSE_DECL (nc) + == get_base_address (ovar)); + ovar = OMP_CLAUSE_DECL (nc); + } + else + { + tree x = build_sender_ref (ovar, ctx); + tree v = ovar; + if (in_reduction_clauses + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_IN_REDUCTION (c)) + { + v = unshare_expr (v); + tree *p = &v; + while (handled_component_p (*p) + || TREE_CODE (*p) == INDIRECT_REF + || TREE_CODE (*p) == ADDR_EXPR + || TREE_CODE (*p) == MEM_REF + || TREE_CODE (*p) == NON_LVALUE_EXPR) + p = &TREE_OPERAND (*p, 0); + tree d = *p; + if (is_variable_sized (d)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (d)); + d = DECL_VALUE_EXPR (d); + gcc_assert (TREE_CODE (d) == INDIRECT_REF); + d = TREE_OPERAND (d, 0); + gcc_assert (DECL_P (d)); + } + splay_tree_key key + = (splay_tree_key) &DECL_CONTEXT (d); + tree nd = (tree) splay_tree_lookup (ctx->field_map, + key)->value; + if (d == *p) + *p = nd; + else + *p = build_fold_indirect_ref (nd); + } + v = build_fold_addr_expr_with_type (v, ptr_type_node); + gimplify_assign (x, v, &ilist); + nc = NULL_TREE; + } + } + else + { + if (DECL_SIZE (ovar) + && TREE_CODE (DECL_SIZE (ovar)) != INTEGER_CST) + { + tree ovar2 = DECL_VALUE_EXPR (ovar); + gcc_assert (TREE_CODE (ovar2) == INDIRECT_REF); + ovar2 = TREE_OPERAND (ovar2, 0); + gcc_assert (DECL_P (ovar2)); + ovar = ovar2; + } + if (!maybe_lookup_field (ovar, ctx) + && !(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))) + continue; + } + + talign = TYPE_ALIGN_UNIT (TREE_TYPE (ovar)); + if (DECL_P (ovar) && DECL_ALIGN_UNIT (ovar) > talign) + talign = DECL_ALIGN_UNIT (ovar); + + var = NULL_TREE; + if (nc) + { + if (in_reduction_clauses + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_IN_REDUCTION (c)) + { + tree d = ovar; + if (is_variable_sized (d)) + { + gcc_assert (DECL_HAS_VALUE_EXPR_P (d)); + d = DECL_VALUE_EXPR (d); + gcc_assert (TREE_CODE (d) == INDIRECT_REF); + d = TREE_OPERAND (d, 0); + gcc_assert (DECL_P (d)); + } + splay_tree_key key + = (splay_tree_key) &DECL_CONTEXT (d); + tree nd = (tree) splay_tree_lookup (ctx->field_map, + key)->value; + if (d == ovar) + var = nd; + else + var = build_fold_indirect_ref (nd); + } + else + var = lookup_decl_in_outer_ctx (ovar, ctx); + } + if (nc + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH) + && is_omp_target (stmt)) + { + x = build_sender_ref (c, ctx); + gimplify_assign (x, build_fold_addr_expr (var), &ilist); + } + else if (nc) + { + x = build_sender_ref (ovar, ctx); + + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP + && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER + && !OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c) + && TREE_CODE (TREE_TYPE (ovar)) == ARRAY_TYPE) + { + gcc_assert (offloaded); + tree avar + = create_tmp_var (TREE_TYPE (TREE_TYPE (x))); + mark_addressable (avar); + gimplify_assign (avar, build_fold_addr_expr (var), &ilist); + talign = DECL_ALIGN_UNIT (avar); + avar = build_fold_addr_expr (avar); + gimplify_assign (x, avar, &ilist); + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + gcc_assert (is_gimple_omp_oacc (ctx->stmt)); + if (!omp_privatize_by_reference (var)) + { + if (is_gimple_reg (var) + && OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)) + suppress_warning (var); + var = build_fold_addr_expr (var); + } + else + talign = TYPE_ALIGN_UNIT (TREE_TYPE (TREE_TYPE (ovar))); + gimplify_assign (x, var, &ilist); + } + else if (is_gimple_reg (var)) + { + gcc_assert (offloaded); + tree avar = create_tmp_var (TREE_TYPE (var)); + mark_addressable (avar); + enum gomp_map_kind map_kind = OMP_CLAUSE_MAP_KIND (c); + if (GOMP_MAP_COPY_TO_P (map_kind) + || map_kind == GOMP_MAP_POINTER + || map_kind == GOMP_MAP_TO_PSET + || map_kind == GOMP_MAP_FORCE_DEVICEPTR) + { + /* If we need to initialize a temporary + with VAR because it is not addressable, and + the variable hasn't been initialized yet, then + we'll get a warning for the store to avar. + Don't warn in that case, the mapping might + be implicit. */ + suppress_warning (var, OPT_Wuninitialized); + gimplify_assign (avar, var, &ilist); + } + avar = build_fold_addr_expr (avar); + gimplify_assign (x, avar, &ilist); + if ((GOMP_MAP_COPY_FROM_P (map_kind) + || map_kind == GOMP_MAP_FORCE_DEVICEPTR) + && !TYPE_READONLY (TREE_TYPE (var))) + { + x = unshare_expr (x); + x = build_simple_mem_ref (x); + gimplify_assign (var, x, &olist); + } + } + else + { + /* While MAP is handled explicitly by the FE, + for 'target update', only the identified is passed. */ + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO) + && (omp_is_allocatable_or_ptr (var) + && omp_check_optional_argument (var, false))) + var = build_fold_indirect_ref (var); + else if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_FROM + && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_TO) + || (!omp_is_allocatable_or_ptr (var) + && !omp_check_optional_argument (var, false))) + var = build_fold_addr_expr (var); + gimplify_assign (x, var, &ilist); + } + } + s = NULL_TREE; + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE) + { + gcc_checking_assert (is_gimple_omp_oacc (ctx->stmt)); + s = TREE_TYPE (ovar); + if (TREE_CODE (s) == REFERENCE_TYPE + || omp_check_optional_argument (ovar, false)) + s = TREE_TYPE (s); + s = TYPE_SIZE_UNIT (s); + } + else + s = OMP_CLAUSE_SIZE (c); + if (s == NULL_TREE) + s = TYPE_SIZE_UNIT (TREE_TYPE (ovar)); + s = fold_convert (size_type_node, s); + purpose = size_int (map_idx++); + CONSTRUCTOR_APPEND_ELT (vsize, purpose, s); + if (TREE_CODE (s) != INTEGER_CST) + TREE_STATIC (TREE_VEC_ELT (t, 1)) = 0; + + unsigned HOST_WIDE_INT tkind, tkind_zero; + switch (OMP_CLAUSE_CODE (c)) + { + case OMP_CLAUSE_MAP: + tkind = OMP_CLAUSE_MAP_KIND (c); + tkind_zero = tkind; + if (OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c)) + switch (tkind) + { + case GOMP_MAP_ALLOC: + case GOMP_MAP_IF_PRESENT: + case GOMP_MAP_TO: + case GOMP_MAP_FROM: + case GOMP_MAP_TOFROM: + case GOMP_MAP_ALWAYS_TO: + case GOMP_MAP_ALWAYS_FROM: + case GOMP_MAP_ALWAYS_TOFROM: + case GOMP_MAP_RELEASE: + case GOMP_MAP_FORCE_TO: + case GOMP_MAP_FORCE_FROM: + case GOMP_MAP_FORCE_TOFROM: + case GOMP_MAP_FORCE_PRESENT: + tkind_zero = GOMP_MAP_ZERO_LEN_ARRAY_SECTION; + break; + case GOMP_MAP_DELETE: + tkind_zero = GOMP_MAP_DELETE_ZERO_LEN_ARRAY_SECTION; + default: + break; + } + if (tkind_zero != tkind) + { + if (integer_zerop (s)) + tkind = tkind_zero; + else if (integer_nonzerop (s)) + tkind_zero = tkind; + } + if (tkind_zero == tkind + && OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (c) + && (((tkind & GOMP_MAP_FLAG_SPECIAL_BITS) + & ~GOMP_MAP_IMPLICIT) + == 0)) + { + /* If this is an implicit map, and the GOMP_MAP_IMPLICIT + bits are not interfered by other special bit encodings, + then turn the GOMP_IMPLICIT_BIT flag on for the runtime + to see. */ + tkind |= GOMP_MAP_IMPLICIT; + tkind_zero = tkind; + } + break; + case OMP_CLAUSE_FIRSTPRIVATE: + gcc_checking_assert (is_gimple_omp_oacc (ctx->stmt)); + tkind = GOMP_MAP_TO; + tkind_zero = tkind; + break; + case OMP_CLAUSE_TO: + tkind = GOMP_MAP_TO; + tkind_zero = tkind; + break; + case OMP_CLAUSE_FROM: + tkind = GOMP_MAP_FROM; + tkind_zero = tkind; + break; + default: + gcc_unreachable (); + } + gcc_checking_assert (tkind + < (HOST_WIDE_INT_C (1U) << talign_shift)); + gcc_checking_assert (tkind_zero + < (HOST_WIDE_INT_C (1U) << talign_shift)); + talign = ceil_log2 (talign); + tkind |= talign << talign_shift; + tkind_zero |= talign << talign_shift; + gcc_checking_assert (tkind + <= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type))); + gcc_checking_assert (tkind_zero + <= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type))); + if (tkind == tkind_zero) + x = build_int_cstu (tkind_type, tkind); + else + { + TREE_STATIC (TREE_VEC_ELT (t, 2)) = 0; + x = build3 (COND_EXPR, tkind_type, + fold_build2 (EQ_EXPR, boolean_type_node, + unshare_expr (s), size_zero_node), + build_int_cstu (tkind_type, tkind_zero), + build_int_cstu (tkind_type, tkind)); + } + CONSTRUCTOR_APPEND_ELT (vkind, purpose, x); + if (nc && nc != c) + c = nc; + break; + + case OMP_CLAUSE_FIRSTPRIVATE: + if (is_gimple_omp_oacc (ctx->stmt)) + goto oacc_firstprivate_map; + ovar = OMP_CLAUSE_DECL (c); + if (omp_privatize_by_reference (ovar)) + talign = TYPE_ALIGN_UNIT (TREE_TYPE (TREE_TYPE (ovar))); + else + talign = DECL_ALIGN_UNIT (ovar); + var = lookup_decl_in_outer_ctx (ovar, ctx); + x = build_sender_ref (ovar, ctx); + tkind = GOMP_MAP_FIRSTPRIVATE; + type = TREE_TYPE (ovar); + if (omp_privatize_by_reference (ovar)) + type = TREE_TYPE (type); + if ((INTEGRAL_TYPE_P (type) + && TYPE_PRECISION (type) <= POINTER_SIZE) + || TREE_CODE (type) == POINTER_TYPE) + { + tkind = GOMP_MAP_FIRSTPRIVATE_INT; + tree t = var; + if (omp_privatize_by_reference (var)) + t = build_simple_mem_ref (var); + else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)) + suppress_warning (var); + if (TREE_CODE (type) != POINTER_TYPE) + t = fold_convert (pointer_sized_int_node, t); + t = fold_convert (TREE_TYPE (x), t); + gimplify_assign (x, t, &ilist); + } + else if (omp_privatize_by_reference (var)) + gimplify_assign (x, var, &ilist); + else if (is_gimple_reg (var)) + { + tree avar = create_tmp_var (TREE_TYPE (var)); + mark_addressable (avar); + if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)) + suppress_warning (var); + gimplify_assign (avar, var, &ilist); + avar = build_fold_addr_expr (avar); + gimplify_assign (x, avar, &ilist); + } + else + { + var = build_fold_addr_expr (var); + gimplify_assign (x, var, &ilist); + } + if (tkind == GOMP_MAP_FIRSTPRIVATE_INT) + s = size_int (0); + else if (omp_privatize_by_reference (ovar)) + s = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ovar))); + else + s = TYPE_SIZE_UNIT (TREE_TYPE (ovar)); + s = fold_convert (size_type_node, s); + purpose = size_int (map_idx++); + CONSTRUCTOR_APPEND_ELT (vsize, purpose, s); + if (TREE_CODE (s) != INTEGER_CST) + TREE_STATIC (TREE_VEC_ELT (t, 1)) = 0; + + gcc_checking_assert (tkind + < (HOST_WIDE_INT_C (1U) << talign_shift)); + talign = ceil_log2 (talign); + tkind |= talign << talign_shift; + gcc_checking_assert (tkind + <= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type))); + CONSTRUCTOR_APPEND_ELT (vkind, purpose, + build_int_cstu (tkind_type, tkind)); + break; + + case OMP_CLAUSE_USE_DEVICE_PTR: + case OMP_CLAUSE_USE_DEVICE_ADDR: + case OMP_CLAUSE_IS_DEVICE_PTR: + ovar = OMP_CLAUSE_DECL (c); + var = lookup_decl_in_outer_ctx (ovar, ctx); + + if (lang_hooks.decls.omp_array_data (ovar, true)) + { + tkind = (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR + ? GOMP_MAP_USE_DEVICE_PTR : GOMP_MAP_FIRSTPRIVATE_INT); + x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx); + } + else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR) + { + tkind = GOMP_MAP_USE_DEVICE_PTR; + x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx); + } + else + { + tkind = GOMP_MAP_FIRSTPRIVATE_INT; + x = build_sender_ref (ovar, ctx); + } + + if (is_gimple_omp_oacc (ctx->stmt)) + { + gcc_assert (tkind == GOMP_MAP_USE_DEVICE_PTR); + + if (OMP_CLAUSE_USE_DEVICE_PTR_IF_PRESENT (c)) + tkind = GOMP_MAP_USE_DEVICE_PTR_IF_PRESENT; + } + + type = TREE_TYPE (ovar); + if (lang_hooks.decls.omp_array_data (ovar, true)) + var = lang_hooks.decls.omp_array_data (ovar, false); + else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR + && !omp_privatize_by_reference (ovar) + && !omp_is_allocatable_or_ptr (ovar)) + || TREE_CODE (type) == ARRAY_TYPE) + var = build_fold_addr_expr (var); + else + { + if (omp_privatize_by_reference (ovar) + || omp_check_optional_argument (ovar, false) + || omp_is_allocatable_or_ptr (ovar)) + { + type = TREE_TYPE (type); + if (POINTER_TYPE_P (type) + && TREE_CODE (type) != ARRAY_TYPE + && ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR + && !omp_is_allocatable_or_ptr (ovar)) + || (omp_privatize_by_reference (ovar) + && omp_is_allocatable_or_ptr (ovar)))) + var = build_simple_mem_ref (var); + var = fold_convert (TREE_TYPE (x), var); + } + } + tree present; + present = omp_check_optional_argument (ovar, true); + if (present) + { + tree null_label = create_artificial_label (UNKNOWN_LOCATION); + tree notnull_label = create_artificial_label (UNKNOWN_LOCATION); + tree opt_arg_label = create_artificial_label (UNKNOWN_LOCATION); + tree new_x = unshare_expr (x); + gimplify_expr (&present, &ilist, NULL, is_gimple_val, + fb_rvalue); + gcond *cond = gimple_build_cond_from_tree (present, + notnull_label, + null_label); + gimple_seq_add_stmt (&ilist, cond); + gimple_seq_add_stmt (&ilist, gimple_build_label (null_label)); + gimplify_assign (new_x, null_pointer_node, &ilist); + gimple_seq_add_stmt (&ilist, gimple_build_goto (opt_arg_label)); + gimple_seq_add_stmt (&ilist, + gimple_build_label (notnull_label)); + gimplify_assign (x, var, &ilist); + gimple_seq_add_stmt (&ilist, + gimple_build_label (opt_arg_label)); + } + else + gimplify_assign (x, var, &ilist); + s = size_int (0); + purpose = size_int (map_idx++); + CONSTRUCTOR_APPEND_ELT (vsize, purpose, s); + gcc_checking_assert (tkind + < (HOST_WIDE_INT_C (1U) << talign_shift)); + gcc_checking_assert (tkind + <= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type))); + CONSTRUCTOR_APPEND_ELT (vkind, purpose, + build_int_cstu (tkind_type, tkind)); + break; + } + + gcc_assert (map_idx == map_cnt); + + DECL_INITIAL (TREE_VEC_ELT (t, 1)) + = build_constructor (TREE_TYPE (TREE_VEC_ELT (t, 1)), vsize); + DECL_INITIAL (TREE_VEC_ELT (t, 2)) + = build_constructor (TREE_TYPE (TREE_VEC_ELT (t, 2)), vkind); + for (int i = 1; i <= 2; i++) + if (!TREE_STATIC (TREE_VEC_ELT (t, i))) + { + gimple_seq initlist = NULL; + force_gimple_operand (build1 (DECL_EXPR, void_type_node, + TREE_VEC_ELT (t, i)), + &initlist, true, NULL_TREE); + gimple_seq_add_seq (&ilist, initlist); + + tree clobber = build_clobber (TREE_TYPE (TREE_VEC_ELT (t, i))); + gimple_seq_add_stmt (&olist, + gimple_build_assign (TREE_VEC_ELT (t, i), + clobber)); + } + else if (omp_maybe_offloaded_ctx (ctx->outer)) + { + tree id = get_identifier ("omp declare target"); + tree decl = TREE_VEC_ELT (t, i); + DECL_ATTRIBUTES (decl) + = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl)); + varpool_node *node = varpool_node::get (decl); + if (node) + { + node->offloadable = 1; + if (ENABLE_OFFLOADING) + { + g->have_offload = true; + vec_safe_push (offload_vars, t); + } + } + } + + tree clobber = build_clobber (ctx->record_type); + gimple_seq_add_stmt (&olist, gimple_build_assign (ctx->sender_decl, + clobber)); + } + + /* Once all the expansions are done, sequence all the different + fragments inside gimple_omp_body. */ + + new_body = NULL; + + if (offloaded + && ctx->record_type) + { + t = build_fold_addr_expr_loc (loc, ctx->sender_decl); + /* fixup_child_record_type might have changed receiver_decl's type. */ + t = fold_convert_loc (loc, TREE_TYPE (ctx->receiver_decl), t); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (ctx->receiver_decl, t)); + } + gimple_seq_add_seq (&new_body, fplist); + + if (offloaded || data_region) + { + tree prev = NULL_TREE; + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + tree var, x; + default: + break; + case OMP_CLAUSE_FIRSTPRIVATE: + if (is_gimple_omp_oacc (ctx->stmt)) + break; + var = OMP_CLAUSE_DECL (c); + if (omp_privatize_by_reference (var) + || is_gimple_reg_type (TREE_TYPE (var))) + { + tree new_var = lookup_decl (var, ctx); + tree type; + type = TREE_TYPE (var); + if (omp_privatize_by_reference (var)) + type = TREE_TYPE (type); + if ((INTEGRAL_TYPE_P (type) + && TYPE_PRECISION (type) <= POINTER_SIZE) + || TREE_CODE (type) == POINTER_TYPE) + { + x = build_receiver_ref (var, false, ctx); + if (TREE_CODE (type) != POINTER_TYPE) + x = fold_convert (pointer_sized_int_node, x); + x = fold_convert (type, x); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, + fb_rvalue); + if (omp_privatize_by_reference (var)) + { + tree v = create_tmp_var_raw (type, get_name (var)); + gimple_add_tmp_var (v); + TREE_ADDRESSABLE (v) = 1; + gimple_seq_add_stmt (&new_body, + gimple_build_assign (v, x)); + x = build_fold_addr_expr (v); + } + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + } + else + { + bool by_ref = !omp_privatize_by_reference (var); + x = build_receiver_ref (var, by_ref, ctx); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, + fb_rvalue); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + } + } + else if (is_variable_sized (var)) + { + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + tree new_var = lookup_decl (pvar, ctx); + x = build_receiver_ref (var, false, ctx); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + } + break; + case OMP_CLAUSE_PRIVATE: + if (is_gimple_omp_oacc (ctx->stmt)) + break; + var = OMP_CLAUSE_DECL (c); + if (omp_privatize_by_reference (var)) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + tree new_var = lookup_decl (var, ctx); + x = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_var))); + if (TREE_CONSTANT (x)) + { + x = create_tmp_var_raw (TREE_TYPE (TREE_TYPE (new_var)), + get_name (var)); + gimple_add_tmp_var (x); + TREE_ADDRESSABLE (x) = 1; + x = build_fold_addr_expr_loc (clause_loc, x); + } + else + break; + + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + } + break; + case OMP_CLAUSE_USE_DEVICE_PTR: + case OMP_CLAUSE_USE_DEVICE_ADDR: + case OMP_CLAUSE_IS_DEVICE_PTR: + tree new_var; + gimple_seq assign_body; + bool is_array_data; + bool do_optional_check; + assign_body = NULL; + do_optional_check = false; + var = OMP_CLAUSE_DECL (c); + is_array_data = lang_hooks.decls.omp_array_data (var, true) != NULL; + + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR) + x = build_sender_ref (is_array_data + ? (splay_tree_key) &DECL_NAME (var) + : (splay_tree_key) &DECL_UID (var), ctx); + else + x = build_receiver_ref (var, false, ctx); + + if (is_array_data) + { + bool is_ref = omp_privatize_by_reference (var); + do_optional_check = true; + /* First, we copy the descriptor data from the host; then + we update its data to point to the target address. */ + new_var = lookup_decl (var, ctx); + new_var = DECL_VALUE_EXPR (new_var); + tree v = new_var; + + if (is_ref) + { + var = build_fold_indirect_ref (var); + gimplify_expr (&var, &assign_body, NULL, is_gimple_val, + fb_rvalue); + v = create_tmp_var_raw (TREE_TYPE (var), get_name (var)); + gimple_add_tmp_var (v); + TREE_ADDRESSABLE (v) = 1; + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (v, var)); + tree rhs = build_fold_addr_expr (v); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (new_var, rhs)); + } + else + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (new_var, var)); + + tree v2 = lang_hooks.decls.omp_array_data (unshare_expr (v), false); + gcc_assert (v2); + gimplify_expr (&x, &assign_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (v2, x)); + } + else if (is_variable_sized (var)) + { + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + new_var = lookup_decl (pvar, ctx); + gimplify_expr (&x, &assign_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (new_var, x)); + } + else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR + && !omp_privatize_by_reference (var) + && !omp_is_allocatable_or_ptr (var)) + || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE) + { + new_var = lookup_decl (var, ctx); + new_var = DECL_VALUE_EXPR (new_var); + gcc_assert (TREE_CODE (new_var) == MEM_REF); + new_var = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_var)); + gimplify_expr (&x, &assign_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (new_var, x)); + } + else + { + tree type = TREE_TYPE (var); + new_var = lookup_decl (var, ctx); + if (omp_privatize_by_reference (var)) + { + type = TREE_TYPE (type); + if (POINTER_TYPE_P (type) + && TREE_CODE (type) != ARRAY_TYPE + && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR + || (omp_privatize_by_reference (var) + && omp_is_allocatable_or_ptr (var)))) + { + tree v = create_tmp_var_raw (type, get_name (var)); + gimple_add_tmp_var (v); + TREE_ADDRESSABLE (v) = 1; + x = fold_convert (type, x); + gimplify_expr (&x, &assign_body, NULL, is_gimple_val, + fb_rvalue); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (v, x)); + x = build_fold_addr_expr (v); + do_optional_check = true; + } + } + new_var = DECL_VALUE_EXPR (new_var); + x = fold_convert (TREE_TYPE (new_var), x); + gimplify_expr (&x, &assign_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&assign_body, + gimple_build_assign (new_var, x)); + } + tree present; + present = (do_optional_check + ? omp_check_optional_argument (OMP_CLAUSE_DECL (c), true) + : NULL_TREE); + if (present) + { + tree null_label = create_artificial_label (UNKNOWN_LOCATION); + tree notnull_label = create_artificial_label (UNKNOWN_LOCATION); + tree opt_arg_label = create_artificial_label (UNKNOWN_LOCATION); + glabel *null_glabel = gimple_build_label (null_label); + glabel *notnull_glabel = gimple_build_label (notnull_label); + ggoto *opt_arg_ggoto = gimple_build_goto (opt_arg_label); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, + fb_rvalue); + gimplify_expr (&present, &new_body, NULL, is_gimple_val, + fb_rvalue); + gcond *cond = gimple_build_cond_from_tree (present, + notnull_label, + null_label); + gimple_seq_add_stmt (&new_body, cond); + gimple_seq_add_stmt (&new_body, null_glabel); + gimplify_assign (new_var, null_pointer_node, &new_body); + gimple_seq_add_stmt (&new_body, opt_arg_ggoto); + gimple_seq_add_stmt (&new_body, notnull_glabel); + gimple_seq_add_seq (&new_body, assign_body); + gimple_seq_add_stmt (&new_body, + gimple_build_label (opt_arg_label)); + } + else + gimple_seq_add_seq (&new_body, assign_body); + break; + } + /* Handle GOMP_MAP_FIRSTPRIVATE_{POINTER,REFERENCE} in second pass, + so that firstprivate vars holding OMP_CLAUSE_SIZE if needed + are already handled. Similarly OMP_CLAUSE_PRIVATE for VLAs + or references to VLAs. */ + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + switch (OMP_CLAUSE_CODE (c)) + { + tree var; + default: + break; + case OMP_CLAUSE_MAP: + if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_REFERENCE) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + poly_int64 offset = 0; + gcc_assert (prev); + var = OMP_CLAUSE_DECL (c); + if (DECL_P (var) + && TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE + && is_global_var (maybe_lookup_decl_in_outer_ctx (var, + ctx)) + && varpool_node::get_create (var)->offloadable) + break; + if (TREE_CODE (var) == INDIRECT_REF + && TREE_CODE (TREE_OPERAND (var, 0)) == COMPONENT_REF) + var = TREE_OPERAND (var, 0); + if (TREE_CODE (var) == COMPONENT_REF) + { + var = get_addr_base_and_unit_offset (var, &offset); + gcc_assert (var != NULL_TREE && DECL_P (var)); + } + else if (DECL_SIZE (var) + && TREE_CODE (DECL_SIZE (var)) != INTEGER_CST) + { + tree var2 = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (var2) == INDIRECT_REF); + var2 = TREE_OPERAND (var2, 0); + gcc_assert (DECL_P (var2)); + var = var2; + } + tree new_var = lookup_decl (var, ctx), x; + tree type = TREE_TYPE (new_var); + bool is_ref; + if (TREE_CODE (OMP_CLAUSE_DECL (c)) == INDIRECT_REF + && (TREE_CODE (TREE_OPERAND (OMP_CLAUSE_DECL (c), 0)) + == COMPONENT_REF)) + { + type = TREE_TYPE (TREE_OPERAND (OMP_CLAUSE_DECL (c), 0)); + is_ref = true; + new_var = build2 (MEM_REF, type, + build_fold_addr_expr (new_var), + build_int_cst (build_pointer_type (type), + offset)); + } + else if (TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF) + { + type = TREE_TYPE (OMP_CLAUSE_DECL (c)); + is_ref = TREE_CODE (type) == REFERENCE_TYPE; + new_var = build2 (MEM_REF, type, + build_fold_addr_expr (new_var), + build_int_cst (build_pointer_type (type), + offset)); + } + else + is_ref = omp_privatize_by_reference (var); + if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_REFERENCE) + is_ref = false; + bool ref_to_array = false; + if (is_ref) + { + type = TREE_TYPE (type); + if (TREE_CODE (type) == ARRAY_TYPE) + { + type = build_pointer_type (type); + ref_to_array = true; + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + tree decl2 = DECL_VALUE_EXPR (new_var); + gcc_assert (TREE_CODE (decl2) == MEM_REF); + decl2 = TREE_OPERAND (decl2, 0); + gcc_assert (DECL_P (decl2)); + new_var = decl2; + type = TREE_TYPE (new_var); + } + x = build_receiver_ref (OMP_CLAUSE_DECL (prev), false, ctx); + x = fold_convert_loc (clause_loc, type, x); + if (!integer_zerop (OMP_CLAUSE_SIZE (c))) + { + tree bias = OMP_CLAUSE_SIZE (c); + if (DECL_P (bias)) + bias = lookup_decl (bias, ctx); + bias = fold_convert_loc (clause_loc, sizetype, bias); + bias = fold_build1_loc (clause_loc, NEGATE_EXPR, sizetype, + bias); + x = fold_build2_loc (clause_loc, POINTER_PLUS_EXPR, + TREE_TYPE (x), x, bias); + } + if (ref_to_array) + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, fb_rvalue); + if (is_ref && !ref_to_array) + { + tree t = create_tmp_var_raw (type, get_name (var)); + gimple_add_tmp_var (t); + TREE_ADDRESSABLE (t) = 1; + gimple_seq_add_stmt (&new_body, + gimple_build_assign (t, x)); + x = build_fold_addr_expr_loc (clause_loc, t); + } + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + prev = NULL_TREE; + } + else if (OMP_CLAUSE_CHAIN (c) + && OMP_CLAUSE_CODE (OMP_CLAUSE_CHAIN (c)) + == OMP_CLAUSE_MAP + && (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (c)) + == GOMP_MAP_FIRSTPRIVATE_POINTER + || (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (c)) + == GOMP_MAP_FIRSTPRIVATE_REFERENCE))) + prev = c; + break; + case OMP_CLAUSE_PRIVATE: + var = OMP_CLAUSE_DECL (c); + if (is_variable_sized (var)) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + tree new_var = lookup_decl (var, ctx); + tree pvar = DECL_VALUE_EXPR (var); + gcc_assert (TREE_CODE (pvar) == INDIRECT_REF); + pvar = TREE_OPERAND (pvar, 0); + gcc_assert (DECL_P (pvar)); + tree new_pvar = lookup_decl (pvar, ctx); + tree atmp = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); + tree al = size_int (DECL_ALIGN (var)); + tree x = TYPE_SIZE_UNIT (TREE_TYPE (new_var)); + x = build_call_expr_loc (clause_loc, atmp, 2, x, al); + x = fold_convert_loc (clause_loc, TREE_TYPE (new_pvar), x); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_pvar, x)); + } + else if (omp_privatize_by_reference (var) + && !is_gimple_omp_oacc (ctx->stmt)) + { + location_t clause_loc = OMP_CLAUSE_LOCATION (c); + tree new_var = lookup_decl (var, ctx); + tree x = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_var))); + if (TREE_CONSTANT (x)) + break; + else + { + tree atmp + = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN); + tree rtype = TREE_TYPE (TREE_TYPE (new_var)); + tree al = size_int (TYPE_ALIGN (rtype)); + x = build_call_expr_loc (clause_loc, atmp, 2, x, al); + } + + x = fold_convert_loc (clause_loc, TREE_TYPE (new_var), x); + gimplify_expr (&x, &new_body, NULL, is_gimple_val, fb_rvalue); + gimple_seq_add_stmt (&new_body, + gimple_build_assign (new_var, x)); + } + break; + } + + gimple_seq fork_seq = NULL; + gimple_seq join_seq = NULL; + + if (offloaded && is_gimple_omp_oacc (ctx->stmt)) + { + /* If there are reductions on the offloaded region itself, treat + them as a dummy GANG loop. */ + tree level = build_int_cst (integer_type_node, GOMP_DIM_GANG); + + gcall *private_marker = lower_oacc_private_marker (ctx); + + if (private_marker) + gimple_call_set_arg (private_marker, 2, level); + + lower_oacc_reductions (gimple_location (ctx->stmt), clauses, level, + false, NULL, private_marker, NULL, &fork_seq, + &join_seq, ctx); + } + + gimple_seq_add_seq (&new_body, fork_seq); + gimple_seq_add_seq (&new_body, tgt_body); + gimple_seq_add_seq (&new_body, join_seq); + + if (offloaded) + { + new_body = maybe_catch_exception (new_body); + gimple_seq_add_stmt (&new_body, gimple_build_omp_return (false)); + } + gimple_omp_set_body (stmt, new_body); + } + + bind = gimple_build_bind (NULL, NULL, + tgt_bind ? gimple_bind_block (tgt_bind) + : NULL_TREE); + gsi_replace (gsi_p, dep_bind ? dep_bind : bind, true); + gimple_bind_add_seq (bind, ilist); + gimple_bind_add_stmt (bind, stmt); + gimple_bind_add_seq (bind, olist); + + pop_gimplify_context (NULL); + + if (dep_bind) + { + gimple_bind_add_seq (dep_bind, dep_ilist); + gimple_bind_add_stmt (dep_bind, bind); + gimple_bind_add_seq (dep_bind, dep_olist); + pop_gimplify_context (dep_bind); + } +} + +/* Expand code for an OpenMP teams directive. */ + +static void +lower_omp_teams (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + gomp_teams *teams_stmt = as_a <gomp_teams *> (gsi_stmt (*gsi_p)); + push_gimplify_context (); + + tree block = make_node (BLOCK); + gbind *bind = gimple_build_bind (NULL, NULL, block); + gsi_replace (gsi_p, bind, true); + gimple_seq bind_body = NULL; + gimple_seq dlist = NULL; + gimple_seq olist = NULL; + + tree num_teams = omp_find_clause (gimple_omp_teams_clauses (teams_stmt), + OMP_CLAUSE_NUM_TEAMS); + tree num_teams_lower = NULL_TREE; + if (num_teams == NULL_TREE) + num_teams = build_int_cst (unsigned_type_node, 0); + else + { + num_teams_lower = OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (num_teams); + if (num_teams_lower) + { + num_teams_lower = fold_convert (unsigned_type_node, num_teams_lower); + gimplify_expr (&num_teams_lower, &bind_body, NULL, is_gimple_val, + fb_rvalue); + } + num_teams = OMP_CLAUSE_NUM_TEAMS_UPPER_EXPR (num_teams); + num_teams = fold_convert (unsigned_type_node, num_teams); + gimplify_expr (&num_teams, &bind_body, NULL, is_gimple_val, fb_rvalue); + } + if (num_teams_lower == NULL_TREE) + num_teams_lower = num_teams; + tree thread_limit = omp_find_clause (gimple_omp_teams_clauses (teams_stmt), + OMP_CLAUSE_THREAD_LIMIT); + if (thread_limit == NULL_TREE) + thread_limit = build_int_cst (unsigned_type_node, 0); + else + { + thread_limit = OMP_CLAUSE_THREAD_LIMIT_EXPR (thread_limit); + thread_limit = fold_convert (unsigned_type_node, thread_limit); + gimplify_expr (&thread_limit, &bind_body, NULL, is_gimple_val, + fb_rvalue); + } + location_t loc = gimple_location (teams_stmt); + tree decl = builtin_decl_explicit (BUILT_IN_GOMP_TEAMS4); + tree rettype = TREE_TYPE (TREE_TYPE (decl)); + tree first = create_tmp_var (rettype); + gimple_seq_add_stmt (&bind_body, + gimple_build_assign (first, build_one_cst (rettype))); + tree llabel = create_artificial_label (loc); + gimple_seq_add_stmt (&bind_body, gimple_build_label (llabel)); + gimple *call + = gimple_build_call (decl, 4, num_teams_lower, num_teams, thread_limit, + first); + gimple_set_location (call, loc); + tree temp = create_tmp_var (rettype); + gimple_call_set_lhs (call, temp); + gimple_seq_add_stmt (&bind_body, call); + + tree tlabel = create_artificial_label (loc); + tree flabel = create_artificial_label (loc); + gimple *cond = gimple_build_cond (NE_EXPR, temp, build_zero_cst (rettype), + tlabel, flabel); + gimple_seq_add_stmt (&bind_body, cond); + gimple_seq_add_stmt (&bind_body, gimple_build_label (tlabel)); + gimple_seq_add_stmt (&bind_body, + gimple_build_assign (first, build_zero_cst (rettype))); + + lower_rec_input_clauses (gimple_omp_teams_clauses (teams_stmt), + &bind_body, &dlist, ctx, NULL); + lower_omp (gimple_omp_body_ptr (teams_stmt), ctx); + lower_reduction_clauses (gimple_omp_teams_clauses (teams_stmt), &olist, + NULL, ctx); + gimple_seq_add_stmt (&bind_body, teams_stmt); + + gimple_seq_add_seq (&bind_body, gimple_omp_body (teams_stmt)); + gimple_omp_set_body (teams_stmt, NULL); + gimple_seq_add_seq (&bind_body, olist); + gimple_seq_add_seq (&bind_body, dlist); + gimple_seq_add_stmt (&bind_body, gimple_build_omp_return (true)); + gimple_seq_add_stmt (&bind_body, gimple_build_goto (llabel)); + gimple_seq_add_stmt (&bind_body, gimple_build_label (flabel)); + gimple_bind_set_body (bind, bind_body); + + pop_gimplify_context (bind); + + gimple_bind_append_vars (bind, ctx->block_vars); + BLOCK_VARS (block) = ctx->block_vars; + if (BLOCK_VARS (block)) + TREE_USED (block) = 1; +} + +/* Callback for lower_omp_1. Return non-NULL if *tp needs to be + regimplified. If DATA is non-NULL, lower_omp_1 is outside + of OMP context, but with task_shared_vars set. */ + +static tree +lower_omp_regimplify_p (tree *tp, int *walk_subtrees, + void *data) +{ + tree t = *tp; + + /* Any variable with DECL_VALUE_EXPR needs to be regimplified. */ + if ((VAR_P (t) || TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL) + && data == NULL + && DECL_HAS_VALUE_EXPR_P (t)) + return t; + + if (task_shared_vars + && DECL_P (t) + && bitmap_bit_p (task_shared_vars, DECL_UID (t))) + return t; + + /* If a global variable has been privatized, TREE_CONSTANT on + ADDR_EXPR might be wrong. */ + if (data == NULL && TREE_CODE (t) == ADDR_EXPR) + recompute_tree_invariant_for_addr_expr (t); + + *walk_subtrees = !IS_TYPE_OR_DECL_P (t); + return NULL_TREE; +} + +/* Data to be communicated between lower_omp_regimplify_operands and + lower_omp_regimplify_operands_p. */ + +struct lower_omp_regimplify_operands_data +{ + omp_context *ctx; + vec<tree> *decls; +}; + +/* Helper function for lower_omp_regimplify_operands. Find + omp_member_access_dummy_var vars and adjust temporarily their + DECL_VALUE_EXPRs if needed. */ + +static tree +lower_omp_regimplify_operands_p (tree *tp, int *walk_subtrees, + void *data) +{ + tree t = omp_member_access_dummy_var (*tp); + if (t) + { + struct walk_stmt_info *wi = (struct walk_stmt_info *) data; + lower_omp_regimplify_operands_data *ldata + = (lower_omp_regimplify_operands_data *) wi->info; + tree o = maybe_lookup_decl (t, ldata->ctx); + if (o != t) + { + ldata->decls->safe_push (DECL_VALUE_EXPR (*tp)); + ldata->decls->safe_push (*tp); + tree v = unshare_and_remap (DECL_VALUE_EXPR (*tp), t, o); + SET_DECL_VALUE_EXPR (*tp, v); + } + } + *walk_subtrees = !IS_TYPE_OR_DECL_P (*tp); + return NULL_TREE; +} + +/* Wrapper around gimple_regimplify_operands that adjusts DECL_VALUE_EXPRs + of omp_member_access_dummy_var vars during regimplification. */ + +static void +lower_omp_regimplify_operands (omp_context *ctx, gimple *stmt, + gimple_stmt_iterator *gsi_p) +{ + auto_vec<tree, 10> decls; + if (ctx) + { + struct walk_stmt_info wi; + memset (&wi, '\0', sizeof (wi)); + struct lower_omp_regimplify_operands_data data; + data.ctx = ctx; + data.decls = &decls; + wi.info = &data; + walk_gimple_op (stmt, lower_omp_regimplify_operands_p, &wi); + } + gimple_regimplify_operands (stmt, gsi_p); + while (!decls.is_empty ()) + { + tree t = decls.pop (); + tree v = decls.pop (); + SET_DECL_VALUE_EXPR (t, v); + } +} + +static void +lower_omp_1 (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + gimple *stmt = gsi_stmt (*gsi_p); + struct walk_stmt_info wi; + gcall *call_stmt; + + if (gimple_has_location (stmt)) + input_location = gimple_location (stmt); + + if (task_shared_vars) + memset (&wi, '\0', sizeof (wi)); + + /* If we have issued syntax errors, avoid doing any heavy lifting. + Just replace the OMP directives with a NOP to avoid + confusing RTL expansion. */ + if (seen_error () && is_gimple_omp (stmt)) + { + gsi_replace (gsi_p, gimple_build_nop (), true); + return; + } + + switch (gimple_code (stmt)) + { + case GIMPLE_COND: + { + gcond *cond_stmt = as_a <gcond *> (stmt); + if ((ctx || task_shared_vars) + && (walk_tree (gimple_cond_lhs_ptr (cond_stmt), + lower_omp_regimplify_p, + ctx ? NULL : &wi, NULL) + || walk_tree (gimple_cond_rhs_ptr (cond_stmt), + lower_omp_regimplify_p, + ctx ? NULL : &wi, NULL))) + lower_omp_regimplify_operands (ctx, cond_stmt, gsi_p); + } + break; + case GIMPLE_CATCH: + lower_omp (gimple_catch_handler_ptr (as_a <gcatch *> (stmt)), ctx); + break; + case GIMPLE_EH_FILTER: + lower_omp (gimple_eh_filter_failure_ptr (stmt), ctx); + break; + case GIMPLE_TRY: + lower_omp (gimple_try_eval_ptr (stmt), ctx); + lower_omp (gimple_try_cleanup_ptr (stmt), ctx); + break; + case GIMPLE_TRANSACTION: + lower_omp (gimple_transaction_body_ptr (as_a <gtransaction *> (stmt)), + ctx); + break; + case GIMPLE_BIND: + if (ctx && is_gimple_omp_oacc (ctx->stmt)) + { + tree vars = gimple_bind_vars (as_a <gbind *> (stmt)); + oacc_privatization_scan_decl_chain (ctx, vars); + } + lower_omp (gimple_bind_body_ptr (as_a <gbind *> (stmt)), ctx); + maybe_remove_omp_member_access_dummy_vars (as_a <gbind *> (stmt)); + break; + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TASK: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + if (ctx->cancellable) + ctx->cancel_label = create_artificial_label (UNKNOWN_LOCATION); + lower_omp_taskreg (gsi_p, ctx); + break; + case GIMPLE_OMP_FOR: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + if (ctx->cancellable) + ctx->cancel_label = create_artificial_label (UNKNOWN_LOCATION); + lower_omp_for (gsi_p, ctx); + break; + case GIMPLE_OMP_SECTIONS: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + if (ctx->cancellable) + ctx->cancel_label = create_artificial_label (UNKNOWN_LOCATION); + lower_omp_sections (gsi_p, ctx); + break; + case GIMPLE_OMP_SCOPE: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_scope (gsi_p, ctx); + break; + case GIMPLE_OMP_SINGLE: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_single (gsi_p, ctx); + break; + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_master (gsi_p, ctx); + break; + case GIMPLE_OMP_TASKGROUP: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_taskgroup (gsi_p, ctx); + break; + case GIMPLE_OMP_ORDERED: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_ordered (gsi_p, ctx); + break; + case GIMPLE_OMP_SCAN: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_scan (gsi_p, ctx); + break; + case GIMPLE_OMP_CRITICAL: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_critical (gsi_p, ctx); + break; + case GIMPLE_OMP_ATOMIC_LOAD: + if ((ctx || task_shared_vars) + && walk_tree (gimple_omp_atomic_load_rhs_ptr ( + as_a <gomp_atomic_load *> (stmt)), + lower_omp_regimplify_p, ctx ? NULL : &wi, NULL)) + lower_omp_regimplify_operands (ctx, stmt, gsi_p); + break; + case GIMPLE_OMP_TARGET: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + lower_omp_target (gsi_p, ctx); + break; + case GIMPLE_OMP_TEAMS: + ctx = maybe_lookup_ctx (stmt); + gcc_assert (ctx); + if (gimple_omp_teams_host (as_a <gomp_teams *> (stmt))) + lower_omp_taskreg (gsi_p, ctx); + else + lower_omp_teams (gsi_p, ctx); + break; + case GIMPLE_CALL: + tree fndecl; + call_stmt = as_a <gcall *> (stmt); + fndecl = gimple_call_fndecl (call_stmt); + if (fndecl + && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_GOMP_BARRIER: + if (ctx == NULL) + break; + /* FALLTHRU */ + case BUILT_IN_GOMP_CANCEL: + case BUILT_IN_GOMP_CANCELLATION_POINT: + omp_context *cctx; + cctx = ctx; + if (gimple_code (cctx->stmt) == GIMPLE_OMP_SECTION) + cctx = cctx->outer; + gcc_assert (gimple_call_lhs (call_stmt) == NULL_TREE); + if (!cctx->cancellable) + { + if (DECL_FUNCTION_CODE (fndecl) + == BUILT_IN_GOMP_CANCELLATION_POINT) + { + stmt = gimple_build_nop (); + gsi_replace (gsi_p, stmt, false); + } + break; + } + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_GOMP_BARRIER) + { + fndecl = builtin_decl_explicit (BUILT_IN_GOMP_BARRIER_CANCEL); + gimple_call_set_fndecl (call_stmt, fndecl); + gimple_call_set_fntype (call_stmt, TREE_TYPE (fndecl)); + } + tree lhs; + lhs = create_tmp_var (TREE_TYPE (TREE_TYPE (fndecl))); + gimple_call_set_lhs (call_stmt, lhs); + tree fallthru_label; + fallthru_label = create_artificial_label (UNKNOWN_LOCATION); + gimple *g; + g = gimple_build_label (fallthru_label); + gsi_insert_after (gsi_p, g, GSI_SAME_STMT); + g = gimple_build_cond (NE_EXPR, lhs, + fold_convert (TREE_TYPE (lhs), + boolean_false_node), + cctx->cancel_label, fallthru_label); + gsi_insert_after (gsi_p, g, GSI_SAME_STMT); + break; + default: + break; + } + goto regimplify; + + case GIMPLE_ASSIGN: + for (omp_context *up = ctx; up; up = up->outer) + { + if (gimple_code (up->stmt) == GIMPLE_OMP_ORDERED + || gimple_code (up->stmt) == GIMPLE_OMP_CRITICAL + || gimple_code (up->stmt) == GIMPLE_OMP_TASKGROUP + || gimple_code (up->stmt) == GIMPLE_OMP_SCOPE + || gimple_code (up->stmt) == GIMPLE_OMP_SECTION + || gimple_code (up->stmt) == GIMPLE_OMP_SCAN + || (gimple_code (up->stmt) == GIMPLE_OMP_TARGET + && (gimple_omp_target_kind (up->stmt) + == GF_OMP_TARGET_KIND_DATA))) + continue; + else if (!up->lastprivate_conditional_map) + break; + tree lhs = get_base_address (gimple_assign_lhs (stmt)); + if (TREE_CODE (lhs) == MEM_REF + && DECL_P (TREE_OPERAND (lhs, 0)) + && TREE_CODE (TREE_TYPE (TREE_OPERAND (lhs, + 0))) == REFERENCE_TYPE) + lhs = TREE_OPERAND (lhs, 0); + if (DECL_P (lhs)) + if (tree *v = up->lastprivate_conditional_map->get (lhs)) + { + tree clauses; + if (up->combined_into_simd_safelen1) + { + up = up->outer; + if (gimple_code (up->stmt) == GIMPLE_OMP_SCAN) + up = up->outer; + } + if (gimple_code (up->stmt) == GIMPLE_OMP_FOR) + clauses = gimple_omp_for_clauses (up->stmt); + else + clauses = gimple_omp_sections_clauses (up->stmt); + tree c = omp_find_clause (clauses, OMP_CLAUSE__CONDTEMP_); + if (!OMP_CLAUSE__CONDTEMP__ITER (c)) + c = omp_find_clause (OMP_CLAUSE_CHAIN (c), + OMP_CLAUSE__CONDTEMP_); + gcc_assert (OMP_CLAUSE__CONDTEMP__ITER (c)); + gimple *g = gimple_build_assign (*v, OMP_CLAUSE_DECL (c)); + gsi_insert_after (gsi_p, g, GSI_SAME_STMT); + } + } + /* FALLTHRU */ + + default: + regimplify: + if ((ctx || task_shared_vars) + && walk_gimple_op (stmt, lower_omp_regimplify_p, + ctx ? NULL : &wi)) + { + /* Just remove clobbers, this should happen only if we have + "privatized" local addressable variables in SIMD regions, + the clobber isn't needed in that case and gimplifying address + of the ARRAY_REF into a pointer and creating MEM_REF based + clobber would create worse code than we get with the clobber + dropped. */ + if (gimple_clobber_p (stmt)) + { + gsi_replace (gsi_p, gimple_build_nop (), true); + break; + } + lower_omp_regimplify_operands (ctx, stmt, gsi_p); + } + break; + } +} + +static void +lower_omp (gimple_seq *body, omp_context *ctx) +{ + location_t saved_location = input_location; + gimple_stmt_iterator gsi; + for (gsi = gsi_start (*body); !gsi_end_p (gsi); gsi_next (&gsi)) + lower_omp_1 (&gsi, ctx); + /* During gimplification, we haven't folded statments inside offloading + or taskreg regions (gimplify.c:maybe_fold_stmt); do that now. */ + if (target_nesting_level || taskreg_nesting_level) + for (gsi = gsi_start (*body); !gsi_end_p (gsi); gsi_next (&gsi)) + fold_stmt (&gsi); + input_location = saved_location; +} + +/* Main entry point. */ + +static unsigned int +execute_lower_omp (void) +{ + gimple_seq body; + int i; + omp_context *ctx; + + /* This pass always runs, to provide PROP_gimple_lomp. + But often, there is nothing to do. */ + if (flag_openacc == 0 && flag_openmp == 0 + && flag_openmp_simd == 0) + return 0; + + all_contexts = splay_tree_new (splay_tree_compare_pointers, 0, + delete_omp_context); + + body = gimple_body (current_function_decl); + + scan_omp (&body, NULL); + gcc_assert (taskreg_nesting_level == 0); + FOR_EACH_VEC_ELT (taskreg_contexts, i, ctx) + finish_taskreg_scan (ctx); + taskreg_contexts.release (); + + if (all_contexts->root) + { + if (task_shared_vars) + push_gimplify_context (); + lower_omp (&body, NULL); + if (task_shared_vars) + pop_gimplify_context (NULL); + } + + if (all_contexts) + { + splay_tree_delete (all_contexts); + all_contexts = NULL; + } + BITMAP_FREE (task_shared_vars); + BITMAP_FREE (global_nonaddressable_vars); + + /* If current function is a method, remove artificial dummy VAR_DECL created + for non-static data member privatization, they aren't needed for + debuginfo nor anything else, have been already replaced everywhere in the + IL and cause problems with LTO. */ + if (DECL_ARGUMENTS (current_function_decl) + && DECL_ARTIFICIAL (DECL_ARGUMENTS (current_function_decl)) + && (TREE_CODE (TREE_TYPE (DECL_ARGUMENTS (current_function_decl))) + == POINTER_TYPE)) + remove_member_access_dummy_vars (DECL_INITIAL (current_function_decl)); + return 0; +} + +namespace { + +const pass_data pass_data_lower_omp = +{ + GIMPLE_PASS, /* type */ + "omplower", /* name */ + OPTGROUP_OMP, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_gimple_any, /* properties_required */ + PROP_gimple_lomp | PROP_gimple_lomp_dev, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_lower_omp : public gimple_opt_pass +{ +public: + pass_lower_omp (gcc::context *ctxt) + : gimple_opt_pass (pass_data_lower_omp, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *) { return execute_lower_omp (); } + +}; // class pass_lower_omp + +} // anon namespace + +gimple_opt_pass * +make_pass_lower_omp (gcc::context *ctxt) +{ + return new pass_lower_omp (ctxt); +} + +/* The following is a utility to diagnose structured block violations. + It is not part of the "omplower" pass, as that's invoked too late. It + should be invoked by the respective front ends after gimplification. */ + +static splay_tree all_labels; + +/* Check for mismatched contexts and generate an error if needed. Return + true if an error is detected. */ + +static bool +diagnose_sb_0 (gimple_stmt_iterator *gsi_p, + gimple *branch_ctx, gimple *label_ctx) +{ + gcc_checking_assert (!branch_ctx || is_gimple_omp (branch_ctx)); + gcc_checking_assert (!label_ctx || is_gimple_omp (label_ctx)); + + if (label_ctx == branch_ctx) + return false; + + const char* kind = NULL; + + if (flag_openacc) + { + if ((branch_ctx && is_gimple_omp_oacc (branch_ctx)) + || (label_ctx && is_gimple_omp_oacc (label_ctx))) + { + gcc_checking_assert (kind == NULL); + kind = "OpenACC"; + } + } + if (kind == NULL) + { + gcc_checking_assert (flag_openmp || flag_openmp_simd); + kind = "OpenMP"; + } + + /* Previously we kept track of the label's entire context in diagnose_sb_[12] + so we could traverse it and issue a correct "exit" or "enter" error + message upon a structured block violation. + + We built the context by building a list with tree_cons'ing, but there is + no easy counterpart in gimple tuples. It seems like far too much work + for issuing exit/enter error messages. If someone really misses the + distinct error message... patches welcome. */ + +#if 0 + /* Try to avoid confusing the user by producing and error message + with correct "exit" or "enter" verbiage. We prefer "exit" + unless we can show that LABEL_CTX is nested within BRANCH_CTX. */ + if (branch_ctx == NULL) + exit_p = false; + else + { + while (label_ctx) + { + if (TREE_VALUE (label_ctx) == branch_ctx) + { + exit_p = false; + break; + } + label_ctx = TREE_CHAIN (label_ctx); + } + } + + if (exit_p) + error ("invalid exit from %s structured block", kind); + else + error ("invalid entry to %s structured block", kind); +#endif + + /* If it's obvious we have an invalid entry, be specific about the error. */ + if (branch_ctx == NULL) + error ("invalid entry to %s structured block", kind); + else + { + /* Otherwise, be vague and lazy, but efficient. */ + error ("invalid branch to/from %s structured block", kind); + } + + gsi_replace (gsi_p, gimple_build_nop (), false); + return true; +} + +/* Pass 1: Create a minimal tree of structured blocks, and record + where each label is found. */ + +static tree +diagnose_sb_1 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + gimple *context = (gimple *) wi->info; + gimple *inner_context; + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + + switch (gimple_code (stmt)) + { + WALK_SUBSTMTS; + + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TASK: + case GIMPLE_OMP_SCOPE: + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_SECTION: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_SCAN: + case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_TARGET: + case GIMPLE_OMP_TEAMS: + case GIMPLE_OMP_TASKGROUP: + /* The minimal context here is just the current OMP construct. */ + inner_context = stmt; + wi->info = inner_context; + walk_gimple_seq (gimple_omp_body (stmt), diagnose_sb_1, NULL, wi); + wi->info = context; + break; + + case GIMPLE_OMP_FOR: + inner_context = stmt; + wi->info = inner_context; + /* gimple_omp_for_{index,initial,final} are all DECLs; no need to + walk them. */ + walk_gimple_seq (gimple_omp_for_pre_body (stmt), + diagnose_sb_1, NULL, wi); + walk_gimple_seq (gimple_omp_body (stmt), diagnose_sb_1, NULL, wi); + wi->info = context; + break; + + case GIMPLE_LABEL: + splay_tree_insert (all_labels, + (splay_tree_key) gimple_label_label ( + as_a <glabel *> (stmt)), + (splay_tree_value) context); + break; + + default: + break; + } + + return NULL_TREE; +} + +/* Pass 2: Check each branch and see if its context differs from that of + the destination label's context. */ + +static tree +diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, + struct walk_stmt_info *wi) +{ + gimple *context = (gimple *) wi->info; + splay_tree_node n; + gimple *stmt = gsi_stmt (*gsi_p); + + *handled_ops_p = true; + + switch (gimple_code (stmt)) + { + WALK_SUBSTMTS; + + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TASK: + case GIMPLE_OMP_SCOPE: + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_SECTION: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_MASKED: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_SCAN: + case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_TARGET: + case GIMPLE_OMP_TEAMS: + case GIMPLE_OMP_TASKGROUP: + wi->info = stmt; + walk_gimple_seq_mod (gimple_omp_body_ptr (stmt), diagnose_sb_2, NULL, wi); + wi->info = context; + break; + + case GIMPLE_OMP_FOR: + wi->info = stmt; + /* gimple_omp_for_{index,initial,final} are all DECLs; no need to + walk them. */ + walk_gimple_seq_mod (gimple_omp_for_pre_body_ptr (stmt), + diagnose_sb_2, NULL, wi); + walk_gimple_seq_mod (gimple_omp_body_ptr (stmt), diagnose_sb_2, NULL, wi); + wi->info = context; + break; + + case GIMPLE_COND: + { + gcond *cond_stmt = as_a <gcond *> (stmt); + tree lab = gimple_cond_true_label (cond_stmt); + if (lab) + { + n = splay_tree_lookup (all_labels, + (splay_tree_key) lab); + diagnose_sb_0 (gsi_p, context, + n ? (gimple *) n->value : NULL); + } + lab = gimple_cond_false_label (cond_stmt); + if (lab) + { + n = splay_tree_lookup (all_labels, + (splay_tree_key) lab); + diagnose_sb_0 (gsi_p, context, + n ? (gimple *) n->value : NULL); + } + } + break; + + case GIMPLE_GOTO: + { + tree lab = gimple_goto_dest (stmt); + if (TREE_CODE (lab) != LABEL_DECL) + break; + + n = splay_tree_lookup (all_labels, (splay_tree_key) lab); + diagnose_sb_0 (gsi_p, context, n ? (gimple *) n->value : NULL); + } + break; + + case GIMPLE_SWITCH: + { + gswitch *switch_stmt = as_a <gswitch *> (stmt); + unsigned int i; + for (i = 0; i < gimple_switch_num_labels (switch_stmt); ++i) + { + tree lab = CASE_LABEL (gimple_switch_label (switch_stmt, i)); + n = splay_tree_lookup (all_labels, (splay_tree_key) lab); + if (n && diagnose_sb_0 (gsi_p, context, (gimple *) n->value)) + break; + } + } + break; + + case GIMPLE_RETURN: + diagnose_sb_0 (gsi_p, context, NULL); + break; + + default: + break; + } + + return NULL_TREE; +} + +static unsigned int +diagnose_omp_structured_block_errors (void) +{ + struct walk_stmt_info wi; + gimple_seq body = gimple_body (current_function_decl); + + all_labels = splay_tree_new (splay_tree_compare_pointers, 0, 0); + + memset (&wi, 0, sizeof (wi)); + walk_gimple_seq (body, diagnose_sb_1, NULL, &wi); + + memset (&wi, 0, sizeof (wi)); + wi.want_locations = true; + walk_gimple_seq_mod (&body, diagnose_sb_2, NULL, &wi); + + gimple_set_body (current_function_decl, body); + + splay_tree_delete (all_labels); + all_labels = NULL; + + return 0; +} + +namespace { + +const pass_data pass_data_diagnose_omp_blocks = +{ + GIMPLE_PASS, /* type */ + "*diagnose_omp_blocks", /* name */ + OPTGROUP_OMP, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_gimple_any, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_diagnose_omp_blocks : public gimple_opt_pass +{ +public: + pass_diagnose_omp_blocks (gcc::context *ctxt) + : gimple_opt_pass (pass_data_diagnose_omp_blocks, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return flag_openacc || flag_openmp || flag_openmp_simd; + } + virtual unsigned int execute (function *) + { + return diagnose_omp_structured_block_errors (); + } + +}; // class pass_diagnose_omp_blocks + +} // anon namespace + +gimple_opt_pass * +make_pass_diagnose_omp_blocks (gcc::context *ctxt) +{ + return new pass_diagnose_omp_blocks (ctxt); +} + + +#include "gt-omp-low.h" |