diff options
Diffstat (limited to 'gcc/gimplify.cc')
-rw-r--r-- | gcc/gimplify.cc | 497 |
1 files changed, 478 insertions, 19 deletions
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index 827941b..0899b279 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -162,7 +162,8 @@ enum omp_region_type { ORT_WORKSHARE = 0x00, ORT_TASKGROUP = 0x01, - ORT_SIMD = 0x04, + ORT_DISPATCH = 0x02, + ORT_SIMD = 0x04, ORT_PARALLEL = 0x08, ORT_COMBINED_PARALLEL = ORT_PARALLEL | 1, @@ -259,6 +260,7 @@ struct gimplify_omp_ctx bool order_concurrent; bool has_depend; bool in_for_exprs; + bool in_call_args; int defaultmap[5]; }; @@ -4072,23 +4074,137 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) /* Gimplify the function arguments. */ if (nargs > 0) { + tree device_num = NULL_TREE; for (i = (PUSH_ARGS_REVERSED ? nargs - 1 : 0); - PUSH_ARGS_REVERSED ? i >= 0 : i < nargs; - PUSH_ARGS_REVERSED ? i-- : i++) - { - enum gimplify_status t; + PUSH_ARGS_REVERSED ? i >= 0 : i < nargs; + PUSH_ARGS_REVERSED ? i-- : i++) + { + enum gimplify_status t; + + /* Avoid gimplifying the second argument to va_start, which needs to + be the plain PARM_DECL. */ + if ((i != 1) || !builtin_va_start_p) + { + tree *arg_p = &CALL_EXPR_ARG (*expr_p, i); + tree adjust_args_list; + if (flag_openmp && gimplify_omp_ctxp != NULL + && gimplify_omp_ctxp->code == OMP_DISPATCH + && !gimplify_omp_ctxp->in_call_args + && !integer_zerop (*arg_p) + && EXPR_P (CALL_EXPR_FN (*expr_p)) + && DECL_P (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0)) + && (adjust_args_list = lookup_attribute ( + "omp declare variant variant adjust_args", + DECL_ATTRIBUTES ( + TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0)))) + != NULL_TREE) + { + tree arg_types = TYPE_ARG_TYPES ( + TREE_TYPE (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0))); + + if (arg_types != NULL_TREE) + { + for (int param_idx = 0; param_idx < i; param_idx++) + arg_types = TREE_CHAIN (arg_types); + + bool need_device_ptr = false; + for (tree arg + = TREE_PURPOSE (TREE_VALUE (adjust_args_list)); + arg != NULL; arg = TREE_CHAIN (arg)) + { + if (TREE_VALUE (arg) + && TREE_CODE (TREE_VALUE (arg)) == INTEGER_CST + && wi::eq_p (i, wi::to_wide (TREE_VALUE (arg)))) + { + need_device_ptr = true; + break; + } + } + + if (need_device_ptr) + { + bool is_device_ptr = false; + for (tree c = gimplify_omp_ctxp->clauses; c; + c = TREE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) + == OMP_CLAUSE_IS_DEVICE_PTR) + { + tree decl1 = DECL_NAME (OMP_CLAUSE_DECL (c)); + tree decl2 + = tree_strip_nop_conversions (*arg_p); + if (TREE_CODE (decl2) == ADDR_EXPR) + decl2 = TREE_OPERAND (decl2, 0); + if (VAR_P (decl2) + || TREE_CODE (decl2) == PARM_DECL) + { + decl2 = DECL_NAME (decl2); + if (decl1 == decl2) + is_device_ptr = true; + } + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE) + device_num = OMP_CLAUSE_OPERAND (c, 0); + } + + if (!is_device_ptr) + { + if (device_num == NULL_TREE) + { + // device_num = omp_get_default_device () + tree fn = builtin_decl_explicit ( + BUILT_IN_OMP_GET_DEFAULT_DEVICE); + gcall *call = gimple_build_call (fn, 0); + device_num = create_tmp_var ( + gimple_call_return_type (call)); + gimple_call_set_lhs (call, device_num); + gimplify_seq_add_stmt (pre_p, call); + } + + // mapped_arg = omp_get_mapped_ptr (arg, + // device_num) + tree fn = builtin_decl_explicit ( + BUILT_IN_OMP_GET_MAPPED_PTR); + gimplify_arg (arg_p, pre_p, loc); + gimplify_arg (&device_num, pre_p, loc); + call + = gimple_build_call (fn, 2, *arg_p, device_num); + tree mapped_arg = create_tmp_var ( + gimple_call_return_type (call)); + gimple_call_set_lhs (call, mapped_arg); + gimplify_seq_add_stmt (pre_p, call); + + *arg_p = mapped_arg; + + // gimplify_call_expr might be called several + // times on the same call, which would result in + // duplicated calls to omp_get_default_device and + // omp_get_mapped_ptr. To prevent that, we mark + // already mapped arguments as device pointers. + gcc_checking_assert (gimplify_omp_ctxp->code + == OMP_DISPATCH); + tree c + = build_omp_clause (input_location, + OMP_CLAUSE_IS_DEVICE_PTR); + OMP_CLAUSE_DECL (c) = *arg_p; + OMP_CLAUSE_CHAIN (c) = gimplify_omp_ctxp->clauses; + gimplify_omp_ctxp->clauses = c; + } + } + } + } - /* Avoid gimplifying the second argument to va_start, which needs to - be the plain PARM_DECL. */ - if ((i != 1) || !builtin_va_start_p) - { - t = gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, - EXPR_LOCATION (*expr_p), ! returns_twice); + if (gimplify_omp_ctxp && gimplify_omp_ctxp->code == OMP_DISPATCH) + gimplify_omp_ctxp->in_call_args = true; + t = gimplify_arg (arg_p, pre_p, EXPR_LOCATION (*expr_p), + !returns_twice); + if (gimplify_omp_ctxp && gimplify_omp_ctxp->code == OMP_DISPATCH) + gimplify_omp_ctxp->in_call_args = false; - if (t == GS_ERROR) - ret = GS_ERROR; - } - } + if (t == GS_ERROR) + ret = GS_ERROR; + } + } } /* Gimplify the static chain. */ @@ -6388,6 +6504,7 @@ is_gimple_stmt (tree t) case OACC_LOOP: case OMP_SCAN: case OMP_SCOPE: + case OMP_DISPATCH: case OMP_SECTIONS: case OMP_SECTION: case OMP_STRUCTURED_BLOCK: @@ -13208,6 +13325,21 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p, break; } } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE + && code == OMP_DISPATCH) + { + bool saved_into_ssa = gimplify_ctxp->into_ssa; + gimplify_ctxp->into_ssa = false; + if (gimplify_expr (&OMP_CLAUSE_DEVICE_ID (c), pre_p, NULL, + is_gimple_val, fb_rvalue) + == GS_ERROR) + remove = true; + else if (DECL_P (OMP_CLAUSE_DEVICE_ID (c))) + omp_add_variable (ctx, OMP_CLAUSE_DEVICE_ID (c), + GOVD_SHARED | GOVD_SEEN); + gimplify_ctxp->into_ssa = saved_into_ssa; + break; + } /* Fall through. */ case OMP_CLAUSE_PRIORITY: @@ -13448,6 +13580,14 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p, } break; + case OMP_CLAUSE_NOVARIANTS: + OMP_CLAUSE_NOVARIANTS_EXPR (c) + = gimple_boolify (OMP_CLAUSE_NOVARIANTS_EXPR (c)); + break; + case OMP_CLAUSE_NOCONTEXT: + OMP_CLAUSE_NOCONTEXT_EXPR (c) + = gimple_boolify (OMP_CLAUSE_NOCONTEXT_EXPR (c)); + break; case OMP_CLAUSE_NOHOST: default: gcc_unreachable (); @@ -14775,6 +14915,54 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p, delete_omp_context (ctx); } +/* Try to evaluate a novariants clause. Return 1 if true, 0 if false or absent, + * -1 if run-time evaluation is needed. */ + +int +omp_has_novariants (void) +{ + struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + if (ctx != NULL && ctx->code == OMP_DISPATCH && !ctx->in_call_args) + { + tree c = omp_find_clause (ctx->clauses, OMP_CLAUSE_NOVARIANTS); + if (c != NULL_TREE) + { + if (integer_nonzerop (OMP_CLAUSE_NOVARIANTS_EXPR (c))) + return 1; + else if (integer_zerop (OMP_CLAUSE_NOVARIANTS_EXPR (c))) + return 0; + else + return -1; + } + return 0; + } + return 0; +} + +/* Try to evaluate a nocontext clause. Return 1 if true, 0 if false or absent, + * -1 if run-time evaluation is needed. */ + +static int +omp_has_nocontext (void) +{ + struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + if (ctx != NULL && ctx->code == OMP_DISPATCH) + { + tree c = omp_find_clause (ctx->clauses, OMP_CLAUSE_NOCONTEXT); + if (c != NULL_TREE) + { + if (integer_nonzerop (OMP_CLAUSE_NOCONTEXT_EXPR (c))) + return 1; + else if (integer_zerop (OMP_CLAUSE_NOCONTEXT_EXPR (c))) + return 0; + else + return -1; + } + return 0; + } + return 0; +} + /* Return 0 if CONSTRUCTS selectors don't match the OpenMP context, -1 if unknown yet (simd is involved, won't be known until vectorization) and 1 if they do. If SCORES is non-NULL, it should point to an array @@ -14802,9 +14990,9 @@ omp_construct_selector_matches (enum tree_code *constructs, int nconstructs, == ORT_TARGET && ctx->code == OMP_TARGET) || ((ctx->region_type & ORT_TEAMS) && ctx->code == OMP_TEAMS) || (ctx->region_type == ORT_WORKSHARE && ctx->code == OMP_FOR) - || (ctx->region_type == ORT_SIMD - && ctx->code == OMP_SIMD - && !omp_find_clause (ctx->clauses, OMP_CLAUSE_BIND))) + || (ctx->region_type == ORT_SIMD && ctx->code == OMP_SIMD + && !omp_find_clause (ctx->clauses, OMP_CLAUSE_BIND)) + || (ctx->code == OMP_DISPATCH && omp_has_nocontext () != 1)) { ++cnt; if (scores) @@ -17915,6 +18103,272 @@ gimplify_omp_ordered (tree expr, gimple_seq body) return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr)); } +/* Callback for walk_tree to find an IFN_GOMP_DISPATCH. */ + +static tree +find_ifn_gomp_dispatch (tree *tp, int *, void *modify) +{ + tree t = *tp; + + if (TREE_CODE (t) == CALL_EXPR && CALL_EXPR_IFN (t) == IFN_GOMP_DISPATCH) + { + *tp = CALL_EXPR_ARG (t, 0); + return *(tree *) modify ? *(tree *) modify : *tp; + } + + if (TREE_CODE (t) == MODIFY_EXPR) + *(tree *) modify = *tp; + + return NULL_TREE; +} + +/* Gimplify an OMP_DISPATCH construct. */ + +static enum gimplify_status +gimplify_omp_dispatch (tree *expr_p, gimple_seq *pre_p) +{ + tree expr = *expr_p; + gimple_seq body = NULL; + + gimplify_scan_omp_clauses (&OMP_DISPATCH_CLAUSES (expr), pre_p, ORT_DISPATCH, + OMP_DISPATCH); + push_gimplify_context (); + + // If device clause, adjust ICV + tree device + = omp_find_clause (OMP_DISPATCH_CLAUSES (expr), OMP_CLAUSE_DEVICE); + tree saved_device_icv = NULL_TREE; + if (device + && (TREE_CODE (OMP_CLAUSE_DEVICE_ID (device)) != INTEGER_CST + || !wi::eq_p (wi::to_wide (OMP_CLAUSE_DEVICE_ID (device)), + -1 /* omp_initial_device */))) + { + // Save current default-device-var ICV + saved_device_icv = create_tmp_var (integer_type_node); + tree fn = builtin_decl_explicit (BUILT_IN_OMP_GET_DEFAULT_DEVICE); + gcall *call = gimple_build_call (fn, 0); + gimple_call_set_lhs (call, saved_device_icv); + gimplify_seq_add_stmt (&body, call); + + // Set default device + fn = builtin_decl_explicit (BUILT_IN_OMP_SET_DEFAULT_DEVICE); + call = gimple_build_call (fn, 1, OMP_CLAUSE_DEVICE_ID (device)); + gimplify_seq_add_stmt (&body, call); + } + + // If the novariants and nocontext clauses are not compile-time constants, + // we need to generate code for all possible cases: + // if (novariants) // implies nocontext + // base() + // else if (nocontext) + // variant1() + // else + // variant2() + tree *dispatch_body_p = &OMP_DISPATCH_BODY (expr); + if (TREE_CODE (*dispatch_body_p) == BIND_EXPR) + dispatch_body_p = &BIND_EXPR_BODY (*dispatch_body_p); + tree dispatch_body = *dispatch_body_p; + + // Look for IFN_GOMP_DISPATCH and extract the base function call + tree base_call_expr = NULL_TREE; + if (TREE_CODE (dispatch_body) == STATEMENT_LIST) + for (tree_stmt_iterator tsi = tsi_start (dispatch_body); !tsi_end_p (tsi); + tsi_next (&tsi)) + { + tree modify = NULL_TREE; + tree stmt = tsi_stmt (tsi); + base_call_expr + = walk_tree (&stmt, find_ifn_gomp_dispatch, &modify, NULL); + if (base_call_expr != NULL_TREE) + { + tsi_link_before (&tsi, base_call_expr, TSI_CONTINUE_LINKING); + tsi_next (&tsi); + tsi_delink (&tsi); + break; + } + } + else + { + tree modify = NULL_TREE; + base_call_expr + = walk_tree (dispatch_body_p, find_ifn_gomp_dispatch, &modify, NULL); + } + gcc_assert (base_call_expr != NULL_TREE); + + tree dst = NULL_TREE; + if (TREE_CODE (base_call_expr) == MODIFY_EXPR) + { + dst = TREE_OPERAND (base_call_expr, 0); + base_call_expr = TREE_OPERAND (base_call_expr, 1); + } + while (TREE_CODE (base_call_expr) == FLOAT_EXPR + || TREE_CODE (base_call_expr) == CONVERT_EXPR + || TREE_CODE (base_call_expr) == COMPLEX_EXPR + || TREE_CODE (base_call_expr) == INDIRECT_REF + || TREE_CODE (base_call_expr) == NOP_EXPR) + base_call_expr = TREE_OPERAND (base_call_expr, 0); + + tree base_fndecl = get_callee_fndecl (base_call_expr); + if (base_fndecl != NULL_TREE) + { + if (DECL_VIRTUAL_P (base_fndecl)) + { + error_at ( + EXPR_LOCATION (base_call_expr), + "%qD is a virtual function but only a direct call is allowed " + "in a dispatch construct", + DECL_NAME (base_fndecl)); + } + + tree variant_fndecl = omp_resolve_declare_variant (base_fndecl); + if (base_fndecl != variant_fndecl + && (omp_has_novariants () == -1 || omp_has_nocontext () == -1)) + { + tree novariants_clause = NULL_TREE, nocontext_clause = NULL_TREE, + novariants_cond = NULL_TREE, nocontext_cond = NULL_TREE; + for (tree c = OMP_DISPATCH_CLAUSES (expr); c; c = TREE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_NOVARIANTS + && !integer_zerop (OMP_CLAUSE_NOVARIANTS_EXPR (c))) + { + gcc_assert (novariants_cond == NULL_TREE); + novariants_clause = c; + novariants_cond = OMP_CLAUSE_NOVARIANTS_EXPR (c); + } + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_NOCONTEXT + && !integer_zerop (OMP_CLAUSE_NOCONTEXT_EXPR (c))) + { + gcc_assert (nocontext_cond == NULL_TREE); + nocontext_clause = c; + nocontext_cond = OMP_CLAUSE_NOCONTEXT_EXPR (c); + } + } + gcc_assert (novariants_cond != NULL_TREE + || nocontext_cond != NULL_TREE); + + enum gimplify_status ret + = gimplify_expr (&novariants_cond, &body, NULL, is_gimple_val, + fb_rvalue); + if (ret == GS_ERROR || ret == GS_UNHANDLED) + return ret; + ret = gimplify_expr (&nocontext_cond, &body, NULL, is_gimple_val, + fb_rvalue); + if (ret == GS_ERROR || ret == GS_UNHANDLED) + return ret; + + tree end_label = create_artificial_label (UNKNOWN_LOCATION); + + if (novariants_cond != NULL_TREE) + { + tree base_label = create_artificial_label (UNKNOWN_LOCATION); + tree cond_label = create_artificial_label (UNKNOWN_LOCATION); + gcond *novariants_cond_stmt + = gimple_build_cond_from_tree (novariants_cond, base_label, + cond_label); + gimplify_seq_add_stmt (&body, novariants_cond_stmt); + + gimplify_seq_add_stmt (&body, gimple_build_label (base_label)); + tree base_call_expr2 = copy_node (base_call_expr); + if (TREE_CODE (dispatch_body) == MODIFY_EXPR) + { + base_call_expr2 = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, + base_call_expr2); + } + OMP_CLAUSE_NOVARIANTS_EXPR (novariants_clause) + = boolean_true_node; + gimplify_and_add (base_call_expr2, &body); + gimplify_seq_add_stmt (&body, gimple_build_goto (end_label)); + + OMP_CLAUSE_NOVARIANTS_EXPR (novariants_clause) + = boolean_false_node; + gimplify_seq_add_stmt (&body, gimple_build_label (cond_label)); + } + + if (nocontext_cond != NULL_TREE) + { + tree variant1_label = create_artificial_label (UNKNOWN_LOCATION); + tree variant2_label = create_artificial_label (UNKNOWN_LOCATION); + gcond *nocontext_cond_stmt + = gimple_build_cond_from_tree (nocontext_cond, variant1_label, + variant2_label); + gimplify_seq_add_stmt (&body, nocontext_cond_stmt); + + gimplify_seq_add_stmt (&body, + gimple_build_label (variant1_label)); + tree variant_call_expr = copy_node (base_call_expr); + if (TREE_CODE (dispatch_body) == MODIFY_EXPR) + { + variant_call_expr = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, + variant_call_expr); + } + OMP_CLAUSE_NOCONTEXT_EXPR (nocontext_clause) = boolean_true_node; + gimplify_and_add (variant_call_expr, &body); + gimplify_seq_add_stmt (&body, gimple_build_goto (end_label)); + OMP_CLAUSE_NOCONTEXT_EXPR (nocontext_clause) = boolean_false_node; + gimplify_seq_add_stmt (&body, + gimple_build_label (variant2_label)); + } + + tree variant_call_expr = base_call_expr; + if (TREE_CODE (dispatch_body) == MODIFY_EXPR) + { + variant_call_expr + = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, variant_call_expr); + } + gimplify_and_add (variant_call_expr, &body); + gimplify_seq_add_stmt (&body, gimple_build_label (end_label)); + } + else + gimplify_and_add (OMP_DISPATCH_BODY (expr), &body); + } + else + gimplify_and_add (OMP_DISPATCH_BODY (expr), &body); + + // Restore default-device-var ICV + if (saved_device_icv != NULL_TREE) + { + tree fn = builtin_decl_explicit (BUILT_IN_OMP_SET_DEFAULT_DEVICE); + gcall *call = gimple_build_call (fn, 1, saved_device_icv); + gimplify_seq_add_stmt (&body, call); + } + + // Wrap dispatch body into a bind + gimple *bind = gimple_build_bind (NULL_TREE, body, NULL_TREE); + pop_gimplify_context (bind); + + // Manually tear down context created by gimplify_scan_omp_clauses to avoid a + // call to gimplify_adjust_omp_clauses + gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + if (ctx != NULL) + { + gcc_assert (ctx->code == OMP_DISPATCH); + gimplify_omp_ctxp = ctx->outer_context; + delete_omp_context (ctx); + } + + // Remove nowait as it has no effect on dispatch (OpenMP 5.2), device as it + // has been handled above, and depend as the front end handled it by inserting + // taskwait. + tree *dispatch_clauses_ptr = &OMP_DISPATCH_CLAUSES (expr); + for (tree c = *dispatch_clauses_ptr; c; c = *dispatch_clauses_ptr) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_NOWAIT + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE) + { + *dispatch_clauses_ptr = OMP_CLAUSE_CHAIN (c); + break; + } + else + dispatch_clauses_ptr = &OMP_CLAUSE_CHAIN (c); + } + + gimple *stmt = gimple_build_omp_dispatch (bind, OMP_DISPATCH_CLAUSES (expr)); + gimplify_seq_add_stmt (pre_p, stmt); + *expr_p = NULL_TREE; + return GS_ALL_DONE; +} + /* Convert the GENERIC expression tree *EXPR_P to GIMPLE. If the expression produces a value to be used as an operand inside a GIMPLE statement, the value will be stored back in *EXPR_P. This value will @@ -18853,6 +19307,10 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ret = gimplify_omp_atomic (expr_p, pre_p); break; + case OMP_DISPATCH: + ret = gimplify_omp_dispatch (expr_p, pre_p); + break; + case TRANSACTION_EXPR: ret = gimplify_transaction (expr_p, pre_p); break; @@ -19178,7 +19636,8 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, && code != OMP_SECTION && code != OMP_STRUCTURED_BLOCK && code != OMP_SINGLE - && code != OMP_SCOPE); + && code != OMP_SCOPE + && code != OMP_DISPATCH); } #endif |