aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/cp-gimplify.c
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 15:02:44 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:02:44 -0800
commit9a510fb0970d3d9a4201bce8965cabe67850386b (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /gcc/cp/cp-gimplify.c
parenta6d3012b274f38b20e2a57162106f625746af6c6 (diff)
parent8dc2499aa62f768c6395c9754b8cabc1ce25c494 (diff)
downloadgcc-9a510fb0970d3d9a4201bce8965cabe67850386b.zip
gcc-9a510fb0970d3d9a4201bce8965cabe67850386b.tar.gz
gcc-9a510fb0970d3d9a4201bce8965cabe67850386b.tar.bz2
Merge from trunk revision 8dc2499aa62f768c6395c9754b8cabc1ce25c494
Diffstat (limited to 'gcc/cp/cp-gimplify.c')
-rw-r--r--gcc/cp/cp-gimplify.c3105
1 files changed, 0 insertions, 3105 deletions
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
deleted file mode 100644
index c86a5fe..0000000
--- a/gcc/cp/cp-gimplify.c
+++ /dev/null
@@ -1,3105 +0,0 @@
-/* C++-specific tree lowering bits; see also c-gimplify.c and gimple.c.
-
- Copyright (C) 2002-2021 Free Software Foundation, Inc.
- Contributed by Jason Merrill <jason@redhat.com>
-
-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 "target.h"
-#include "basic-block.h"
-#include "cp-tree.h"
-#include "gimple.h"
-#include "predict.h"
-#include "stor-layout.h"
-#include "tree-iterator.h"
-#include "gimplify.h"
-#include "c-family/c-ubsan.h"
-#include "stringpool.h"
-#include "attribs.h"
-#include "asan.h"
-#include "gcc-rich-location.h"
-#include "memmodel.h"
-#include "tm_p.h"
-#include "output.h"
-#include "file-prefix-map.h"
-#include "cgraph.h"
-#include "omp-general.h"
-
-/* Forward declarations. */
-
-static tree cp_genericize_r (tree *, int *, void *);
-static tree cp_fold_r (tree *, int *, void *);
-static void cp_genericize_tree (tree*, bool);
-static tree cp_fold (tree);
-
-/* Genericize a TRY_BLOCK. */
-
-static void
-genericize_try_block (tree *stmt_p)
-{
- tree body = TRY_STMTS (*stmt_p);
- tree cleanup = TRY_HANDLERS (*stmt_p);
-
- *stmt_p = build2 (TRY_CATCH_EXPR, void_type_node, body, cleanup);
-}
-
-/* Genericize a HANDLER by converting to a CATCH_EXPR. */
-
-static void
-genericize_catch_block (tree *stmt_p)
-{
- tree type = HANDLER_TYPE (*stmt_p);
- tree body = HANDLER_BODY (*stmt_p);
-
- /* FIXME should the caught type go in TREE_TYPE? */
- *stmt_p = build2 (CATCH_EXPR, void_type_node, type, body);
-}
-
-/* A terser interface for building a representation of an exception
- specification. */
-
-static tree
-build_gimple_eh_filter_tree (tree body, tree allowed, tree failure)
-{
- tree t;
-
- /* FIXME should the allowed types go in TREE_TYPE? */
- t = build2 (EH_FILTER_EXPR, void_type_node, allowed, NULL_TREE);
- append_to_statement_list (failure, &EH_FILTER_FAILURE (t));
-
- t = build2 (TRY_CATCH_EXPR, void_type_node, NULL_TREE, t);
- append_to_statement_list (body, &TREE_OPERAND (t, 0));
-
- return t;
-}
-
-/* Genericize an EH_SPEC_BLOCK by converting it to a
- TRY_CATCH_EXPR/EH_FILTER_EXPR pair. */
-
-static void
-genericize_eh_spec_block (tree *stmt_p)
-{
- tree body = EH_SPEC_STMTS (*stmt_p);
- tree allowed = EH_SPEC_RAISES (*stmt_p);
- tree failure = build_call_n (call_unexpected_fn, 1, build_exc_ptr ());
-
- *stmt_p = build_gimple_eh_filter_tree (body, allowed, failure);
- suppress_warning (*stmt_p);
- suppress_warning (TREE_OPERAND (*stmt_p, 1));
-}
-
-/* Return the first non-compound statement in STMT. */
-
-tree
-first_stmt (tree stmt)
-{
- switch (TREE_CODE (stmt))
- {
- case STATEMENT_LIST:
- if (tree_statement_list_node *p = STATEMENT_LIST_HEAD (stmt))
- return first_stmt (p->stmt);
- return void_node;
-
- case BIND_EXPR:
- return first_stmt (BIND_EXPR_BODY (stmt));
-
- default:
- return stmt;
- }
-}
-
-/* Genericize an IF_STMT by turning it into a COND_EXPR. */
-
-static void
-genericize_if_stmt (tree *stmt_p)
-{
- tree stmt, cond, then_, else_;
- location_t locus = EXPR_LOCATION (*stmt_p);
-
- stmt = *stmt_p;
- cond = IF_COND (stmt);
- then_ = THEN_CLAUSE (stmt);
- else_ = ELSE_CLAUSE (stmt);
-
- if (then_ && else_)
- {
- tree ft = first_stmt (then_);
- tree fe = first_stmt (else_);
- br_predictor pr;
- if (TREE_CODE (ft) == PREDICT_EXPR
- && TREE_CODE (fe) == PREDICT_EXPR
- && (pr = PREDICT_EXPR_PREDICTOR (ft)) == PREDICT_EXPR_PREDICTOR (fe)
- && (pr == PRED_HOT_LABEL || pr == PRED_COLD_LABEL))
- {
- gcc_rich_location richloc (EXPR_LOC_OR_LOC (ft, locus));
- richloc.add_range (EXPR_LOC_OR_LOC (fe, locus));
- warning_at (&richloc, OPT_Wattributes,
- "both branches of %<if%> statement marked as %qs",
- pr == PRED_HOT_LABEL ? "likely" : "unlikely");
- }
- }
-
- if (!then_)
- then_ = build_empty_stmt (locus);
- if (!else_)
- else_ = build_empty_stmt (locus);
-
- /* consteval if has been verified not to have the then_/else_ blocks
- entered by gotos/case labels from elsewhere, and as then_ block
- can contain unfolded immediate function calls, we have to discard
- the then_ block regardless of whether else_ has side-effects or not. */
- if (IF_STMT_CONSTEVAL_P (stmt))
- stmt = else_;
- else if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
- stmt = then_;
- else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_))
- stmt = else_;
- else
- stmt = build3 (COND_EXPR, void_type_node, cond, then_, else_);
- protected_set_expr_location_if_unset (stmt, locus);
- *stmt_p = stmt;
-}
-
-/* Hook into the middle of gimplifying an OMP_FOR node. */
-
-static enum gimplify_status
-cp_gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
-{
- tree for_stmt = *expr_p;
- gimple_seq seq = NULL;
-
- /* Protect ourselves from recursion. */
- if (OMP_FOR_GIMPLIFYING_P (for_stmt))
- return GS_UNHANDLED;
- OMP_FOR_GIMPLIFYING_P (for_stmt) = 1;
-
- gimplify_and_add (for_stmt, &seq);
- gimple_seq_add_seq (pre_p, seq);
-
- OMP_FOR_GIMPLIFYING_P (for_stmt) = 0;
-
- return GS_ALL_DONE;
-}
-
-/* Gimplify an EXPR_STMT node. */
-
-static void
-gimplify_expr_stmt (tree *stmt_p)
-{
- tree stmt = EXPR_STMT_EXPR (*stmt_p);
-
- if (stmt == error_mark_node)
- stmt = NULL;
-
- /* Gimplification of a statement expression will nullify the
- statement if all its side effects are moved to *PRE_P and *POST_P.
-
- In this case we will not want to emit the gimplified statement.
- However, we may still want to emit a warning, so we do that before
- gimplification. */
- if (stmt && warn_unused_value)
- {
- if (!TREE_SIDE_EFFECTS (stmt))
- {
- if (!IS_EMPTY_STMT (stmt)
- && !VOID_TYPE_P (TREE_TYPE (stmt))
- && !warning_suppressed_p (stmt, OPT_Wunused_value))
- warning (OPT_Wunused_value, "statement with no effect");
- }
- else
- warn_if_unused_value (stmt, input_location);
- }
-
- if (stmt == NULL_TREE)
- stmt = alloc_stmt_list ();
-
- *stmt_p = stmt;
-}
-
-/* Gimplify initialization from an AGGR_INIT_EXPR. */
-
-static void
-cp_gimplify_init_expr (tree *expr_p)
-{
- tree from = TREE_OPERAND (*expr_p, 1);
- tree to = TREE_OPERAND (*expr_p, 0);
- tree t;
-
- /* What about code that pulls out the temp and uses it elsewhere? I
- think that such code never uses the TARGET_EXPR as an initializer. If
- I'm wrong, we'll abort because the temp won't have any RTL. In that
- case, I guess we'll need to replace references somehow. */
- if (TREE_CODE (from) == TARGET_EXPR && TARGET_EXPR_INITIAL (from))
- from = TARGET_EXPR_INITIAL (from);
-
- /* Look through any COMPOUND_EXPRs, since build_compound_expr pushes them
- inside the TARGET_EXPR. */
- for (t = from; t; )
- {
- tree sub = TREE_CODE (t) == COMPOUND_EXPR ? TREE_OPERAND (t, 0) : t;
-
- /* If we are initializing from an AGGR_INIT_EXPR, drop the INIT_EXPR and
- replace the slot operand with our target.
-
- Should we add a target parm to gimplify_expr instead? No, as in this
- case we want to replace the INIT_EXPR. */
- if (TREE_CODE (sub) == AGGR_INIT_EXPR
- || TREE_CODE (sub) == VEC_INIT_EXPR)
- {
- if (TREE_CODE (sub) == AGGR_INIT_EXPR)
- AGGR_INIT_EXPR_SLOT (sub) = to;
- else
- VEC_INIT_EXPR_SLOT (sub) = to;
- *expr_p = from;
-
- /* The initialization is now a side-effect, so the container can
- become void. */
- if (from != sub)
- TREE_TYPE (from) = void_type_node;
- }
-
- /* Handle aggregate NSDMI. */
- replace_placeholders (sub, to);
-
- if (t == sub)
- break;
- else
- t = TREE_OPERAND (t, 1);
- }
-
-}
-
-/* Gimplify a MUST_NOT_THROW_EXPR. */
-
-static enum gimplify_status
-gimplify_must_not_throw_expr (tree *expr_p, gimple_seq *pre_p)
-{
- tree stmt = *expr_p;
- tree temp = voidify_wrapper_expr (stmt, NULL);
- tree body = TREE_OPERAND (stmt, 0);
- gimple_seq try_ = NULL;
- gimple_seq catch_ = NULL;
- gimple *mnt;
-
- gimplify_and_add (body, &try_);
- mnt = gimple_build_eh_must_not_throw (terminate_fn);
- gimple_seq_add_stmt_without_update (&catch_, mnt);
- mnt = gimple_build_try (try_, catch_, GIMPLE_TRY_CATCH);
-
- gimple_seq_add_stmt_without_update (pre_p, mnt);
- if (temp)
- {
- *expr_p = temp;
- return GS_OK;
- }
-
- *expr_p = NULL;
- return GS_ALL_DONE;
-}
-
-/* Return TRUE if an operand (OP) of a given TYPE being copied is
- really just an empty class copy.
-
- Check that the operand has a simple form so that TARGET_EXPRs and
- non-empty CONSTRUCTORs get reduced properly, and we leave the
- return slot optimization alone because it isn't a copy. */
-
-bool
-simple_empty_class_p (tree type, tree op, tree_code code)
-{
- if (TREE_CODE (op) == COMPOUND_EXPR)
- return simple_empty_class_p (type, TREE_OPERAND (op, 1), code);
- if (SIMPLE_TARGET_EXPR_P (op)
- && TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
- /* The TARGET_EXPR is itself a simple copy, look through it. */
- return simple_empty_class_p (type, TARGET_EXPR_INITIAL (op), code);
-
- if (TREE_CODE (op) == PARM_DECL
- && TREE_ADDRESSABLE (TREE_TYPE (op)))
- {
- tree fn = DECL_CONTEXT (op);
- if (DECL_THUNK_P (fn)
- || lambda_static_thunk_p (fn))
- /* In a thunk, we pass through invisible reference parms, so this isn't
- actually a copy. */
- return false;
- }
-
- return
- (TREE_CODE (op) == EMPTY_CLASS_EXPR
- || code == MODIFY_EXPR
- || is_gimple_lvalue (op)
- || INDIRECT_REF_P (op)
- || (TREE_CODE (op) == CONSTRUCTOR
- && CONSTRUCTOR_NELTS (op) == 0)
- || (TREE_CODE (op) == CALL_EXPR
- && !CALL_EXPR_RETURN_SLOT_OPT (op)))
- && !TREE_CLOBBER_P (op)
- && is_really_empty_class (type, /*ignore_vptr*/true);
-}
-
-/* Returns true if evaluating E as an lvalue has side-effects;
- specifically, a volatile lvalue has TREE_SIDE_EFFECTS, but it doesn't really
- have side-effects until there is a read or write through it. */
-
-static bool
-lvalue_has_side_effects (tree e)
-{
- if (!TREE_SIDE_EFFECTS (e))
- return false;
- while (handled_component_p (e))
- {
- if (TREE_CODE (e) == ARRAY_REF
- && TREE_SIDE_EFFECTS (TREE_OPERAND (e, 1)))
- return true;
- e = TREE_OPERAND (e, 0);
- }
- if (DECL_P (e))
- /* Just naming a variable has no side-effects. */
- return false;
- else if (INDIRECT_REF_P (e))
- /* Similarly, indirection has no side-effects. */
- return TREE_SIDE_EFFECTS (TREE_OPERAND (e, 0));
- else
- /* For anything else, trust TREE_SIDE_EFFECTS. */
- return TREE_SIDE_EFFECTS (e);
-}
-
-/* Gimplify *EXPR_P as rvalue into an expression that can't be modified
- by expressions with side-effects in other operands. */
-
-static enum gimplify_status
-gimplify_to_rvalue (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
- bool (*gimple_test_f) (tree))
-{
- enum gimplify_status t
- = gimplify_expr (expr_p, pre_p, post_p, gimple_test_f, fb_rvalue);
- if (t == GS_ERROR)
- return GS_ERROR;
- else if (is_gimple_variable (*expr_p) && TREE_CODE (*expr_p) != SSA_NAME)
- *expr_p = get_initialized_tmp_var (*expr_p, pre_p);
- return t;
-}
-
-/* Do C++-specific gimplification. Args are as for gimplify_expr. */
-
-int
-cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
-{
- int saved_stmts_are_full_exprs_p = 0;
- location_t loc = cp_expr_loc_or_input_loc (*expr_p);
- enum tree_code code = TREE_CODE (*expr_p);
- enum gimplify_status ret;
-
- if (STATEMENT_CODE_P (code))
- {
- saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
- current_stmt_tree ()->stmts_are_full_exprs_p
- = STMT_IS_FULL_EXPR_P (*expr_p);
- }
-
- switch (code)
- {
- case AGGR_INIT_EXPR:
- simplify_aggr_init_expr (expr_p);
- ret = GS_OK;
- break;
-
- case VEC_INIT_EXPR:
- {
- location_t loc = input_location;
- tree init = VEC_INIT_EXPR_INIT (*expr_p);
- int from_array = (init && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
- gcc_assert (EXPR_HAS_LOCATION (*expr_p));
- input_location = EXPR_LOCATION (*expr_p);
- *expr_p = build_vec_init (VEC_INIT_EXPR_SLOT (*expr_p), NULL_TREE,
- init, VEC_INIT_EXPR_VALUE_INIT (*expr_p),
- from_array,
- tf_warning_or_error);
- hash_set<tree> pset;
- cp_walk_tree (expr_p, cp_fold_r, &pset, NULL);
- cp_genericize_tree (expr_p, false);
- copy_if_shared (expr_p);
- ret = GS_OK;
- input_location = loc;
- }
- break;
-
- case THROW_EXPR:
- /* FIXME communicate throw type to back end, probably by moving
- THROW_EXPR into ../tree.def. */
- *expr_p = TREE_OPERAND (*expr_p, 0);
- ret = GS_OK;
- break;
-
- case MUST_NOT_THROW_EXPR:
- ret = gimplify_must_not_throw_expr (expr_p, pre_p);
- break;
-
- /* We used to do this for MODIFY_EXPR as well, but that's unsafe; the
- LHS of an assignment might also be involved in the RHS, as in bug
- 25979. */
- case INIT_EXPR:
- cp_gimplify_init_expr (expr_p);
- if (TREE_CODE (*expr_p) != INIT_EXPR)
- return GS_OK;
- /* Fall through. */
- case MODIFY_EXPR:
- modify_expr_case:
- {
- /* If the back end isn't clever enough to know that the lhs and rhs
- types are the same, add an explicit conversion. */
- tree op0 = TREE_OPERAND (*expr_p, 0);
- tree op1 = TREE_OPERAND (*expr_p, 1);
-
- if (!error_operand_p (op0)
- && !error_operand_p (op1)
- && (TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (op0))
- || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (op1)))
- && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0)))
- TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR,
- TREE_TYPE (op0), op1);
-
- else if (simple_empty_class_p (TREE_TYPE (op0), op1, code))
- {
- while (TREE_CODE (op1) == TARGET_EXPR)
- /* We're disconnecting the initializer from its target,
- don't create a temporary. */
- op1 = TARGET_EXPR_INITIAL (op1);
-
- /* Remove any copies of empty classes. Also drop volatile
- variables on the RHS to avoid infinite recursion from
- gimplify_expr trying to load the value. */
- if (TREE_SIDE_EFFECTS (op1))
- {
- if (TREE_THIS_VOLATILE (op1)
- && (REFERENCE_CLASS_P (op1) || DECL_P (op1)))
- op1 = build_fold_addr_expr (op1);
-
- gimplify_and_add (op1, pre_p);
- }
- gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
- is_gimple_lvalue, fb_lvalue);
- *expr_p = TREE_OPERAND (*expr_p, 0);
- if (code == RETURN_EXPR && REFERENCE_CLASS_P (*expr_p))
- /* Avoid 'return *<retval>;' */
- *expr_p = TREE_OPERAND (*expr_p, 0);
- }
- /* P0145 says that the RHS is sequenced before the LHS.
- gimplify_modify_expr gimplifies the RHS before the LHS, but that
- isn't quite strong enough in two cases:
-
- 1) gimplify.c wants to leave a CALL_EXPR on the RHS, which would
- mean it's evaluated after the LHS.
-
- 2) the value calculation of the RHS is also sequenced before the
- LHS, so for scalar assignment we need to preevaluate if the
- RHS could be affected by LHS side-effects even if it has no
- side-effects of its own. We don't need this for classes because
- class assignment takes its RHS by reference. */
- else if (flag_strong_eval_order > 1
- && TREE_CODE (*expr_p) == MODIFY_EXPR
- && lvalue_has_side_effects (op0)
- && (TREE_CODE (op1) == CALL_EXPR
- || (SCALAR_TYPE_P (TREE_TYPE (op1))
- && !TREE_CONSTANT (op1))))
- TREE_OPERAND (*expr_p, 1) = get_initialized_tmp_var (op1, pre_p);
- }
- ret = GS_OK;
- break;
-
- case EMPTY_CLASS_EXPR:
- /* We create an empty CONSTRUCTOR with RECORD_TYPE. */
- *expr_p = build_constructor (TREE_TYPE (*expr_p), NULL);
- ret = GS_OK;
- break;
-
- case BASELINK:
- *expr_p = BASELINK_FUNCTIONS (*expr_p);
- ret = GS_OK;
- break;
-
- case TRY_BLOCK:
- genericize_try_block (expr_p);
- ret = GS_OK;
- break;
-
- case HANDLER:
- genericize_catch_block (expr_p);
- ret = GS_OK;
- break;
-
- case EH_SPEC_BLOCK:
- genericize_eh_spec_block (expr_p);
- ret = GS_OK;
- break;
-
- case USING_STMT:
- gcc_unreachable ();
-
- case FOR_STMT:
- case WHILE_STMT:
- case DO_STMT:
- case SWITCH_STMT:
- case CONTINUE_STMT:
- case BREAK_STMT:
- gcc_unreachable ();
-
- case OMP_FOR:
- case OMP_SIMD:
- case OMP_DISTRIBUTE:
- case OMP_LOOP:
- case OMP_TASKLOOP:
- ret = cp_gimplify_omp_for (expr_p, pre_p);
- break;
-
- case EXPR_STMT:
- gimplify_expr_stmt (expr_p);
- ret = GS_OK;
- break;
-
- case UNARY_PLUS_EXPR:
- {
- tree arg = TREE_OPERAND (*expr_p, 0);
- tree type = TREE_TYPE (*expr_p);
- *expr_p = (TREE_TYPE (arg) != type) ? fold_convert (type, arg)
- : arg;
- ret = GS_OK;
- }
- break;
-
- case CALL_EXPR:
- ret = GS_OK;
- if (flag_strong_eval_order == 2
- && CALL_EXPR_FN (*expr_p)
- && !CALL_EXPR_OPERATOR_SYNTAX (*expr_p)
- && cp_get_callee_fndecl_nofold (*expr_p) == NULL_TREE)
- {
- tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
- enum gimplify_status t
- = gimplify_to_rvalue (&CALL_EXPR_FN (*expr_p), pre_p, NULL,
- is_gimple_call_addr);
- if (t == GS_ERROR)
- ret = GS_ERROR;
- /* GIMPLE considers most pointer conversion useless, but for
- calls we actually care about the exact function pointer type. */
- else if (TREE_TYPE (CALL_EXPR_FN (*expr_p)) != fnptrtype)
- CALL_EXPR_FN (*expr_p)
- = build1 (NOP_EXPR, fnptrtype, CALL_EXPR_FN (*expr_p));
- }
- if (!CALL_EXPR_FN (*expr_p))
- /* Internal function call. */;
- else if (CALL_EXPR_REVERSE_ARGS (*expr_p))
- {
- /* This is a call to a (compound) assignment operator that used
- the operator syntax; gimplify the RHS first. */
- gcc_assert (call_expr_nargs (*expr_p) == 2);
- gcc_assert (!CALL_EXPR_ORDERED_ARGS (*expr_p));
- enum gimplify_status t
- = gimplify_arg (&CALL_EXPR_ARG (*expr_p, 1), pre_p, loc);
- if (t == GS_ERROR)
- ret = GS_ERROR;
- }
- else if (CALL_EXPR_ORDERED_ARGS (*expr_p))
- {
- /* Leave the last argument for gimplify_call_expr, to avoid problems
- with __builtin_va_arg_pack(). */
- int nargs = call_expr_nargs (*expr_p) - 1;
- for (int i = 0; i < nargs; ++i)
- {
- enum gimplify_status t
- = gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, loc);
- if (t == GS_ERROR)
- ret = GS_ERROR;
- }
- }
- else if (flag_strong_eval_order
- && !CALL_EXPR_OPERATOR_SYNTAX (*expr_p))
- {
- /* If flag_strong_eval_order, evaluate the object argument first. */
- tree fntype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
- if (INDIRECT_TYPE_P (fntype))
- fntype = TREE_TYPE (fntype);
- if (TREE_CODE (fntype) == METHOD_TYPE)
- {
- enum gimplify_status t
- = gimplify_arg (&CALL_EXPR_ARG (*expr_p, 0), pre_p, loc);
- if (t == GS_ERROR)
- ret = GS_ERROR;
- }
- }
- if (ret != GS_ERROR)
- {
- tree decl = cp_get_callee_fndecl_nofold (*expr_p);
- if (decl && fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
- switch (DECL_FE_FUNCTION_CODE (decl))
- {
- case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
- *expr_p = boolean_false_node;
- break;
- case CP_BUILT_IN_SOURCE_LOCATION:
- *expr_p
- = fold_builtin_source_location (EXPR_LOCATION (*expr_p));
- break;
- case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
- *expr_p
- = fold_builtin_is_corresponding_member
- (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
- &CALL_EXPR_ARG (*expr_p, 0));
- break;
- case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
- *expr_p
- = fold_builtin_is_pointer_inverconvertible_with_class
- (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
- &CALL_EXPR_ARG (*expr_p, 0));
- break;
- default:
- break;
- }
- }
- break;
-
- case TARGET_EXPR:
- /* A TARGET_EXPR that expresses direct-initialization should have been
- elided by cp_gimplify_init_expr. */
- gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (*expr_p));
- ret = GS_UNHANDLED;
- break;
-
- case PTRMEM_CST:
- *expr_p = cplus_expand_constant (*expr_p);
- if (TREE_CODE (*expr_p) == PTRMEM_CST)
- ret = GS_ERROR;
- else
- ret = GS_OK;
- break;
-
- case RETURN_EXPR:
- if (TREE_OPERAND (*expr_p, 0)
- && (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == INIT_EXPR
- || TREE_CODE (TREE_OPERAND (*expr_p, 0)) == MODIFY_EXPR))
- {
- expr_p = &TREE_OPERAND (*expr_p, 0);
- /* Avoid going through the INIT_EXPR case, which can
- degrade INIT_EXPRs into AGGR_INIT_EXPRs. */
- goto modify_expr_case;
- }
- /* Fall through. */
-
- default:
- ret = (enum gimplify_status) c_gimplify_expr (expr_p, pre_p, post_p);
- break;
- }
-
- /* Restore saved state. */
- if (STATEMENT_CODE_P (code))
- current_stmt_tree ()->stmts_are_full_exprs_p
- = saved_stmts_are_full_exprs_p;
-
- return ret;
-}
-
-static inline bool
-is_invisiref_parm (const_tree t)
-{
- return ((TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL)
- && DECL_BY_REFERENCE (t));
-}
-
-/* A stable comparison routine for use with splay trees and DECLs. */
-
-static int
-splay_tree_compare_decl_uid (splay_tree_key xa, splay_tree_key xb)
-{
- tree a = (tree) xa;
- tree b = (tree) xb;
-
- return DECL_UID (a) - DECL_UID (b);
-}
-
-/* OpenMP context during genericization. */
-
-struct cp_genericize_omp_taskreg
-{
- bool is_parallel;
- bool default_shared;
- struct cp_genericize_omp_taskreg *outer;
- splay_tree variables;
-};
-
-/* Return true if genericization should try to determine if
- DECL is firstprivate or shared within task regions. */
-
-static bool
-omp_var_to_track (tree decl)
-{
- tree type = TREE_TYPE (decl);
- if (is_invisiref_parm (decl))
- type = TREE_TYPE (type);
- else if (TYPE_REF_P (type))
- type = TREE_TYPE (type);
- while (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
- if (type == error_mark_node || !CLASS_TYPE_P (type))
- return false;
- if (VAR_P (decl) && CP_DECL_THREAD_LOCAL_P (decl))
- return false;
- if (cxx_omp_predetermined_sharing (decl) != OMP_CLAUSE_DEFAULT_UNSPECIFIED)
- return false;
- return true;
-}
-
-/* Note DECL use in OpenMP region OMP_CTX during genericization. */
-
-static void
-omp_cxx_notice_variable (struct cp_genericize_omp_taskreg *omp_ctx, tree decl)
-{
- splay_tree_node n = splay_tree_lookup (omp_ctx->variables,
- (splay_tree_key) decl);
- if (n == NULL)
- {
- int flags = OMP_CLAUSE_DEFAULT_SHARED;
- if (omp_ctx->outer)
- omp_cxx_notice_variable (omp_ctx->outer, decl);
- if (!omp_ctx->default_shared)
- {
- struct cp_genericize_omp_taskreg *octx;
-
- for (octx = omp_ctx->outer; octx; octx = octx->outer)
- {
- n = splay_tree_lookup (octx->variables, (splay_tree_key) decl);
- if (n && n->value != OMP_CLAUSE_DEFAULT_SHARED)
- {
- flags = OMP_CLAUSE_DEFAULT_FIRSTPRIVATE;
- break;
- }
- if (octx->is_parallel)
- break;
- }
- if (octx == NULL
- && (TREE_CODE (decl) == PARM_DECL
- || (!(TREE_STATIC (decl) || DECL_EXTERNAL (decl))
- && DECL_CONTEXT (decl) == current_function_decl)))
- flags = OMP_CLAUSE_DEFAULT_FIRSTPRIVATE;
- if (flags == OMP_CLAUSE_DEFAULT_FIRSTPRIVATE)
- {
- /* DECL is implicitly determined firstprivate in
- the current task construct. Ensure copy ctor and
- dtor are instantiated, because during gimplification
- it will be already too late. */
- tree type = TREE_TYPE (decl);
- if (is_invisiref_parm (decl))
- type = TREE_TYPE (type);
- else if (TYPE_REF_P (type))
- type = TREE_TYPE (type);
- while (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
- get_copy_ctor (type, tf_none);
- get_dtor (type, tf_none);
- }
- }
- splay_tree_insert (omp_ctx->variables, (splay_tree_key) decl, flags);
- }
-}
-
-/* Genericization context. */
-
-struct cp_genericize_data
-{
- hash_set<tree> *p_set;
- auto_vec<tree> bind_expr_stack;
- struct cp_genericize_omp_taskreg *omp_ctx;
- tree try_block;
- bool no_sanitize_p;
- bool handle_invisiref_parm_p;
-};
-
-/* Perform any pre-gimplification folding of C++ front end trees to
- GENERIC.
- Note: The folding of none-omp cases is something to move into
- the middle-end. As for now we have most foldings only on GENERIC
- in fold-const, we need to perform this before transformation to
- GIMPLE-form. */
-
-static tree
-cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data)
-{
- tree stmt;
- enum tree_code code;
-
- *stmt_p = stmt = cp_fold (*stmt_p);
-
- if (((hash_set<tree> *) data)->add (stmt))
- {
- /* Don't walk subtrees of stmts we've already walked once, otherwise
- we can have exponential complexity with e.g. lots of nested
- SAVE_EXPRs or TARGET_EXPRs. cp_fold uses a cache and will return
- always the same tree, which the first time cp_fold_r has been
- called on it had the subtrees walked. */
- *walk_subtrees = 0;
- return NULL;
- }
-
- code = TREE_CODE (stmt);
- if (code == OMP_FOR || code == OMP_SIMD || code == OMP_DISTRIBUTE
- || code == OMP_LOOP || code == OMP_TASKLOOP || code == OACC_LOOP)
- {
- tree x;
- int i, n;
-
- cp_walk_tree (&OMP_FOR_BODY (stmt), cp_fold_r, data, NULL);
- cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_fold_r, data, NULL);
- cp_walk_tree (&OMP_FOR_INIT (stmt), cp_fold_r, data, NULL);
- x = OMP_FOR_COND (stmt);
- if (x && TREE_CODE_CLASS (TREE_CODE (x)) == tcc_comparison)
- {
- cp_walk_tree (&TREE_OPERAND (x, 0), cp_fold_r, data, NULL);
- cp_walk_tree (&TREE_OPERAND (x, 1), cp_fold_r, data, NULL);
- }
- else if (x && TREE_CODE (x) == TREE_VEC)
- {
- n = TREE_VEC_LENGTH (x);
- for (i = 0; i < n; i++)
- {
- tree o = TREE_VEC_ELT (x, i);
- if (o && TREE_CODE_CLASS (TREE_CODE (o)) == tcc_comparison)
- cp_walk_tree (&TREE_OPERAND (o, 1), cp_fold_r, data, NULL);
- }
- }
- x = OMP_FOR_INCR (stmt);
- if (x && TREE_CODE (x) == TREE_VEC)
- {
- n = TREE_VEC_LENGTH (x);
- for (i = 0; i < n; i++)
- {
- tree o = TREE_VEC_ELT (x, i);
- if (o && TREE_CODE (o) == MODIFY_EXPR)
- o = TREE_OPERAND (o, 1);
- if (o && (TREE_CODE (o) == PLUS_EXPR || TREE_CODE (o) == MINUS_EXPR
- || TREE_CODE (o) == POINTER_PLUS_EXPR))
- {
- cp_walk_tree (&TREE_OPERAND (o, 0), cp_fold_r, data, NULL);
- cp_walk_tree (&TREE_OPERAND (o, 1), cp_fold_r, data, NULL);
- }
- }
- }
- cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL);
- *walk_subtrees = 0;
- }
-
- return NULL;
-}
-
-/* Fold ALL the trees! FIXME we should be able to remove this, but
- apparently that still causes optimization regressions. */
-
-void
-cp_fold_function (tree fndecl)
-{
- hash_set<tree> pset;
- cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &pset, NULL);
-}
-
-/* Turn SPACESHIP_EXPR EXPR into GENERIC. */
-
-static tree genericize_spaceship (tree expr)
-{
- iloc_sentinel s (cp_expr_location (expr));
- tree type = TREE_TYPE (expr);
- tree op0 = TREE_OPERAND (expr, 0);
- tree op1 = TREE_OPERAND (expr, 1);
- return genericize_spaceship (input_location, type, op0, op1);
-}
-
-/* If EXPR involves an anonymous VLA type, prepend a DECL_EXPR for that type
- to trigger gimplify_type_sizes; otherwise a cast to pointer-to-VLA confuses
- the middle-end (c++/88256). If EXPR is a DECL, use add_stmt and return
- NULL_TREE; otherwise return a COMPOUND_STMT of the DECL_EXPR and EXPR. */
-
-tree
-predeclare_vla (tree expr)
-{
- tree type = TREE_TYPE (expr);
- if (type == error_mark_node)
- return expr;
- if (is_typedef_decl (expr))
- type = DECL_ORIGINAL_TYPE (expr);
-
- /* We need to strip pointers for gimplify_type_sizes. */
- tree vla = type;
- while (POINTER_TYPE_P (vla))
- {
- if (TYPE_NAME (vla))
- return expr;
- vla = TREE_TYPE (vla);
- }
- if (vla == type || TYPE_NAME (vla)
- || !variably_modified_type_p (vla, NULL_TREE))
- return expr;
-
- tree decl = build_decl (input_location, TYPE_DECL, NULL_TREE, vla);
- DECL_ARTIFICIAL (decl) = 1;
- TYPE_NAME (vla) = decl;
- tree dexp = build_stmt (input_location, DECL_EXPR, decl);
- if (DECL_P (expr))
- {
- add_stmt (dexp);
- return NULL_TREE;
- }
- else
- {
- expr = build2 (COMPOUND_EXPR, type, dexp, expr);
- return expr;
- }
-}
-
-/* Perform any pre-gimplification lowering of C++ front end trees to
- GENERIC. */
-
-static tree
-cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
-{
- tree stmt = *stmt_p;
- struct cp_genericize_data *wtd = (struct cp_genericize_data *) data;
- hash_set<tree> *p_set = wtd->p_set;
-
- /* If in an OpenMP context, note var uses. */
- if (__builtin_expect (wtd->omp_ctx != NULL, 0)
- && (VAR_P (stmt)
- || TREE_CODE (stmt) == PARM_DECL
- || TREE_CODE (stmt) == RESULT_DECL)
- && omp_var_to_track (stmt))
- omp_cxx_notice_variable (wtd->omp_ctx, stmt);
-
- /* Don't dereference parms in a thunk, pass the references through. */
- if ((TREE_CODE (stmt) == CALL_EXPR && call_from_lambda_thunk_p (stmt))
- || (TREE_CODE (stmt) == AGGR_INIT_EXPR && AGGR_INIT_FROM_THUNK_P (stmt)))
- {
- *walk_subtrees = 0;
- return NULL;
- }
-
- /* Dereference invisible reference parms. */
- if (wtd->handle_invisiref_parm_p && is_invisiref_parm (stmt))
- {
- *stmt_p = convert_from_reference (stmt);
- p_set->add (*stmt_p);
- *walk_subtrees = 0;
- return NULL;
- }
-
- /* Map block scope extern declarations to visible declarations with the
- same name and type in outer scopes if any. */
- if (VAR_OR_FUNCTION_DECL_P (stmt) && DECL_LOCAL_DECL_P (stmt))
- if (tree alias = DECL_LOCAL_DECL_ALIAS (stmt))
- {
- if (alias != error_mark_node)
- {
- *stmt_p = alias;
- TREE_USED (alias) |= TREE_USED (stmt);
- }
- *walk_subtrees = 0;
- return NULL;
- }
-
- if (TREE_CODE (stmt) == INTEGER_CST
- && TYPE_REF_P (TREE_TYPE (stmt))
- && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
- && !wtd->no_sanitize_p)
- {
- ubsan_maybe_instrument_reference (stmt_p);
- if (*stmt_p != stmt)
- {
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
-
- /* Other than invisiref parms, don't walk the same tree twice. */
- if (p_set->contains (stmt))
- {
- *walk_subtrees = 0;
- return NULL_TREE;
- }
-
- switch (TREE_CODE (stmt))
- {
- case ADDR_EXPR:
- if (is_invisiref_parm (TREE_OPERAND (stmt, 0)))
- {
- /* If in an OpenMP context, note var uses. */
- if (__builtin_expect (wtd->omp_ctx != NULL, 0)
- && omp_var_to_track (TREE_OPERAND (stmt, 0)))
- omp_cxx_notice_variable (wtd->omp_ctx, TREE_OPERAND (stmt, 0));
- *stmt_p = fold_convert (TREE_TYPE (stmt), TREE_OPERAND (stmt, 0));
- *walk_subtrees = 0;
- }
- break;
-
- case RETURN_EXPR:
- if (TREE_OPERAND (stmt, 0) && is_invisiref_parm (TREE_OPERAND (stmt, 0)))
- /* Don't dereference an invisiref RESULT_DECL inside a RETURN_EXPR. */
- *walk_subtrees = 0;
- break;
-
- case OMP_CLAUSE:
- switch (OMP_CLAUSE_CODE (stmt))
- {
- case OMP_CLAUSE_LASTPRIVATE:
- /* Don't dereference an invisiref in OpenMP clauses. */
- if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt)))
- {
- *walk_subtrees = 0;
- if (OMP_CLAUSE_LASTPRIVATE_STMT (stmt))
- cp_walk_tree (&OMP_CLAUSE_LASTPRIVATE_STMT (stmt),
- cp_genericize_r, data, NULL);
- }
- break;
- case OMP_CLAUSE_PRIVATE:
- /* Don't dereference an invisiref in OpenMP clauses. */
- if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt)))
- *walk_subtrees = 0;
- else if (wtd->omp_ctx != NULL)
- {
- /* Private clause doesn't cause any references to the
- var in outer contexts, avoid calling
- omp_cxx_notice_variable for it. */
- struct cp_genericize_omp_taskreg *old = wtd->omp_ctx;
- wtd->omp_ctx = NULL;
- cp_walk_tree (&OMP_CLAUSE_DECL (stmt), cp_genericize_r,
- data, NULL);
- wtd->omp_ctx = old;
- *walk_subtrees = 0;
- }
- break;
- case OMP_CLAUSE_SHARED:
- case OMP_CLAUSE_FIRSTPRIVATE:
- case OMP_CLAUSE_COPYIN:
- case OMP_CLAUSE_COPYPRIVATE:
- case OMP_CLAUSE_INCLUSIVE:
- case OMP_CLAUSE_EXCLUSIVE:
- /* Don't dereference an invisiref in OpenMP clauses. */
- if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt)))
- *walk_subtrees = 0;
- break;
- case OMP_CLAUSE_REDUCTION:
- case OMP_CLAUSE_IN_REDUCTION:
- case OMP_CLAUSE_TASK_REDUCTION:
- /* Don't dereference an invisiref in reduction clause's
- OMP_CLAUSE_DECL either. OMP_CLAUSE_REDUCTION_{INIT,MERGE}
- still needs to be genericized. */
- if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt)))
- {
- *walk_subtrees = 0;
- if (OMP_CLAUSE_REDUCTION_INIT (stmt))
- cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (stmt),
- cp_genericize_r, data, NULL);
- if (OMP_CLAUSE_REDUCTION_MERGE (stmt))
- cp_walk_tree (&OMP_CLAUSE_REDUCTION_MERGE (stmt),
- cp_genericize_r, data, NULL);
- }
- break;
- default:
- break;
- }
- break;
-
- /* Due to the way voidify_wrapper_expr is written, we don't get a chance
- to lower this construct before scanning it, so we need to lower these
- before doing anything else. */
- case CLEANUP_STMT:
- *stmt_p = build2_loc (EXPR_LOCATION (stmt),
- CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR
- : TRY_FINALLY_EXPR,
- void_type_node,
- CLEANUP_BODY (stmt),
- CLEANUP_EXPR (stmt));
- break;
-
- case IF_STMT:
- genericize_if_stmt (stmt_p);
- /* *stmt_p has changed, tail recurse to handle it again. */
- return cp_genericize_r (stmt_p, walk_subtrees, data);
-
- /* COND_EXPR might have incompatible types in branches if one or both
- arms are bitfields. Fix it up now. */
- case COND_EXPR:
- {
- tree type_left
- = (TREE_OPERAND (stmt, 1)
- ? is_bitfield_expr_with_lowered_type (TREE_OPERAND (stmt, 1))
- : NULL_TREE);
- tree type_right
- = (TREE_OPERAND (stmt, 2)
- ? is_bitfield_expr_with_lowered_type (TREE_OPERAND (stmt, 2))
- : NULL_TREE);
- if (type_left
- && !useless_type_conversion_p (TREE_TYPE (stmt),
- TREE_TYPE (TREE_OPERAND (stmt, 1))))
- {
- TREE_OPERAND (stmt, 1)
- = fold_convert (type_left, TREE_OPERAND (stmt, 1));
- gcc_assert (useless_type_conversion_p (TREE_TYPE (stmt),
- type_left));
- }
- if (type_right
- && !useless_type_conversion_p (TREE_TYPE (stmt),
- TREE_TYPE (TREE_OPERAND (stmt, 2))))
- {
- TREE_OPERAND (stmt, 2)
- = fold_convert (type_right, TREE_OPERAND (stmt, 2));
- gcc_assert (useless_type_conversion_p (TREE_TYPE (stmt),
- type_right));
- }
- }
- break;
-
- case BIND_EXPR:
- if (__builtin_expect (wtd->omp_ctx != NULL, 0))
- {
- tree decl;
- for (decl = BIND_EXPR_VARS (stmt); decl; decl = DECL_CHAIN (decl))
- if (VAR_P (decl)
- && !DECL_EXTERNAL (decl)
- && omp_var_to_track (decl))
- {
- splay_tree_node n
- = splay_tree_lookup (wtd->omp_ctx->variables,
- (splay_tree_key) decl);
- if (n == NULL)
- splay_tree_insert (wtd->omp_ctx->variables,
- (splay_tree_key) decl,
- TREE_STATIC (decl)
- ? OMP_CLAUSE_DEFAULT_SHARED
- : OMP_CLAUSE_DEFAULT_PRIVATE);
- }
- }
- if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
- {
- /* The point here is to not sanitize static initializers. */
- bool no_sanitize_p = wtd->no_sanitize_p;
- wtd->no_sanitize_p = true;
- for (tree decl = BIND_EXPR_VARS (stmt);
- decl;
- decl = DECL_CHAIN (decl))
- if (VAR_P (decl)
- && TREE_STATIC (decl)
- && DECL_INITIAL (decl))
- cp_walk_tree (&DECL_INITIAL (decl), cp_genericize_r, data, NULL);
- wtd->no_sanitize_p = no_sanitize_p;
- }
- wtd->bind_expr_stack.safe_push (stmt);
- cp_walk_tree (&BIND_EXPR_BODY (stmt),
- cp_genericize_r, data, NULL);
- wtd->bind_expr_stack.pop ();
- break;
-
- case USING_STMT:
- {
- tree block = NULL_TREE;
-
- /* Get the innermost inclosing GIMPLE_BIND that has a non NULL
- BLOCK, and append an IMPORTED_DECL to its
- BLOCK_VARS chained list. */
- if (wtd->bind_expr_stack.exists ())
- {
- int i;
- for (i = wtd->bind_expr_stack.length () - 1; i >= 0; i--)
- if ((block = BIND_EXPR_BLOCK (wtd->bind_expr_stack[i])))
- break;
- }
- if (block)
- {
- tree decl = TREE_OPERAND (stmt, 0);
- gcc_assert (decl);
-
- if (undeduced_auto_decl (decl))
- /* Omit from the GENERIC, the back-end can't handle it. */;
- else
- {
- tree using_directive = make_node (IMPORTED_DECL);
- TREE_TYPE (using_directive) = void_type_node;
- DECL_CONTEXT (using_directive) = current_function_decl;
-
- IMPORTED_DECL_ASSOCIATED_DECL (using_directive) = decl;
- DECL_CHAIN (using_directive) = BLOCK_VARS (block);
- BLOCK_VARS (block) = using_directive;
- }
- }
- /* The USING_STMT won't appear in GENERIC. */
- *stmt_p = build1 (NOP_EXPR, void_type_node, integer_zero_node);
- *walk_subtrees = 0;
- }
- break;
-
- case DECL_EXPR:
- if (TREE_CODE (DECL_EXPR_DECL (stmt)) == USING_DECL)
- {
- /* Using decls inside DECL_EXPRs are just dropped on the floor. */
- *stmt_p = build1 (NOP_EXPR, void_type_node, integer_zero_node);
- *walk_subtrees = 0;
- }
- else
- {
- tree d = DECL_EXPR_DECL (stmt);
- if (VAR_P (d))
- gcc_assert (CP_DECL_THREAD_LOCAL_P (d) == DECL_THREAD_LOCAL_P (d));
- }
- break;
-
- case OMP_PARALLEL:
- case OMP_TASK:
- case OMP_TASKLOOP:
- {
- struct cp_genericize_omp_taskreg omp_ctx;
- tree c, decl;
- splay_tree_node n;
-
- *walk_subtrees = 0;
- cp_walk_tree (&OMP_CLAUSES (stmt), cp_genericize_r, data, NULL);
- omp_ctx.is_parallel = TREE_CODE (stmt) == OMP_PARALLEL;
- omp_ctx.default_shared = omp_ctx.is_parallel;
- omp_ctx.outer = wtd->omp_ctx;
- omp_ctx.variables = splay_tree_new (splay_tree_compare_decl_uid, 0, 0);
- wtd->omp_ctx = &omp_ctx;
- for (c = OMP_CLAUSES (stmt); c; c = OMP_CLAUSE_CHAIN (c))
- switch (OMP_CLAUSE_CODE (c))
- {
- case OMP_CLAUSE_SHARED:
- case OMP_CLAUSE_PRIVATE:
- case OMP_CLAUSE_FIRSTPRIVATE:
- case OMP_CLAUSE_LASTPRIVATE:
- decl = OMP_CLAUSE_DECL (c);
- if (decl == error_mark_node || !omp_var_to_track (decl))
- break;
- n = splay_tree_lookup (omp_ctx.variables, (splay_tree_key) decl);
- if (n != NULL)
- break;
- splay_tree_insert (omp_ctx.variables, (splay_tree_key) decl,
- OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED
- ? OMP_CLAUSE_DEFAULT_SHARED
- : OMP_CLAUSE_DEFAULT_PRIVATE);
- if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_PRIVATE && omp_ctx.outer)
- omp_cxx_notice_variable (omp_ctx.outer, decl);
- break;
- case OMP_CLAUSE_DEFAULT:
- if (OMP_CLAUSE_DEFAULT_KIND (c) == OMP_CLAUSE_DEFAULT_SHARED)
- omp_ctx.default_shared = true;
- default:
- break;
- }
- if (TREE_CODE (stmt) == OMP_TASKLOOP)
- c_genericize_control_stmt (stmt_p, walk_subtrees, data,
- cp_genericize_r, cp_walk_subtrees);
- else
- cp_walk_tree (&OMP_BODY (stmt), cp_genericize_r, data, NULL);
- wtd->omp_ctx = omp_ctx.outer;
- splay_tree_delete (omp_ctx.variables);
- }
- break;
-
- case OMP_TARGET:
- cfun->has_omp_target = true;
- break;
-
- case TRY_BLOCK:
- {
- *walk_subtrees = 0;
- tree try_block = wtd->try_block;
- wtd->try_block = stmt;
- cp_walk_tree (&TRY_STMTS (stmt), cp_genericize_r, data, NULL);
- wtd->try_block = try_block;
- cp_walk_tree (&TRY_HANDLERS (stmt), cp_genericize_r, data, NULL);
- }
- break;
-
- case MUST_NOT_THROW_EXPR:
- /* MUST_NOT_THROW_COND might be something else with TM. */
- if (MUST_NOT_THROW_COND (stmt) == NULL_TREE)
- {
- *walk_subtrees = 0;
- tree try_block = wtd->try_block;
- wtd->try_block = stmt;
- cp_walk_tree (&TREE_OPERAND (stmt, 0), cp_genericize_r, data, NULL);
- wtd->try_block = try_block;
- }
- break;
-
- case THROW_EXPR:
- {
- location_t loc = location_of (stmt);
- if (warning_suppressed_p (stmt /* What warning? */))
- /* Never mind. */;
- else if (wtd->try_block)
- {
- if (TREE_CODE (wtd->try_block) == MUST_NOT_THROW_EXPR)
- {
- auto_diagnostic_group d;
- if (warning_at (loc, OPT_Wterminate,
- "%<throw%> will always call %<terminate%>")
- && cxx_dialect >= cxx11
- && DECL_DESTRUCTOR_P (current_function_decl))
- inform (loc, "in C++11 destructors default to %<noexcept%>");
- }
- }
- else
- {
- if (warn_cxx11_compat && cxx_dialect < cxx11
- && DECL_DESTRUCTOR_P (current_function_decl)
- && (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))
- == NULL_TREE)
- && (get_defaulted_eh_spec (current_function_decl)
- == empty_except_spec))
- warning_at (loc, OPT_Wc__11_compat,
- "in C++11 this %<throw%> will call %<terminate%> "
- "because destructors default to %<noexcept%>");
- }
- }
- break;
-
- case CONVERT_EXPR:
- gcc_assert (!CONVERT_EXPR_VBASE_PATH (stmt));
- break;
-
- case SPACESHIP_EXPR:
- *stmt_p = genericize_spaceship (*stmt_p);
- break;
-
- case PTRMEM_CST:
- /* By the time we get here we're handing off to the back end, so we don't
- need or want to preserve PTRMEM_CST anymore. */
- *stmt_p = cplus_expand_constant (stmt);
- *walk_subtrees = 0;
- break;
-
- case MEM_REF:
- /* For MEM_REF, make sure not to sanitize the second operand even
- if it has reference type. It is just an offset with a type
- holding other information. There is no other processing we
- need to do for INTEGER_CSTs, so just ignore the second argument
- unconditionally. */
- cp_walk_tree (&TREE_OPERAND (stmt, 0), cp_genericize_r, data, NULL);
- *walk_subtrees = 0;
- break;
-
- case NOP_EXPR:
- *stmt_p = predeclare_vla (*stmt_p);
- if (!wtd->no_sanitize_p
- && sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT)
- && TYPE_REF_P (TREE_TYPE (stmt)))
- ubsan_maybe_instrument_reference (stmt_p);
- break;
-
- case CALL_EXPR:
- /* Evaluate function concept checks instead of treating them as
- normal functions. */
- if (concept_check_p (stmt))
- {
- *stmt_p = evaluate_concept_check (stmt);
- * walk_subtrees = 0;
- break;
- }
-
- if (tree fndecl = cp_get_callee_fndecl_nofold (stmt))
- if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
- {
- gcc_assert (source_location_current_p (fndecl));
- *stmt_p = cxx_constant_value (stmt);
- break;
- }
-
- if (!wtd->no_sanitize_p
- && sanitize_flags_p ((SANITIZE_NULL
- | SANITIZE_ALIGNMENT | SANITIZE_VPTR)))
- {
- tree fn = CALL_EXPR_FN (stmt);
- if (fn != NULL_TREE
- && !error_operand_p (fn)
- && INDIRECT_TYPE_P (TREE_TYPE (fn))
- && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) == METHOD_TYPE)
- {
- bool is_ctor
- = TREE_CODE (fn) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
- && DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0));
- if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT))
- ubsan_maybe_instrument_member_call (stmt, is_ctor);
- if (sanitize_flags_p (SANITIZE_VPTR) && !is_ctor)
- cp_ubsan_maybe_instrument_member_call (stmt);
- }
- else if (fn == NULL_TREE
- && CALL_EXPR_IFN (stmt) == IFN_UBSAN_NULL
- && TREE_CODE (CALL_EXPR_ARG (stmt, 0)) == INTEGER_CST
- && TYPE_REF_P (TREE_TYPE (CALL_EXPR_ARG (stmt, 0))))
- *walk_subtrees = 0;
- }
- /* Fall through. */
- case AGGR_INIT_EXPR:
- /* For calls to a multi-versioned function, overload resolution
- returns the function with the highest target priority, that is,
- the version that will checked for dispatching first. If this
- version is inlinable, a direct call to this version can be made
- otherwise the call should go through the dispatcher. */
- {
- tree fn = cp_get_callee_fndecl_nofold (stmt);
- if (fn && DECL_FUNCTION_VERSIONED (fn)
- && (current_function_decl == NULL
- || !targetm.target_option.can_inline_p (current_function_decl,
- fn)))
- if (tree dis = get_function_version_dispatcher (fn))
- {
- mark_versions_used (dis);
- dis = build_address (dis);
- if (TREE_CODE (stmt) == CALL_EXPR)
- CALL_EXPR_FN (stmt) = dis;
- else
- AGGR_INIT_EXPR_FN (stmt) = dis;
- }
- }
- break;
-
- case TARGET_EXPR:
- if (TARGET_EXPR_INITIAL (stmt)
- && TREE_CODE (TARGET_EXPR_INITIAL (stmt)) == CONSTRUCTOR
- && CONSTRUCTOR_PLACEHOLDER_BOUNDARY (TARGET_EXPR_INITIAL (stmt)))
- TARGET_EXPR_NO_ELIDE (stmt) = 1;
- break;
-
- case TEMPLATE_ID_EXPR:
- gcc_assert (concept_check_p (stmt));
- /* Emit the value of the concept check. */
- *stmt_p = evaluate_concept_check (stmt);
- walk_subtrees = 0;
- break;
-
- case OMP_DISTRIBUTE:
- /* Need to explicitly instantiate copy ctors on class iterators of
- composite distribute parallel for. */
- if (OMP_FOR_INIT (*stmt_p) == NULL_TREE)
- {
- tree *data[4] = { NULL, NULL, NULL, NULL };
- tree inner = walk_tree (&OMP_FOR_BODY (*stmt_p),
- find_combined_omp_for, data, NULL);
- if (inner != NULL_TREE
- && TREE_CODE (inner) == OMP_FOR)
- {
- for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (inner)); i++)
- if (OMP_FOR_ORIG_DECLS (inner)
- && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner),
- i)) == TREE_LIST
- && TREE_PURPOSE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner),
- i)))
- {
- tree orig = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner), i);
- /* Class iterators aren't allowed on OMP_SIMD, so the only
- case we need to solve is distribute parallel for. */
- gcc_assert (TREE_CODE (inner) == OMP_FOR
- && data[1]);
- tree orig_decl = TREE_PURPOSE (orig);
- tree c, cl = NULL_TREE;
- for (c = OMP_FOR_CLAUSES (inner);
- c; c = OMP_CLAUSE_CHAIN (c))
- if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
- || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE)
- && OMP_CLAUSE_DECL (c) == orig_decl)
- {
- cl = c;
- break;
- }
- if (cl == NULL_TREE)
- {
- for (c = OMP_PARALLEL_CLAUSES (*data[1]);
- c; c = OMP_CLAUSE_CHAIN (c))
- if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
- && OMP_CLAUSE_DECL (c) == orig_decl)
- {
- cl = c;
- break;
- }
- }
- if (cl)
- {
- orig_decl = require_complete_type (orig_decl);
- tree inner_type = TREE_TYPE (orig_decl);
- if (orig_decl == error_mark_node)
- continue;
- if (TYPE_REF_P (TREE_TYPE (orig_decl)))
- inner_type = TREE_TYPE (inner_type);
-
- while (TREE_CODE (inner_type) == ARRAY_TYPE)
- inner_type = TREE_TYPE (inner_type);
- get_copy_ctor (inner_type, tf_warning_or_error);
- }
- }
- }
- }
- /* FALLTHRU */
-
- case FOR_STMT:
- case WHILE_STMT:
- case DO_STMT:
- case SWITCH_STMT:
- case CONTINUE_STMT:
- case BREAK_STMT:
- case OMP_FOR:
- case OMP_SIMD:
- case OMP_LOOP:
- case OACC_LOOP:
- case STATEMENT_LIST:
- /* These cases are handled by shared code. */
- c_genericize_control_stmt (stmt_p, walk_subtrees, data,
- cp_genericize_r, cp_walk_subtrees);
- break;
-
- case BIT_CAST_EXPR:
- *stmt_p = build1_loc (EXPR_LOCATION (stmt), VIEW_CONVERT_EXPR,
- TREE_TYPE (stmt), TREE_OPERAND (stmt, 0));
- break;
-
- default:
- if (IS_TYPE_OR_DECL_P (stmt))
- *walk_subtrees = 0;
- break;
- }
-
- p_set->add (*stmt_p);
-
- return NULL;
-}
-
-/* Lower C++ front end trees to GENERIC in T_P. */
-
-static void
-cp_genericize_tree (tree* t_p, bool handle_invisiref_parm_p)
-{
- struct cp_genericize_data wtd;
-
- wtd.p_set = new hash_set<tree>;
- wtd.bind_expr_stack.create (0);
- wtd.omp_ctx = NULL;
- wtd.try_block = NULL_TREE;
- wtd.no_sanitize_p = false;
- wtd.handle_invisiref_parm_p = handle_invisiref_parm_p;
- cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL);
- delete wtd.p_set;
- if (sanitize_flags_p (SANITIZE_VPTR))
- cp_ubsan_instrument_member_accesses (t_p);
-}
-
-/* If a function that should end with a return in non-void
- function doesn't obviously end with return, add ubsan
- instrumentation code to verify it at runtime. If -fsanitize=return
- is not enabled, instrument __builtin_unreachable. */
-
-static void
-cp_maybe_instrument_return (tree fndecl)
-{
- if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl)))
- || DECL_CONSTRUCTOR_P (fndecl)
- || DECL_DESTRUCTOR_P (fndecl)
- || !targetm.warn_func_return (fndecl))
- return;
-
- if (!sanitize_flags_p (SANITIZE_RETURN, fndecl)
- /* Don't add __builtin_unreachable () if not optimizing, it will not
- improve any optimizations in that case, just break UB code.
- Don't add it if -fsanitize=unreachable -fno-sanitize=return either,
- UBSan covers this with ubsan_instrument_return above where sufficient
- information is provided, while the __builtin_unreachable () below
- if return sanitization is disabled will just result in hard to
- understand runtime error without location. */
- && (!optimize
- || sanitize_flags_p (SANITIZE_UNREACHABLE, fndecl)))
- return;
-
- tree t = DECL_SAVED_TREE (fndecl);
- while (t)
- {
- switch (TREE_CODE (t))
- {
- case BIND_EXPR:
- t = BIND_EXPR_BODY (t);
- continue;
- case TRY_FINALLY_EXPR:
- case CLEANUP_POINT_EXPR:
- t = TREE_OPERAND (t, 0);
- continue;
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i = tsi_last (t);
- while (!tsi_end_p (i))
- {
- tree p = tsi_stmt (i);
- if (TREE_CODE (p) != DEBUG_BEGIN_STMT)
- break;
- tsi_prev (&i);
- }
- if (!tsi_end_p (i))
- {
- t = tsi_stmt (i);
- continue;
- }
- }
- break;
- case RETURN_EXPR:
- return;
- default:
- break;
- }
- break;
- }
- if (t == NULL_TREE)
- return;
- tree *p = &DECL_SAVED_TREE (fndecl);
- if (TREE_CODE (*p) == BIND_EXPR)
- p = &BIND_EXPR_BODY (*p);
-
- location_t loc = DECL_SOURCE_LOCATION (fndecl);
- if (sanitize_flags_p (SANITIZE_RETURN, fndecl))
- t = ubsan_instrument_return (loc);
- else
- {
- tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
- t = build_call_expr_loc (BUILTINS_LOCATION, fndecl, 0);
- }
-
- append_to_statement_list (t, p);
-}
-
-void
-cp_genericize (tree fndecl)
-{
- tree t;
-
- /* Fix up the types of parms passed by invisible reference. */
- for (t = DECL_ARGUMENTS (fndecl); t; t = DECL_CHAIN (t))
- if (TREE_ADDRESSABLE (TREE_TYPE (t)))
- {
- /* If a function's arguments are copied to create a thunk,
- then DECL_BY_REFERENCE will be set -- but the type of the
- argument will be a pointer type, so we will never get
- here. */
- gcc_assert (!DECL_BY_REFERENCE (t));
- gcc_assert (DECL_ARG_TYPE (t) != TREE_TYPE (t));
- TREE_TYPE (t) = DECL_ARG_TYPE (t);
- DECL_BY_REFERENCE (t) = 1;
- TREE_ADDRESSABLE (t) = 0;
- relayout_decl (t);
- }
-
- /* Do the same for the return value. */
- if (TREE_ADDRESSABLE (TREE_TYPE (DECL_RESULT (fndecl))))
- {
- t = DECL_RESULT (fndecl);
- TREE_TYPE (t) = build_reference_type (TREE_TYPE (t));
- DECL_BY_REFERENCE (t) = 1;
- TREE_ADDRESSABLE (t) = 0;
- relayout_decl (t);
- if (DECL_NAME (t))
- {
- /* Adjust DECL_VALUE_EXPR of the original var. */
- tree outer = outer_curly_brace_block (current_function_decl);
- tree var;
-
- if (outer)
- for (var = BLOCK_VARS (outer); var; var = DECL_CHAIN (var))
- if (VAR_P (var)
- && DECL_NAME (t) == DECL_NAME (var)
- && DECL_HAS_VALUE_EXPR_P (var)
- && DECL_VALUE_EXPR (var) == t)
- {
- tree val = convert_from_reference (t);
- SET_DECL_VALUE_EXPR (var, val);
- break;
- }
- }
- }
-
- /* If we're a clone, the body is already GIMPLE. */
- if (DECL_CLONED_FUNCTION_P (fndecl))
- return;
-
- /* Allow cp_genericize calls to be nested. */
- bc_state_t save_state;
- save_bc_state (&save_state);
-
- /* We do want to see every occurrence of the parms, so we can't just use
- walk_tree's hash functionality. */
- cp_genericize_tree (&DECL_SAVED_TREE (fndecl), true);
-
- cp_maybe_instrument_return (fndecl);
-
- /* Do everything else. */
- c_genericize (fndecl);
- restore_bc_state (&save_state);
-}
-
-/* Build code to apply FN to each member of ARG1 and ARG2. FN may be
- NULL if there is in fact nothing to do. ARG2 may be null if FN
- actually only takes one argument. */
-
-static tree
-cxx_omp_clause_apply_fn (tree fn, tree arg1, tree arg2)
-{
- tree defparm, parm, t;
- int i = 0;
- int nargs;
- tree *argarray;
-
- if (fn == NULL)
- return NULL;
-
- nargs = list_length (DECL_ARGUMENTS (fn));
- argarray = XALLOCAVEC (tree, nargs);
-
- defparm = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)));
- if (arg2)
- defparm = TREE_CHAIN (defparm);
-
- bool is_method = TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE;
- if (TREE_CODE (TREE_TYPE (arg1)) == ARRAY_TYPE)
- {
- tree inner_type = TREE_TYPE (arg1);
- tree start1, end1, p1;
- tree start2 = NULL, p2 = NULL;
- tree ret = NULL, lab;
-
- start1 = arg1;
- start2 = arg2;
- do
- {
- inner_type = TREE_TYPE (inner_type);
- start1 = build4 (ARRAY_REF, inner_type, start1,
- size_zero_node, NULL, NULL);
- if (arg2)
- start2 = build4 (ARRAY_REF, inner_type, start2,
- size_zero_node, NULL, NULL);
- }
- while (TREE_CODE (inner_type) == ARRAY_TYPE);
- start1 = build_fold_addr_expr_loc (input_location, start1);
- if (arg2)
- start2 = build_fold_addr_expr_loc (input_location, start2);
-
- end1 = TYPE_SIZE_UNIT (TREE_TYPE (arg1));
- end1 = fold_build_pointer_plus (start1, end1);
-
- p1 = create_tmp_var (TREE_TYPE (start1));
- t = build2 (MODIFY_EXPR, TREE_TYPE (p1), p1, start1);
- append_to_statement_list (t, &ret);
-
- if (arg2)
- {
- p2 = create_tmp_var (TREE_TYPE (start2));
- t = build2 (MODIFY_EXPR, TREE_TYPE (p2), p2, start2);
- append_to_statement_list (t, &ret);
- }
-
- lab = create_artificial_label (input_location);
- t = build1 (LABEL_EXPR, void_type_node, lab);
- append_to_statement_list (t, &ret);
-
- argarray[i++] = p1;
- if (arg2)
- argarray[i++] = p2;
- /* Handle default arguments. */
- for (parm = defparm; parm && parm != void_list_node;
- parm = TREE_CHAIN (parm), i++)
- argarray[i] = convert_default_arg (TREE_VALUE (parm),
- TREE_PURPOSE (parm), fn,
- i - is_method, tf_warning_or_error);
- t = build_call_a (fn, i, argarray);
- t = fold_convert (void_type_node, t);
- t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
- append_to_statement_list (t, &ret);
-
- t = fold_build_pointer_plus (p1, TYPE_SIZE_UNIT (inner_type));
- t = build2 (MODIFY_EXPR, TREE_TYPE (p1), p1, t);
- append_to_statement_list (t, &ret);
-
- if (arg2)
- {
- t = fold_build_pointer_plus (p2, TYPE_SIZE_UNIT (inner_type));
- t = build2 (MODIFY_EXPR, TREE_TYPE (p2), p2, t);
- append_to_statement_list (t, &ret);
- }
-
- t = build2 (NE_EXPR, boolean_type_node, p1, end1);
- t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&lab), NULL);
- append_to_statement_list (t, &ret);
-
- return ret;
- }
- else
- {
- argarray[i++] = build_fold_addr_expr_loc (input_location, arg1);
- if (arg2)
- argarray[i++] = build_fold_addr_expr_loc (input_location, arg2);
- /* Handle default arguments. */
- for (parm = defparm; parm && parm != void_list_node;
- parm = TREE_CHAIN (parm), i++)
- argarray[i] = convert_default_arg (TREE_VALUE (parm),
- TREE_PURPOSE (parm), fn,
- i - is_method, tf_warning_or_error);
- t = build_call_a (fn, i, argarray);
- t = fold_convert (void_type_node, t);
- return fold_build_cleanup_point_expr (TREE_TYPE (t), t);
- }
-}
-
-/* Return code to initialize DECL with its default constructor, or
- NULL if there's nothing to do. */
-
-tree
-cxx_omp_clause_default_ctor (tree clause, tree decl, tree /*outer*/)
-{
- tree info = CP_OMP_CLAUSE_INFO (clause);
- tree ret = NULL;
-
- if (info)
- ret = cxx_omp_clause_apply_fn (TREE_VEC_ELT (info, 0), decl, NULL);
-
- return ret;
-}
-
-/* Return code to initialize DST with a copy constructor from SRC. */
-
-tree
-cxx_omp_clause_copy_ctor (tree clause, tree dst, tree src)
-{
- tree info = CP_OMP_CLAUSE_INFO (clause);
- tree ret = NULL;
-
- if (info)
- ret = cxx_omp_clause_apply_fn (TREE_VEC_ELT (info, 0), dst, src);
- if (ret == NULL)
- ret = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
-
- return ret;
-}
-
-/* Similarly, except use an assignment operator instead. */
-
-tree
-cxx_omp_clause_assign_op (tree clause, tree dst, tree src)
-{
- tree info = CP_OMP_CLAUSE_INFO (clause);
- tree ret = NULL;
-
- if (info)
- ret = cxx_omp_clause_apply_fn (TREE_VEC_ELT (info, 2), dst, src);
- if (ret == NULL)
- ret = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
-
- return ret;
-}
-
-/* Return code to destroy DECL. */
-
-tree
-cxx_omp_clause_dtor (tree clause, tree decl)
-{
- tree info = CP_OMP_CLAUSE_INFO (clause);
- tree ret = NULL;
-
- if (info)
- ret = cxx_omp_clause_apply_fn (TREE_VEC_ELT (info, 1), decl, NULL);
-
- return ret;
-}
-
-/* True if OpenMP should privatize what this DECL points to rather
- than the DECL itself. */
-
-bool
-cxx_omp_privatize_by_reference (const_tree decl)
-{
- return (TYPE_REF_P (TREE_TYPE (decl))
- || is_invisiref_parm (decl));
-}
-
-/* Return true if DECL is const qualified var having no mutable member. */
-bool
-cxx_omp_const_qual_no_mutable (tree decl)
-{
- tree type = TREE_TYPE (decl);
- if (TYPE_REF_P (type))
- {
- if (!is_invisiref_parm (decl))
- return false;
- type = TREE_TYPE (type);
-
- if (TREE_CODE (decl) == RESULT_DECL && DECL_NAME (decl))
- {
- /* NVR doesn't preserve const qualification of the
- variable's type. */
- tree outer = outer_curly_brace_block (current_function_decl);
- tree var;
-
- if (outer)
- for (var = BLOCK_VARS (outer); var; var = DECL_CHAIN (var))
- if (VAR_P (var)
- && DECL_NAME (decl) == DECL_NAME (var)
- && (TYPE_MAIN_VARIANT (type)
- == TYPE_MAIN_VARIANT (TREE_TYPE (var))))
- {
- if (TYPE_READONLY (TREE_TYPE (var)))
- type = TREE_TYPE (var);
- break;
- }
- }
- }
-
- if (type == error_mark_node)
- return false;
-
- /* Variables with const-qualified type having no mutable member
- are predetermined shared. */
- if (TYPE_READONLY (type) && !cp_has_mutable_p (type))
- return true;
-
- return false;
-}
-
-/* OMP_CLAUSE_DEFAULT_UNSPECIFIED unless OpenMP sharing attribute
- of DECL is predetermined. */
-
-enum omp_clause_default_kind
-cxx_omp_predetermined_sharing_1 (tree decl)
-{
- /* Static data members are predetermined shared. */
- if (TREE_STATIC (decl))
- {
- tree ctx = CP_DECL_CONTEXT (decl);
- if (TYPE_P (ctx) && MAYBE_CLASS_TYPE_P (ctx))
- return OMP_CLAUSE_DEFAULT_SHARED;
-
- if (c_omp_predefined_variable (decl))
- return OMP_CLAUSE_DEFAULT_SHARED;
- }
-
- /* this may not be specified in data-sharing clauses, still we need
- to predetermined it firstprivate. */
- if (decl == current_class_ptr)
- return OMP_CLAUSE_DEFAULT_FIRSTPRIVATE;
-
- return OMP_CLAUSE_DEFAULT_UNSPECIFIED;
-}
-
-/* Likewise, but also include the artificial vars. We don't want to
- disallow the artificial vars being mentioned in explicit clauses,
- as we use artificial vars e.g. for loop constructs with random
- access iterators other than pointers, but during gimplification
- we want to treat them as predetermined. */
-
-enum omp_clause_default_kind
-cxx_omp_predetermined_sharing (tree decl)
-{
- enum omp_clause_default_kind ret = cxx_omp_predetermined_sharing_1 (decl);
- if (ret != OMP_CLAUSE_DEFAULT_UNSPECIFIED)
- return ret;
-
- /* Predetermine artificial variables holding integral values, those
- are usually result of gimplify_one_sizepos or SAVE_EXPR
- gimplification. */
- if (VAR_P (decl)
- && DECL_ARTIFICIAL (decl)
- && INTEGRAL_TYPE_P (TREE_TYPE (decl))
- && !(DECL_LANG_SPECIFIC (decl)
- && DECL_OMP_PRIVATIZED_MEMBER (decl)))
- return OMP_CLAUSE_DEFAULT_SHARED;
-
- /* Similarly for typeinfo symbols. */
- if (VAR_P (decl) && DECL_ARTIFICIAL (decl) && DECL_TINFO_P (decl))
- return OMP_CLAUSE_DEFAULT_SHARED;
-
- return OMP_CLAUSE_DEFAULT_UNSPECIFIED;
-}
-
-enum omp_clause_defaultmap_kind
-cxx_omp_predetermined_mapping (tree decl)
-{
- /* Predetermine artificial variables holding integral values, those
- are usually result of gimplify_one_sizepos or SAVE_EXPR
- gimplification. */
- if (VAR_P (decl)
- && DECL_ARTIFICIAL (decl)
- && INTEGRAL_TYPE_P (TREE_TYPE (decl))
- && !(DECL_LANG_SPECIFIC (decl)
- && DECL_OMP_PRIVATIZED_MEMBER (decl)))
- return OMP_CLAUSE_DEFAULTMAP_FIRSTPRIVATE;
-
- if (c_omp_predefined_variable (decl))
- return OMP_CLAUSE_DEFAULTMAP_TO;
-
- return OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED;
-}
-
-/* Finalize an implicitly determined clause. */
-
-void
-cxx_omp_finish_clause (tree c, gimple_seq *, bool /* openacc */)
-{
- tree decl, inner_type;
- bool make_shared = false;
-
- if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_FIRSTPRIVATE
- && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_PRIVATE
- && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_LASTPRIVATE
- || !OMP_CLAUSE_LASTPRIVATE_LOOP_IV (c)))
- return;
-
- decl = OMP_CLAUSE_DECL (c);
- decl = require_complete_type (decl);
- inner_type = TREE_TYPE (decl);
- if (decl == error_mark_node)
- make_shared = true;
- else if (TYPE_REF_P (TREE_TYPE (decl)))
- inner_type = TREE_TYPE (inner_type);
-
- /* We're interested in the base element, not arrays. */
- while (TREE_CODE (inner_type) == ARRAY_TYPE)
- inner_type = TREE_TYPE (inner_type);
-
- /* Check for special function availability by building a call to one.
- Save the results, because later we won't be in the right context
- for making these queries. */
- bool first = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE;
- bool last = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE;
- if (!make_shared
- && CLASS_TYPE_P (inner_type)
- && cxx_omp_create_clause_info (c, inner_type, !first, first, last,
- true))
- make_shared = true;
-
- if (make_shared)
- {
- OMP_CLAUSE_CODE (c) = OMP_CLAUSE_SHARED;
- OMP_CLAUSE_SHARED_FIRSTPRIVATE (c) = 0;
- OMP_CLAUSE_SHARED_READONLY (c) = 0;
- }
-}
-
-/* Return true if DECL's DECL_VALUE_EXPR (if any) should be
- disregarded in OpenMP construct, because it is going to be
- remapped during OpenMP lowering. SHARED is true if DECL
- is going to be shared, false if it is going to be privatized. */
-
-bool
-cxx_omp_disregard_value_expr (tree decl, bool shared)
-{
- if (shared)
- return false;
- if (VAR_P (decl)
- && DECL_HAS_VALUE_EXPR_P (decl)
- && DECL_ARTIFICIAL (decl)
- && DECL_LANG_SPECIFIC (decl)
- && DECL_OMP_PRIVATIZED_MEMBER (decl))
- return true;
- if (VAR_P (decl) && DECL_CONTEXT (decl) && is_capture_proxy (decl))
- return true;
- return false;
-}
-
-/* Fold expression X which is used as an rvalue if RVAL is true. */
-
-tree
-cp_fold_maybe_rvalue (tree x, bool rval)
-{
- while (true)
- {
- x = cp_fold (x);
- if (rval)
- x = mark_rvalue_use (x);
- if (rval && DECL_P (x)
- && !TYPE_REF_P (TREE_TYPE (x)))
- {
- tree v = decl_constant_value (x);
- if (v != x && v != error_mark_node)
- {
- x = v;
- continue;
- }
- }
- break;
- }
- return x;
-}
-
-/* Fold expression X which is used as an rvalue. */
-
-tree
-cp_fold_rvalue (tree x)
-{
- return cp_fold_maybe_rvalue (x, true);
-}
-
-/* Perform folding on expression X. */
-
-tree
-cp_fully_fold (tree x)
-{
- if (processing_template_decl)
- return x;
- /* FIXME cp_fold ought to be a superset of maybe_constant_value so we don't
- have to call both. */
- if (cxx_dialect >= cxx11)
- {
- x = maybe_constant_value (x);
- /* Sometimes we are given a CONSTRUCTOR but the call above wraps it into
- a TARGET_EXPR; undo that here. */
- if (TREE_CODE (x) == TARGET_EXPR)
- x = TARGET_EXPR_INITIAL (x);
- else if (TREE_CODE (x) == VIEW_CONVERT_EXPR
- && TREE_CODE (TREE_OPERAND (x, 0)) == CONSTRUCTOR
- && TREE_TYPE (TREE_OPERAND (x, 0)) == TREE_TYPE (x))
- x = TREE_OPERAND (x, 0);
- }
- return cp_fold_rvalue (x);
-}
-
-/* Likewise, but also fold recursively, which cp_fully_fold doesn't perform
- in some cases. */
-
-tree
-cp_fully_fold_init (tree x)
-{
- if (processing_template_decl)
- return x;
- x = cp_fully_fold (x);
- hash_set<tree> pset;
- cp_walk_tree (&x, cp_fold_r, &pset, NULL);
- return x;
-}
-
-/* c-common interface to cp_fold. If IN_INIT, this is in a static initializer
- and certain changes are made to the folding done. Or should be (FIXME). We
- never touch maybe_const, as it is only used for the C front-end
- C_MAYBE_CONST_EXPR. */
-
-tree
-c_fully_fold (tree x, bool /*in_init*/, bool */*maybe_const*/, bool lval)
-{
- return cp_fold_maybe_rvalue (x, !lval);
-}
-
-static GTY((deletable)) hash_map<tree, tree> *fold_cache;
-
-/* Dispose of the whole FOLD_CACHE. */
-
-void
-clear_fold_cache (void)
-{
- if (fold_cache != NULL)
- fold_cache->empty ();
-}
-
-/* This function tries to fold an expression X.
- To avoid combinatorial explosion, folding results are kept in fold_cache.
- If X is invalid, we don't fold at all.
- For performance reasons we don't cache expressions representing a
- declaration or constant.
- Function returns X or its folded variant. */
-
-static tree
-cp_fold (tree x)
-{
- tree op0, op1, op2, op3;
- tree org_x = x, r = NULL_TREE;
- enum tree_code code;
- location_t loc;
- bool rval_ops = true;
-
- if (!x || x == error_mark_node)
- return x;
-
- if (EXPR_P (x) && (!TREE_TYPE (x) || TREE_TYPE (x) == error_mark_node))
- return x;
-
- /* Don't bother to cache DECLs or constants. */
- if (DECL_P (x) || CONSTANT_CLASS_P (x))
- return x;
-
- if (fold_cache == NULL)
- fold_cache = hash_map<tree, tree>::create_ggc (101);
-
- if (tree *cached = fold_cache->get (x))
- return *cached;
-
- uid_sensitive_constexpr_evaluation_checker c;
-
- code = TREE_CODE (x);
- switch (code)
- {
- case CLEANUP_POINT_EXPR:
- /* Strip CLEANUP_POINT_EXPR if the expression doesn't have side
- effects. */
- r = cp_fold_rvalue (TREE_OPERAND (x, 0));
- if (!TREE_SIDE_EFFECTS (r))
- x = r;
- break;
-
- case SIZEOF_EXPR:
- x = fold_sizeof_expr (x);
- break;
-
- case VIEW_CONVERT_EXPR:
- rval_ops = false;
- /* FALLTHRU */
- case CONVERT_EXPR:
- case NOP_EXPR:
- case NON_LVALUE_EXPR:
-
- if (VOID_TYPE_P (TREE_TYPE (x)))
- {
- /* This is just to make sure we don't end up with casts to
- void from error_mark_node. If we just return x, then
- cp_fold_r might fold the operand into error_mark_node and
- leave the conversion in the IR. STRIP_USELESS_TYPE_CONVERSION
- during gimplification doesn't like such casts.
- Don't create a new tree if op0 != TREE_OPERAND (x, 0), the
- folding of the operand should be in the caches and if in cp_fold_r
- it will modify it in place. */
- op0 = cp_fold (TREE_OPERAND (x, 0));
- if (op0 == error_mark_node)
- x = error_mark_node;
- break;
- }
-
- loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
-
- if (code == CONVERT_EXPR
- && SCALAR_TYPE_P (TREE_TYPE (x))
- && op0 != void_node)
- /* During parsing we used convert_to_*_nofold; re-convert now using the
- folding variants, since fold() doesn't do those transformations. */
- x = fold (convert (TREE_TYPE (x), op0));
- else if (op0 != TREE_OPERAND (x, 0))
- {
- if (op0 == error_mark_node)
- x = error_mark_node;
- else
- x = fold_build1_loc (loc, code, TREE_TYPE (x), op0);
- }
- else
- x = fold (x);
-
- /* Conversion of an out-of-range value has implementation-defined
- behavior; the language considers it different from arithmetic
- overflow, which is undefined. */
- if (TREE_CODE (op0) == INTEGER_CST
- && TREE_OVERFLOW_P (x) && !TREE_OVERFLOW_P (op0))
- TREE_OVERFLOW (x) = false;
-
- break;
-
- case INDIRECT_REF:
- /* We don't need the decltype(auto) obfuscation anymore. */
- if (REF_PARENTHESIZED_P (x))
- {
- tree p = maybe_undo_parenthesized_ref (x);
- return cp_fold (p);
- }
- goto unary;
-
- case ADDR_EXPR:
- loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), false);
-
- /* Cope with user tricks that amount to offsetof. */
- if (op0 != error_mark_node
- && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (op0)))
- {
- tree val = get_base_address (op0);
- if (val
- && INDIRECT_REF_P (val)
- && COMPLETE_TYPE_P (TREE_TYPE (val))
- && TREE_CONSTANT (TREE_OPERAND (val, 0)))
- {
- val = TREE_OPERAND (val, 0);
- STRIP_NOPS (val);
- val = maybe_constant_value (val);
- if (TREE_CODE (val) == INTEGER_CST)
- return fold_offsetof (op0, TREE_TYPE (x));
- }
- }
- goto finish_unary;
-
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- rval_ops = false;
- /* FALLTHRU */
- case CONJ_EXPR:
- case FIX_TRUNC_EXPR:
- case FLOAT_EXPR:
- case NEGATE_EXPR:
- case ABS_EXPR:
- case ABSU_EXPR:
- case BIT_NOT_EXPR:
- case TRUTH_NOT_EXPR:
- case FIXED_CONVERT_EXPR:
- unary:
-
- loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
-
- finish_unary:
- if (op0 != TREE_OPERAND (x, 0))
- {
- if (op0 == error_mark_node)
- x = error_mark_node;
- else
- {
- x = fold_build1_loc (loc, code, TREE_TYPE (x), op0);
- if (code == INDIRECT_REF
- && (INDIRECT_REF_P (x) || TREE_CODE (x) == MEM_REF))
- {
- TREE_READONLY (x) = TREE_READONLY (org_x);
- TREE_SIDE_EFFECTS (x) = TREE_SIDE_EFFECTS (org_x);
- TREE_THIS_VOLATILE (x) = TREE_THIS_VOLATILE (org_x);
- }
- }
- }
- else
- x = fold (x);
-
- gcc_assert (TREE_CODE (x) != COND_EXPR
- || !VOID_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0))));
- break;
-
- case UNARY_PLUS_EXPR:
- op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
- if (op0 == error_mark_node)
- x = error_mark_node;
- else
- x = fold_convert (TREE_TYPE (x), op0);
- break;
-
- case POSTDECREMENT_EXPR:
- case POSTINCREMENT_EXPR:
- case INIT_EXPR:
- case PREDECREMENT_EXPR:
- case PREINCREMENT_EXPR:
- case COMPOUND_EXPR:
- case MODIFY_EXPR:
- rval_ops = false;
- /* FALLTHRU */
- case POINTER_PLUS_EXPR:
- case PLUS_EXPR:
- case POINTER_DIFF_EXPR:
- case MINUS_EXPR:
- case MULT_EXPR:
- case TRUNC_DIV_EXPR:
- case CEIL_DIV_EXPR:
- case FLOOR_DIV_EXPR:
- case ROUND_DIV_EXPR:
- case TRUNC_MOD_EXPR:
- case CEIL_MOD_EXPR:
- case ROUND_MOD_EXPR:
- case RDIV_EXPR:
- case EXACT_DIV_EXPR:
- case MIN_EXPR:
- case MAX_EXPR:
- case LSHIFT_EXPR:
- case RSHIFT_EXPR:
- case LROTATE_EXPR:
- case RROTATE_EXPR:
- case BIT_AND_EXPR:
- case BIT_IOR_EXPR:
- case BIT_XOR_EXPR:
- case TRUTH_AND_EXPR:
- case TRUTH_ANDIF_EXPR:
- case TRUTH_OR_EXPR:
- case TRUTH_ORIF_EXPR:
- case TRUTH_XOR_EXPR:
- case LT_EXPR: case LE_EXPR:
- case GT_EXPR: case GE_EXPR:
- case EQ_EXPR: case NE_EXPR:
- case UNORDERED_EXPR: case ORDERED_EXPR:
- case UNLT_EXPR: case UNLE_EXPR:
- case UNGT_EXPR: case UNGE_EXPR:
- case UNEQ_EXPR: case LTGT_EXPR:
- case RANGE_EXPR: case COMPLEX_EXPR:
-
- loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
- op1 = cp_fold_rvalue (TREE_OPERAND (x, 1));
-
- /* decltype(nullptr) has only one value, so optimize away all comparisons
- with that type right away, keeping them in the IL causes troubles for
- various optimizations. */
- if (COMPARISON_CLASS_P (org_x)
- && TREE_CODE (TREE_TYPE (op0)) == NULLPTR_TYPE
- && TREE_CODE (TREE_TYPE (op1)) == NULLPTR_TYPE)
- {
- switch (code)
- {
- case EQ_EXPR:
- x = constant_boolean_node (true, TREE_TYPE (x));
- break;
- case NE_EXPR:
- x = constant_boolean_node (false, TREE_TYPE (x));
- break;
- default:
- gcc_unreachable ();
- }
- return omit_two_operands_loc (loc, TREE_TYPE (x), x,
- op0, op1);
- }
-
- if (op0 != TREE_OPERAND (x, 0) || op1 != TREE_OPERAND (x, 1))
- {
- if (op0 == error_mark_node || op1 == error_mark_node)
- x = error_mark_node;
- else
- x = fold_build2_loc (loc, code, TREE_TYPE (x), op0, op1);
- }
- else
- x = fold (x);
-
- /* This is only needed for -Wnonnull-compare and only if
- TREE_NO_WARNING (org_x), but to avoid that option affecting code
- generation, we do it always. */
- if (COMPARISON_CLASS_P (org_x))
- {
- if (x == error_mark_node || TREE_CODE (x) == INTEGER_CST)
- ;
- else if (COMPARISON_CLASS_P (x))
- {
- if (warn_nonnull_compare
- && warning_suppressed_p (org_x, OPT_Wnonnull_compare))
- suppress_warning (x, OPT_Wnonnull_compare);
- }
- /* Otherwise give up on optimizing these, let GIMPLE folders
- optimize those later on. */
- else if (op0 != TREE_OPERAND (org_x, 0)
- || op1 != TREE_OPERAND (org_x, 1))
- {
- x = build2_loc (loc, code, TREE_TYPE (org_x), op0, op1);
- if (warn_nonnull_compare
- && warning_suppressed_p (org_x, OPT_Wnonnull_compare))
- suppress_warning (x, OPT_Wnonnull_compare);
- }
- else
- x = org_x;
- }
-
- break;
-
- case VEC_COND_EXPR:
- case COND_EXPR:
- loc = EXPR_LOCATION (x);
- op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
- op1 = cp_fold (TREE_OPERAND (x, 1));
- op2 = cp_fold (TREE_OPERAND (x, 2));
-
- if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
- {
- warning_sentinel s (warn_int_in_bool_context);
- if (!VOID_TYPE_P (TREE_TYPE (op1)))
- op1 = cp_truthvalue_conversion (op1, tf_warning_or_error);
- if (!VOID_TYPE_P (TREE_TYPE (op2)))
- op2 = cp_truthvalue_conversion (op2, tf_warning_or_error);
- }
- else if (VOID_TYPE_P (TREE_TYPE (x)))
- {
- if (TREE_CODE (op0) == INTEGER_CST)
- {
- /* If the condition is constant, fold can fold away
- the COND_EXPR. If some statement-level uses of COND_EXPR
- have one of the branches NULL, avoid folding crash. */
- if (!op1)
- op1 = build_empty_stmt (loc);
- if (!op2)
- op2 = build_empty_stmt (loc);
- }
- else
- {
- /* Otherwise, don't bother folding a void condition, since
- it can't produce a constant value. */
- if (op0 != TREE_OPERAND (x, 0)
- || op1 != TREE_OPERAND (x, 1)
- || op2 != TREE_OPERAND (x, 2))
- x = build3_loc (loc, code, TREE_TYPE (x), op0, op1, op2);
- break;
- }
- }
-
- if (op0 != TREE_OPERAND (x, 0)
- || op1 != TREE_OPERAND (x, 1)
- || op2 != TREE_OPERAND (x, 2))
- {
- if (op0 == error_mark_node
- || op1 == error_mark_node
- || op2 == error_mark_node)
- x = error_mark_node;
- else
- x = fold_build3_loc (loc, code, TREE_TYPE (x), op0, op1, op2);
- }
- else
- x = fold (x);
-
- /* A COND_EXPR might have incompatible types in branches if one or both
- arms are bitfields. If folding exposed such a branch, fix it up. */
- if (TREE_CODE (x) != code
- && x != error_mark_node
- && !useless_type_conversion_p (TREE_TYPE (org_x), TREE_TYPE (x)))
- x = fold_convert (TREE_TYPE (org_x), x);
-
- break;
-
- case CALL_EXPR:
- {
- int sv = optimize, nw = sv;
- tree callee = get_callee_fndecl (x);
-
- /* Some built-in function calls will be evaluated at compile-time in
- fold (). Set optimize to 1 when folding __builtin_constant_p inside
- a constexpr function so that fold_builtin_1 doesn't fold it to 0. */
- if (callee && fndecl_built_in_p (callee) && !optimize
- && DECL_IS_BUILTIN_CONSTANT_P (callee)
- && current_function_decl
- && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
- nw = 1;
-
- if (callee && fndecl_built_in_p (callee, BUILT_IN_FRONTEND))
- {
- switch (DECL_FE_FUNCTION_CODE (callee))
- {
- /* Defer folding __builtin_is_constant_evaluated. */
- case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
- break;
- case CP_BUILT_IN_SOURCE_LOCATION:
- x = fold_builtin_source_location (EXPR_LOCATION (x));
- break;
- case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
- x = fold_builtin_is_corresponding_member
- (EXPR_LOCATION (x), call_expr_nargs (x),
- &CALL_EXPR_ARG (x, 0));
- break;
- case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
- x = fold_builtin_is_pointer_inverconvertible_with_class
- (EXPR_LOCATION (x), call_expr_nargs (x),
- &CALL_EXPR_ARG (x, 0));
- break;
- default:
- break;
- }
- break;
- }
-
- if (callee
- && fndecl_built_in_p (callee, CP_BUILT_IN_SOURCE_LOCATION,
- BUILT_IN_FRONTEND))
- {
- x = fold_builtin_source_location (EXPR_LOCATION (x));
- break;
- }
-
- bool changed = false;
- int m = call_expr_nargs (x);
- for (int i = 0; i < m; i++)
- {
- r = cp_fold (CALL_EXPR_ARG (x, i));
- if (r != CALL_EXPR_ARG (x, i))
- {
- if (r == error_mark_node)
- {
- x = error_mark_node;
- break;
- }
- if (!changed)
- x = copy_node (x);
- CALL_EXPR_ARG (x, i) = r;
- changed = true;
- }
- }
- if (x == error_mark_node)
- break;
-
- optimize = nw;
- r = fold (x);
- optimize = sv;
-
- if (TREE_CODE (r) != CALL_EXPR)
- {
- x = cp_fold (r);
- break;
- }
-
- optimize = nw;
-
- /* Invoke maybe_constant_value for functions declared
- constexpr and not called with AGGR_INIT_EXPRs.
- TODO:
- Do constexpr expansion of expressions where the call itself is not
- constant, but the call followed by an INDIRECT_REF is. */
- if (callee && DECL_DECLARED_CONSTEXPR_P (callee)
- && !flag_no_inline)
- r = maybe_constant_value (x);
- optimize = sv;
-
- if (TREE_CODE (r) != CALL_EXPR)
- {
- if (DECL_CONSTRUCTOR_P (callee))
- {
- loc = EXPR_LOCATION (x);
- tree s = build_fold_indirect_ref_loc (loc,
- CALL_EXPR_ARG (x, 0));
- r = build2_loc (loc, INIT_EXPR, TREE_TYPE (s), s, r);
- }
- x = r;
- break;
- }
-
- break;
- }
-
- case CONSTRUCTOR:
- {
- unsigned i;
- constructor_elt *p;
- vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (x);
- vec<constructor_elt, va_gc> *nelts = NULL;
- FOR_EACH_VEC_SAFE_ELT (elts, i, p)
- {
- tree op = cp_fold (p->value);
- if (op != p->value)
- {
- if (op == error_mark_node)
- {
- x = error_mark_node;
- vec_free (nelts);
- break;
- }
- if (nelts == NULL)
- nelts = elts->copy ();
- (*nelts)[i].value = op;
- }
- }
- if (nelts)
- {
- x = build_constructor (TREE_TYPE (x), nelts);
- CONSTRUCTOR_PLACEHOLDER_BOUNDARY (x)
- = CONSTRUCTOR_PLACEHOLDER_BOUNDARY (org_x);
- }
- if (VECTOR_TYPE_P (TREE_TYPE (x)))
- x = fold (x);
- break;
- }
- case TREE_VEC:
- {
- bool changed = false;
- int n = TREE_VEC_LENGTH (x);
-
- for (int i = 0; i < n; i++)
- {
- tree op = cp_fold (TREE_VEC_ELT (x, i));
- if (op != TREE_VEC_ELT (x, i))
- {
- if (!changed)
- x = copy_node (x);
- TREE_VEC_ELT (x, i) = op;
- changed = true;
- }
- }
- }
-
- break;
-
- case ARRAY_REF:
- case ARRAY_RANGE_REF:
-
- loc = EXPR_LOCATION (x);
- op0 = cp_fold (TREE_OPERAND (x, 0));
- op1 = cp_fold (TREE_OPERAND (x, 1));
- op2 = cp_fold (TREE_OPERAND (x, 2));
- op3 = cp_fold (TREE_OPERAND (x, 3));
-
- if (op0 != TREE_OPERAND (x, 0)
- || op1 != TREE_OPERAND (x, 1)
- || op2 != TREE_OPERAND (x, 2)
- || op3 != TREE_OPERAND (x, 3))
- {
- if (op0 == error_mark_node
- || op1 == error_mark_node
- || op2 == error_mark_node
- || op3 == error_mark_node)
- x = error_mark_node;
- else
- {
- x = build4_loc (loc, code, TREE_TYPE (x), op0, op1, op2, op3);
- TREE_READONLY (x) = TREE_READONLY (org_x);
- TREE_SIDE_EFFECTS (x) = TREE_SIDE_EFFECTS (org_x);
- TREE_THIS_VOLATILE (x) = TREE_THIS_VOLATILE (org_x);
- }
- }
-
- x = fold (x);
- break;
-
- case SAVE_EXPR:
- /* A SAVE_EXPR might contain e.g. (0 * i) + (0 * j), which, after
- folding, evaluates to an invariant. In that case no need to wrap
- this folded tree with a SAVE_EXPR. */
- r = cp_fold (TREE_OPERAND (x, 0));
- if (tree_invariant_p (r))
- x = r;
- break;
-
- case REQUIRES_EXPR:
- x = evaluate_requires_expr (x);
- break;
-
- default:
- return org_x;
- }
-
- if (EXPR_P (x) && TREE_CODE (x) == code)
- {
- TREE_THIS_VOLATILE (x) = TREE_THIS_VOLATILE (org_x);
- copy_warning (x, org_x);
- }
-
- if (!c.evaluation_restricted_p ())
- {
- fold_cache->put (org_x, x);
- /* Prevent that we try to fold an already folded result again. */
- if (x != org_x)
- fold_cache->put (x, x);
- }
-
- return x;
-}
-
-/* Look up either "hot" or "cold" in attribute list LIST. */
-
-tree
-lookup_hotness_attribute (tree list)
-{
- for (; list; list = TREE_CHAIN (list))
- {
- tree name = get_attribute_name (list);
- if (is_attribute_p ("hot", name)
- || is_attribute_p ("cold", name)
- || is_attribute_p ("likely", name)
- || is_attribute_p ("unlikely", name))
- break;
- }
- return list;
-}
-
-/* Remove both "hot" and "cold" attributes from LIST. */
-
-static tree
-remove_hotness_attribute (tree list)
-{
- list = remove_attribute ("hot", list);
- list = remove_attribute ("cold", list);
- list = remove_attribute ("likely", list);
- list = remove_attribute ("unlikely", list);
- return list;
-}
-
-/* If [[likely]] or [[unlikely]] appear on this statement, turn it into a
- PREDICT_EXPR. */
-
-tree
-process_stmt_hotness_attribute (tree std_attrs, location_t attrs_loc)
-{
- if (std_attrs == error_mark_node)
- return std_attrs;
- if (tree attr = lookup_hotness_attribute (std_attrs))
- {
- tree name = get_attribute_name (attr);
- bool hot = (is_attribute_p ("hot", name)
- || is_attribute_p ("likely", name));
- tree pred = build_predict_expr (hot ? PRED_HOT_LABEL : PRED_COLD_LABEL,
- hot ? TAKEN : NOT_TAKEN);
- SET_EXPR_LOCATION (pred, attrs_loc);
- add_stmt (pred);
- if (tree other = lookup_hotness_attribute (TREE_CHAIN (attr)))
- warning (OPT_Wattributes, "ignoring attribute %qE after earlier %qE",
- get_attribute_name (other), name);
- std_attrs = remove_hotness_attribute (std_attrs);
- }
- return std_attrs;
-}
-
-/* Helper of fold_builtin_source_location, return the
- std::source_location::__impl type after performing verification
- on it. LOC is used for reporting any errors. */
-
-static tree
-get_source_location_impl_type (location_t loc)
-{
- tree name = get_identifier ("source_location");
- tree decl = lookup_qualified_name (std_node, name);
- if (TREE_CODE (decl) != TYPE_DECL)
- {
- auto_diagnostic_group d;
- if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
- qualified_name_lookup_error (std_node, name, decl, loc);
- else
- error_at (loc, "%qD is not a type", decl);
- return error_mark_node;
- }
- name = get_identifier ("__impl");
- tree type = TREE_TYPE (decl);
- decl = lookup_qualified_name (type, name);
- if (TREE_CODE (decl) != TYPE_DECL)
- {
- auto_diagnostic_group d;
- if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
- qualified_name_lookup_error (type, name, decl, loc);
- else
- error_at (loc, "%qD is not a type", decl);
- return error_mark_node;
- }
- type = TREE_TYPE (decl);
- if (TREE_CODE (type) != RECORD_TYPE)
- {
- error_at (loc, "%qD is not a class type", decl);
- return error_mark_node;
- }
-
- int cnt = 0;
- for (tree field = TYPE_FIELDS (type);
- (field = next_initializable_field (field)) != NULL_TREE;
- field = DECL_CHAIN (field))
- {
- if (DECL_NAME (field) != NULL_TREE)
- {
- const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
- if (strcmp (n, "_M_file_name") == 0
- || strcmp (n, "_M_function_name") == 0)
- {
- if (TREE_TYPE (field) != const_string_type_node)
- {
- error_at (loc, "%qD does not have %<const char *%> type",
- field);
- return error_mark_node;
- }
- cnt++;
- continue;
- }
- else if (strcmp (n, "_M_line") == 0 || strcmp (n, "_M_column") == 0)
- {
- if (TREE_CODE (TREE_TYPE (field)) != INTEGER_TYPE)
- {
- error_at (loc, "%qD does not have integral type", field);
- return error_mark_node;
- }
- cnt++;
- continue;
- }
- }
- cnt = 0;
- break;
- }
- if (cnt != 4)
- {
- error_at (loc, "%<std::source_location::__impl%> does not contain only "
- "non-static data members %<_M_file_name%>, "
- "%<_M_function_name%>, %<_M_line%> and %<_M_column%>");
- return error_mark_node;
- }
- return build_qualified_type (type, TYPE_QUAL_CONST);
-}
-
-/* Type for source_location_table hash_set. */
-struct GTY((for_user)) source_location_table_entry {
- location_t loc;
- unsigned uid;
- tree var;
-};
-
-/* Traits class for function start hash maps below. */
-
-struct source_location_table_entry_hash
- : ggc_remove <source_location_table_entry>
-{
- typedef source_location_table_entry value_type;
- typedef source_location_table_entry compare_type;
-
- static hashval_t
- hash (const source_location_table_entry &ref)
- {
- inchash::hash hstate (0);
- hstate.add_int (ref.loc);
- hstate.add_int (ref.uid);
- return hstate.end ();
- }
-
- static bool
- equal (const source_location_table_entry &ref1,
- const source_location_table_entry &ref2)
- {
- return ref1.loc == ref2.loc && ref1.uid == ref2.uid;
- }
-
- static void
- mark_deleted (source_location_table_entry &ref)
- {
- ref.loc = UNKNOWN_LOCATION;
- ref.uid = -1U;
- ref.var = NULL_TREE;
- }
-
- static const bool empty_zero_p = true;
-
- static void
- mark_empty (source_location_table_entry &ref)
- {
- ref.loc = UNKNOWN_LOCATION;
- ref.uid = 0;
- ref.var = NULL_TREE;
- }
-
- static bool
- is_deleted (const source_location_table_entry &ref)
- {
- return (ref.loc == UNKNOWN_LOCATION
- && ref.uid == -1U
- && ref.var == NULL_TREE);
- }
-
- static bool
- is_empty (const source_location_table_entry &ref)
- {
- return (ref.loc == UNKNOWN_LOCATION
- && ref.uid == 0
- && ref.var == NULL_TREE);
- }
-
- static void
- pch_nx (source_location_table_entry &p)
- {
- extern void gt_pch_nx (source_location_table_entry &);
- gt_pch_nx (p);
- }
-
- static void
- pch_nx (source_location_table_entry &p, gt_pointer_operator op, void *cookie)
- {
- extern void gt_pch_nx (source_location_table_entry *, gt_pointer_operator,
- void *);
- gt_pch_nx (&p, op, cookie);
- }
-};
-
-static GTY(()) hash_table <source_location_table_entry_hash>
- *source_location_table;
-static GTY(()) unsigned int source_location_id;
-
-/* Fold __builtin_source_location () call. LOC is the location
- of the call. */
-
-tree
-fold_builtin_source_location (location_t loc)
-{
- if (source_location_impl == NULL_TREE)
- {
- auto_diagnostic_group d;
- source_location_impl = get_source_location_impl_type (loc);
- if (source_location_impl == error_mark_node)
- inform (loc, "evaluating %qs", "__builtin_source_location");
- }
- if (source_location_impl == error_mark_node)
- return build_zero_cst (const_ptr_type_node);
- if (source_location_table == NULL)
- source_location_table
- = hash_table <source_location_table_entry_hash>::create_ggc (64);
- const line_map_ordinary *map;
- source_location_table_entry entry;
- entry.loc
- = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
- &map);
- entry.uid = current_function_decl ? DECL_UID (current_function_decl) : -1;
- entry.var = error_mark_node;
- source_location_table_entry *entryp
- = source_location_table->find_slot (entry, INSERT);
- tree var;
- if (entryp->var)
- var = entryp->var;
- else
- {
- char tmp_name[32];
- ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", source_location_id++);
- var = build_decl (loc, VAR_DECL, get_identifier (tmp_name),
- source_location_impl);
- TREE_STATIC (var) = 1;
- TREE_PUBLIC (var) = 0;
- DECL_ARTIFICIAL (var) = 1;
- DECL_IGNORED_P (var) = 1;
- DECL_EXTERNAL (var) = 0;
- DECL_DECLARED_CONSTEXPR_P (var) = 1;
- DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1;
- layout_decl (var, 0);
-
- vec<constructor_elt, va_gc> *v = NULL;
- vec_alloc (v, 4);
- for (tree field = TYPE_FIELDS (source_location_impl);
- (field = next_initializable_field (field)) != NULL_TREE;
- field = DECL_CHAIN (field))
- {
- const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
- tree val = NULL_TREE;
- if (strcmp (n, "_M_file_name") == 0)
- {
- if (const char *fname = LOCATION_FILE (loc))
- {
- fname = remap_macro_filename (fname);
- val = build_string_literal (strlen (fname) + 1, fname);
- }
- else
- val = build_string_literal (1, "");
- }
- else if (strcmp (n, "_M_function_name") == 0)
- {
- const char *name = "";
-
- if (current_function_decl)
- name = cxx_printable_name (current_function_decl, 2);
-
- val = build_string_literal (strlen (name) + 1, name);
- }
- else if (strcmp (n, "_M_line") == 0)
- val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc));
- else if (strcmp (n, "_M_column") == 0)
- val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc));
- else
- gcc_unreachable ();
- CONSTRUCTOR_APPEND_ELT (v, field, val);
- }
-
- tree ctor = build_constructor (source_location_impl, v);
- TREE_CONSTANT (ctor) = 1;
- TREE_STATIC (ctor) = 1;
- DECL_INITIAL (var) = ctor;
- varpool_node::finalize_decl (var);
- *entryp = entry;
- entryp->var = var;
- }
-
- return build_fold_addr_expr_with_type_loc (loc, var, const_ptr_type_node);
-}
-
-#include "gt-cp-cp-gimplify.h"