diff options
| -rw-r--r-- | gcc/omp-general.cc | 203 | ||||
| -rw-r--r-- | gcc/omp-general.h | 5 |
2 files changed, 208 insertions, 0 deletions
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 973b246cfdb..194dff3e892 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -1501,6 +1501,75 @@ omp_check_context_selector (location_t loc, tree ctx, return ctx; } +/* Produce a mangled version of BASE_ID for the name of the variant + function with context selector CTX. SEP is a separator string. + The return value is an IDENTIFIER_NODE. + + Per the OpenMP spec, "the symbol names of two definitions of a function are + considered to be equal if and only if their effective context selectors are + equivalent". However, if we did have two such definitions, we'd get an ODR + violation. We already take steps in the front ends to make variant + functions internal to the compilation unit, since there is no (portable) way + to reference them directly by name or declare them as extern in another + compilation unit. So, we can diagnose the would-be ODR violations by + checking that there is not already a variant for the same function with an + equivalent context selector, and otherwise just use a simple counter to name + the variant functions instead of any complicated scheme to encode the + context selector in the name. + + C++ and Fortran modules are an exception to this, as variants in a module + interface unit are visible to implementation TUs for that module. This + is handled in the C++ front end by adding an additional prefix to SEP for + variants in a module interface to prevent collisions with names in the + other TUs. The Fortran and C++ front ends add the module name to the + output of this function using their normal mechanisms for symbols with + module linkage. */ +tree +omp_mangle_variant_name (tree base_id, tree ctx ATTRIBUTE_UNUSED, + const char *sep) +{ + const char *base_name = IDENTIFIER_POINTER (base_id); + + /* Now do the actual mangling. */ + static int variant_counter; + /* The numeric suffix and terminating byte ought to need way less than + 32 bytes extra, that's just a magic number. */ + size_t buflen = (strlen (base_name) + strlen (sep) + strlen ("ompvariant") + + 32); + char *buffer = (char *) alloca (buflen); + buflen = snprintf (buffer, buflen, "%s%sompvariant%d", base_name, sep, + ++variant_counter); + return get_identifier_with_length (buffer, buflen); +} + +/* Forward declaration. */ +static int omp_context_selector_compare (tree ctx1, tree ctx2); + +/* Diagnose an error if there is already a variant with CTX registered + for BASE_DECL. Returns true if OK, false otherwise. */ +bool +omp_check_for_duplicate_variant (location_t loc, tree base_decl, tree ctx) +{ + for (tree attr = DECL_ATTRIBUTES (base_decl); attr; attr = TREE_CHAIN (attr)) + { + attr = lookup_attribute ("omp declare variant base", attr); + if (attr == NULL_TREE) + break; + + tree selector = TREE_VALUE (TREE_VALUE (attr)); + if (omp_context_selector_compare (ctx, selector) == 0) + { + error_at (loc, + "multiple definitions of variants with the same " + "context selector violate the one-definition rule"); + inform (DECL_SOURCE_LOCATION (TREE_PURPOSE (attr)), + "previous variant declaration here"); + return false; + } + } + return true; +} + /* Forward declarations. */ static int omp_context_selector_set_compare (enum omp_tss_code, tree, tree); static int omp_construct_simd_compare (tree, tree, bool); @@ -4928,3 +4997,137 @@ omp_maybe_apply_loop_xforms (tree *expr_p, tree for_clauses) } } +/* The next group of functions support merging of context selectors for + nested "begin declare variant" directives. The spec says: + + ...the effective context selectors of the outer directive are + appended to the context selector of the inner directive to form the + effective context selector of the inner directive. If a + trait-set-selector is present on both directives, the trait-selector + list of the outer directive is appended to the trait-selector list + of the inner directive after equivalent trait-selectors have been + removed from the outer list. + + In other words, there is no requirement to combine non-equivalent + trait-selectors according to their peculiar semantics, such as allowing + "any" as a wildcard, ANDing trait-property-expressions of "condition" trait + property expressions together, etc. Also there is no special provision for + treating the "construct" selector as an ordered list. + + Note that the spec does not explicitly say what "equivalent" means; + whether the properties and score of the trait-selectors must be identical, + or only the name of the trait-selector. This code assumes the former + except for the construct trait set where the order of selectors + is significant (so that it is *not* equivalent to have the same + trait-selector appearing in a different order in the list). */ + +/* Copy traits from FROM_TS and push them onto TAIL. */ + +static tree +omp_copy_trait_set (tree from_ts, tree tail) +{ + for (tree ts = from_ts; ts; ts = TREE_CHAIN (ts)) + tail = make_trait_selector (OMP_TS_CODE (ts), OMP_TS_SCORE (ts), + OMP_TS_PROPERTIES (ts), tail); + return nreverse (tail); +} + +/* Return true if trait selectors TS1 and TS2 for set TSS are "equivalent". */ + +static bool +omp_trait_selectors_equivalent (tree ts1, tree ts2, enum omp_tss_code tss) +{ + if (OMP_TS_CODE (ts1) != OMP_TS_CODE (ts2)) + return false; + + tree score1 = OMP_TS_SCORE (ts1); + tree score2 = OMP_TS_SCORE (ts2); + if ((score1 && score2 && !simple_cst_equal (score1, score2)) + || (score1 && !score2) + || (!score1 && score2)) + return false; + + return (omp_context_selector_props_compare (tss, OMP_TS_CODE (ts1), + OMP_TS_PROPERTIES (ts1), + OMP_TS_PROPERTIES (ts2)) + == 0); +} + +/* Merge lists of the trait selectors OUTER_TS and INNER_TS for selector set + TSS: "the trait-selector list of the outer directive is appended to the + trait-selector list of the inner directive after equivalent trait-selectors + have been removed from the outer list". */ + +static tree +omp_combine_trait_sets (tree outer_ts, tree inner_ts, enum omp_tss_code tss) +{ + unsigned HOST_WIDE_INT inner_traits = 0; + tree to_list = NULL_TREE; + + for (tree inner = inner_ts; inner; inner = TREE_CHAIN (inner)) + { + omp_ts_code ts_code = OMP_TS_CODE (inner); + inner_traits |= 1 << ts_code; + to_list + = make_trait_selector (ts_code, OMP_TS_SCORE (inner), + unshare_expr (OMP_TS_PROPERTIES (inner)), + to_list); + } + + for (tree outer = outer_ts; outer; outer = TREE_CHAIN (outer)) + { + omp_ts_code ts_code = OMP_TS_CODE (outer); + bool remove = false; + if (inner_traits & (1 << ts_code)) + for (tree inner = inner_ts; inner; inner = TREE_CHAIN (inner)) + if (OMP_TS_CODE (inner) == ts_code) + { + if (omp_trait_selectors_equivalent (inner, outer, tss)) + remove = true; + break; + } + if (!remove) + to_list + = make_trait_selector (ts_code, OMP_TS_SCORE (outer), + unshare_expr (OMP_TS_PROPERTIES (outer)), + to_list); + } + + return nreverse (to_list); +} + +/* Merge context selector INNER_CTX with OUTER_CTX. LOC and DIRECTIVE are + used for error checking. Returns the merged context, or error_mark_node + if the contexts cannot be merged. */ + +tree +omp_merge_context_selectors (location_t loc, tree outer_ctx, tree inner_ctx, + enum omp_ctx_directive directive) +{ + tree merged_ctx = NULL_TREE; + + if (inner_ctx == error_mark_node || outer_ctx == error_mark_node) + return error_mark_node; + + for (unsigned i = OMP_TRAIT_SET_CONSTRUCT; i != OMP_TRAIT_SET_LAST; i++) + { + omp_tss_code tss_code = static_cast<omp_tss_code>(i); + tree outer_ts = omp_get_context_selector_list (outer_ctx, tss_code); + tree inner_ts = omp_get_context_selector_list (inner_ctx, tss_code); + tree merged_ts = NULL_TREE; + + if (inner_ts && outer_ts) + merged_ts = omp_combine_trait_sets (outer_ts, inner_ts, tss_code); + else if (inner_ts) + merged_ts = omp_copy_trait_set (inner_ts, NULL_TREE); + else if (outer_ts) + merged_ts = omp_copy_trait_set (outer_ts, NULL_TREE); + + if (merged_ts) + merged_ctx = make_trait_set_selector (tss_code, merged_ts, + merged_ctx); + } + + merged_ctx = nreverse (merged_ctx); + return omp_check_context_selector (loc, merged_ctx, directive); +} diff --git a/gcc/omp-general.h b/gcc/omp-general.h index 37e331b2cfd..553230d429e 100644 --- a/gcc/omp-general.h +++ b/gcc/omp-general.h @@ -200,9 +200,14 @@ enum omp_ctx_directive OMP_CTX_METADIRECTIVE }; extern tree omp_check_context_selector (location_t loc, tree ctx, enum omp_ctx_directive directive); +extern tree omp_mangle_variant_name (tree base_id, tree ctx, const char *sep); +extern bool omp_check_for_duplicate_variant (location_t loc, + tree base_decl, tree ctx); extern void omp_mark_declare_variant (location_t loc, tree variant, tree construct); extern int omp_context_selector_matches (tree, tree, bool); +extern tree omp_merge_context_selectors (location_t, tree, tree, + enum omp_ctx_directive); extern tree resolve_omp_target_device_matches (tree node); extern tree omp_get_context_selector (tree, enum omp_tss_code, enum omp_ts_code); |
