From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 2051A3857817; Mon, 29 Aug 2022 15:35:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2051A3857817 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1661787316; bh=3KIWXIdH3end0gb5YwDeaWVVazuCagRHQQxXQZw8GJk=; h=From:To:Subject:Date:From; b=dBFEWryMX4AN702w5lVbMRfY7/7FlFBActWyIZeLcrhNFCFf0/gWEEfcbj/Ca2sa4 zlN0sJOxCbUrX/Spk6wwY5TG/vRFIdAabDPujKp9AI49Cb4r3A3iO2YExJaoRulKlm h46geKO7mgqKHffNPQhKY7Fxz33fy4ZDavt099YI= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Thomas Schwinge To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] rust constexpr: port over cxx_eval_builtin_function_call(). X-Act-Checkin: gcc X-Git-Author: Faisal Abbas <90.abbasfaisal@gmail.com> X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: 8c2da345e08b313dfd0548064cdb3813a7c86d5a X-Git-Newrev: 533effe0f3f49c144df3e1a918f422d2982d21bf Message-Id: <20220829153516.2051A3857817@sourceware.org> Date: Mon, 29 Aug 2022 15:35:16 +0000 (GMT) List-Id: https://gcc.gnu.org/g:533effe0f3f49c144df3e1a918f422d2982d21bf commit 533effe0f3f49c144df3e1a918f422d2982d21bf Author: Faisal Abbas <90.abbasfaisal@gmail.com> Date: Fri Aug 12 18:58:04 2022 +0100 rust constexpr: port over cxx_eval_builtin_function_call(). It still needs potential_constant_expression_1() which will be continued to be ported over. Signed-off-by: Faisal Abbas <90.abbasfaisal@gmail.com> Diff: --- gcc/rust/backend/rust-constexpr.cc | 318 +++++++++++++++++++++++++++++++++++++ gcc/rust/backend/rust-tree.cc | 96 +++++++++++ gcc/rust/backend/rust-tree.h | 105 ++++++++++++ 3 files changed, 519 insertions(+) diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc index e41d184cf71..190abc96070 100644 --- a/gcc/rust/backend/rust-constexpr.cc +++ b/gcc/rust/backend/rust-constexpr.cc @@ -1084,6 +1084,7 @@ init_subob_ctx (const constexpr_ctx *ctx, constexpr_ctx &new_ctx, tree index, /* There's no well-defined subobject for this index. */ new_ctx.object = NULL_TREE; else + // Faisal: commenting this out as not sure if it's needed and it's huge // new_ctx.object = build_ctor_subob_ref (index, type, ctx->object); ; } @@ -2761,6 +2762,217 @@ rs_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, return binds; } +// forked from gcc/cp/constexpr.cc cxx_eval_builtin_function_call + +/* Attempt to evaluate T which represents a call to a builtin function. + We assume here that all builtin functions evaluate to scalar types + represented by _CST nodes. */ + +static tree +eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, + bool lval, bool *non_constant_p, bool *overflow_p) +{ + const int nargs = call_expr_nargs (t); + tree *args = (tree *) alloca (nargs * sizeof (tree)); + tree new_call; + int i; + + /* Don't fold __builtin_constant_p within a constexpr function. */ + bool bi_const_p = DECL_IS_BUILTIN_CONSTANT_P (fun); + + /* If we aren't requiring a constant expression, defer __builtin_constant_p + in a constexpr function until we have values for the parameters. */ + if (bi_const_p && !ctx->manifestly_const_eval && current_function_decl + && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + { + *non_constant_p = true; + return t; + } + + /* For __builtin_is_constant_evaluated, defer it if not + ctx->manifestly_const_eval (as sometimes we try to constant evaluate + without manifestly_const_eval even expressions or parts thereof which + will later be manifestly const_eval evaluated), otherwise fold it to + true. */ + if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED, + BUILT_IN_FRONTEND)) + { + if (!ctx->manifestly_const_eval) + { + *non_constant_p = true; + return t; + } + return boolean_true_node; + } + + if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND)) + { + temp_override ovr (current_function_decl); + if (ctx->call && ctx->call->fundef) + current_function_decl = ctx->call->fundef->decl; + return fold_builtin_source_location (EXPR_LOCATION (t)); + } + + int strops = 0; + int strret = 0; + if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) + switch (DECL_FUNCTION_CODE (fun)) + { + case BUILT_IN_STRLEN: + case BUILT_IN_STRNLEN: + strops = 1; + break; + case BUILT_IN_MEMCHR: + case BUILT_IN_STRCHR: + case BUILT_IN_STRRCHR: + strops = 1; + strret = 1; + break; + case BUILT_IN_MEMCMP: + case BUILT_IN_STRCMP: + strops = 2; + break; + case BUILT_IN_STRSTR: + strops = 2; + strret = 1; + break; + case BUILT_IN_ASAN_POINTER_COMPARE: + case BUILT_IN_ASAN_POINTER_SUBTRACT: + /* These builtins shall be ignored during constant expression + evaluation. */ + return void_node; + default: + break; + } + + /* Be permissive for arguments to built-ins; __builtin_constant_p should + return constant false for a non-constant argument. */ + constexpr_ctx new_ctx = *ctx; + new_ctx.quiet = true; + for (i = 0; i < nargs; ++i) + { + tree arg = CALL_EXPR_ARG (t, i); + tree oarg = arg; + + /* To handle string built-ins we need to pass ADDR_EXPR since + expand_builtin doesn't know how to look in the values table. */ + bool strop = i < strops; + if (strop) + { + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR) + arg = TREE_OPERAND (arg, 0); + else + strop = false; + } + + /* If builtin_valid_in_constant_expr_p is true, + potential_constant_expression_1 has not recursed into the arguments + of the builtin, verify it here. */ + if (!builtin_valid_in_constant_expr_p (fun) + || potential_constant_expression (arg)) + { + bool dummy1 = false, dummy2 = false; + arg + = eval_constant_expression (&new_ctx, arg, false, &dummy1, &dummy2); + } + + if (bi_const_p) + /* For __builtin_constant_p, fold all expressions with constant values + even if they aren't C++ constant-expressions. */ + arg = cp_fold_rvalue (arg); + else if (strop) + { + if (TREE_CODE (arg) == CONSTRUCTOR) + arg = braced_lists_to_strings (TREE_TYPE (arg), arg); + if (TREE_CODE (arg) == STRING_CST) + arg = build_address (arg); + else + arg = oarg; + } + + args[i] = arg; + } + + bool save_ffbcp = force_folding_builtin_constant_p; + force_folding_builtin_constant_p |= ctx->manifestly_const_eval; + tree save_cur_fn = current_function_decl; + /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION (). */ + if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION) && ctx->call + && ctx->call->fundef) + current_function_decl = ctx->call->fundef->decl; + if (fndecl_built_in_p (fun, + CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS, + BUILT_IN_FRONTEND)) + { + location_t loc = EXPR_LOCATION (t); + if (nargs >= 1) + VERIFY_CONSTANT (args[0]); + new_call + = fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs, + args); + } + else if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CORRESPONDING_MEMBER, + BUILT_IN_FRONTEND)) + { + location_t loc = EXPR_LOCATION (t); + if (nargs >= 2) + { + VERIFY_CONSTANT (args[0]); + VERIFY_CONSTANT (args[1]); + } + new_call = fold_builtin_is_corresponding_member (loc, nargs, args); + } + else + new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t), + CALL_EXPR_FN (t), nargs, args); + current_function_decl = save_cur_fn; + force_folding_builtin_constant_p = save_ffbcp; + if (new_call == NULL) + { + if (!*non_constant_p && !ctx->quiet) + { + /* Do not allow__builtin_unreachable in constexpr function. + The __builtin_unreachable call with BUILTINS_LOCATION + comes from cp_maybe_instrument_return. */ + if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE) + && EXPR_LOCATION (t) == BUILTINS_LOCATION) + error ("% call flows off the end of the function"); + else + { + new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t), + CALL_EXPR_FN (t), nargs, args); + error ("%q+E is not a constant expression", new_call); + } + } + *non_constant_p = true; + return t; + } + + if (!potential_constant_expression (new_call)) + { + if (!*non_constant_p && !ctx->quiet) + error ("%q+E is not a constant expression", new_call); + *non_constant_p = true; + return t; + } + + if (strret) + { + /* memchr returns a pointer into the first argument, but we replaced the + argument above with a STRING_CST; put it back it now. */ + tree op = CALL_EXPR_ARG (t, strret - 1); + STRIP_NOPS (new_call); + if (TREE_CODE (new_call) == POINTER_PLUS_EXPR) + TREE_OPERAND (new_call, 0) = op; + else if (TREE_CODE (new_call) == ADDR_EXPR) + new_call = op; + } + + return eval_constant_expression (&new_ctx, new_call, lval, non_constant_p, + overflow_p); +} + // Subroutine of cxx_eval_constant_expression. // Evaluate the call expression tree T in the context of OLD_CALL expression // evaluation. @@ -2792,6 +3004,10 @@ eval_call_expression (const constexpr_ctx *ctx, tree t, bool lval, return t; } + if (fndecl_built_in_p (fun)) + return eval_builtin_function_call (ctx, t, fun, lval, non_constant_p, + overflow_p); + bool non_constant_args = false; new_call.bindings = rs_bind_parameters_in_call (ctx, t, fun, non_constant_p, overflow_p, @@ -4443,6 +4659,20 @@ is_static_init_expression (tree t) // faisal: just return false for now to make it compile } +/* Like potential_constant_expression, but don't consider possible constexpr + substitution of the current function. That is, PARM_DECL qualifies under + potential_constant_expression, but not here. + + This is basically what you can check when any actual constant values might + be value-dependent. */ + +bool +is_constant_expression (tree t) +{ + // return potential_constant_expression_1 (t, false, true, true, tf_none); + // faisal: just return false for now to make it compile +} + /* Returns true if T is a potential static initializer expression that is not instantiation-dependent. */ @@ -4497,6 +4727,94 @@ maybe_constant_init (tree t, tree decl, bool manifestly_const_eval) return maybe_constant_init_1 (t, decl, true, manifestly_const_eval); } +/* Returns true if T is a potential constant expression that is not + instantiation-dependent, and therefore a candidate for constant folding even + in a template. */ + +bool +is_nondependent_constant_expression (tree t) +{ + return (!type_unknown_p (t) && is_constant_expression (t) + && !instantiation_dependent_expression_p (t)); +} + +// forked from gcc/cp/parser.cc cp_unevaluated_operand + +/* Nonzero if we are parsing an unevaluated operand: an operand to + sizeof, typeof, or alignof. */ +int cp_unevaluated_operand; + +// forked from gcc/cp/constexpr.cc cv_cache + +/* If T is a constant expression, returns its reduced value. + Otherwise, if T does not have TREE_CONSTANT set, returns T. + Otherwise, returns a version of T without TREE_CONSTANT. + MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated + as per P0595. */ + +static GTY ((deletable)) hash_map *cv_cache; + +// forked from gcc/cp/constexpr.cc maybe_constant_value + +tree +maybe_constant_value (tree t, tree decl, bool manifestly_const_eval) +{ + tree r; + + if (!is_nondependent_constant_expression (t)) + { + if (TREE_OVERFLOW_P (t)) + { + t = build_nop (TREE_TYPE (t), t); + TREE_CONSTANT (t) = false; + } + return t; + } + else if (CONSTANT_CLASS_P (t)) + /* No caching or evaluation needed. */ + return t; + + if (manifestly_const_eval) + return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl); + + if (cv_cache == NULL) + cv_cache = hash_map::create_ggc (101); + if (tree *cached = cv_cache->get (t)) + { + r = *cached; + if (r != t) + { + // Faisal: commenting this out as not sure if it's needed and it's + // huge r = break_out_target_exprs (r, /*clear_loc*/true); + protected_set_expr_location (r, EXPR_LOCATION (t)); + } + return r; + } + + /* Don't evaluate an unevaluated operand. */ + if (cp_unevaluated_operand) + return t; + + uid_sensitive_constexpr_evaluation_checker c; + r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl); + gcc_checking_assert ( + r == t || CONVERT_EXPR_P (t) || TREE_CODE (t) == VIEW_CONVERT_EXPR + || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) || !rs_tree_equal (r, t)); + if (!c.evaluation_restricted_p ()) + cv_cache->put (t, r); + return r; +} + +// forked from gcc/cp/constexpr.cc + +bool +potential_constant_expression (tree t) +{ + // return potential_constant_expression_1 (t, false, true, false, tf_none); + // Faisal: return false until we port above call to make the code compile + return false; +} + // #include "gt-rust-rust-constexpr.h" } // namespace Compile diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc index e1e56fc8336..beb4711493d 100644 --- a/gcc/rust/backend/rust-tree.cc +++ b/gcc/rust/backend/rust-tree.cc @@ -5257,6 +5257,8 @@ fold_builtin_is_pointer_inverconvertible_with_class (location_t loc, int nargs, build_zero_cst (TREE_TYPE (arg))); } +// forked from gcc/c-family/c-common.cc registered_builtin_types + /* Used for communication between c_common_type_for_mode and c_register_builtin_type. */ tree registered_builtin_types; @@ -5267,6 +5269,8 @@ tree registered_builtin_types; If the mode is a fixed-point mode, then UNSIGNEDP selects between saturating and nonsaturating types. */ +// forked from gcc/c-family/c-common.cc c_common_type_for_mode + tree c_common_type_for_mode (machine_mode mode, int unsignedp) { @@ -5487,6 +5491,8 @@ c_common_type_for_mode (machine_mode mode, int unsignedp) return NULL_TREE; } +// forked from gcc/cp/semantics.cc finish_underlying_type + /* Implement the __underlying_type keyword: Return the underlying type of TYPE, suitable for use as a type-specifier. */ @@ -5516,6 +5522,8 @@ finish_underlying_type (tree type) return underlying_type; } +// forked from gcc/cp/typeck.cc layout_compatible_type_p + /* Return true if TYPE1 and TYPE2 are layout-compatible types. */ bool @@ -5619,6 +5627,8 @@ layout_compatible_type_p (tree type1, tree type2) return same_type_p (type1, type2); } +// forked from gcc/cp/semnatics.cc is_corresponding_member_union + /* Helper function for is_corresponding_member_aggr. Return true if MEMBERTYPE pointer-to-data-member ARG can be found in anonymous union or structure BASETYPE. */ @@ -5648,6 +5658,8 @@ is_corresponding_member_union (tree basetype, tree membertype, tree arg) return false; } +// forked from gcc/cp/typeck.cc next_common_initial_seqence + /* Helper function for layout_compatible_type_p and is_corresponding_member_aggr. Advance to next members (NULL if no further ones) and return true if those members are still part of @@ -5713,6 +5725,8 @@ next_common_initial_seqence (tree &memb1, tree &memb2) return true; } +// forked from gcc/cp/semantics.cc is_corresponding_member_aggr + /* Helper function for fold_builtin_is_corresponding_member call. Return boolean_false_node if MEMBERTYPE1 BASETYPE1::*ARG1 and MEMBERTYPE2 BASETYPE2::*ARG2 aren't corresponding members, @@ -5833,6 +5847,8 @@ is_corresponding_member_aggr (location_t loc, tree basetype1, tree membertype1, return ret; } +// forked from gcc/cp/call.cc null_member_pointer_value_p + /* Returns true iff T is a null member pointer value (4.11). */ bool @@ -5850,6 +5866,8 @@ null_member_pointer_value_p (tree t) return false; } +// forked from gcc/cp/semantics.cc fold_builtin_is_corresponding_member + /* Fold __builtin_is_corresponding_member call. */ tree @@ -5937,4 +5955,82 @@ fold_builtin_is_corresponding_member (location_t loc, int nargs, tree *args) fold_convert (TREE_TYPE (arg1), arg2))); } +// forked from gcc/cp/tree.cc lvalue_type + +/* The type of ARG when used as an lvalue. */ + +tree +lvalue_type (tree arg) +{ + tree type = TREE_TYPE (arg); + return type; +} + +// forked from gcc/c-family/c-warn.cc lvalue_error + +/* Print an error message for an invalid lvalue. USE says + how the lvalue is being used and so selects the error message. LOC + is the location for the error. */ + +void +lvalue_error (location_t loc, enum lvalue_use use) +{ + switch (use) + { + case lv_assign: + error_at (loc, "lvalue required as left operand of assignment"); + break; + case lv_increment: + error_at (loc, "lvalue required as increment operand"); + break; + case lv_decrement: + error_at (loc, "lvalue required as decrement operand"); + break; + case lv_addressof: + error_at (loc, "lvalue required as unary %<&%> operand"); + break; + case lv_asm: + error_at (loc, "lvalue required in % statement"); + break; + default: + gcc_unreachable (); + } +} + +// forked from gcc/cp/cp--gimplify.cc cp_fold_maybe_rvalue + +/* 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 = 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; +} + +// forked from gcc/cp/cp--gimplify.cc cp_fold_rvalue + +/* Fold expression X which is used as an rvalue. */ + +tree +cp_fold_rvalue (tree x) +{ + return cp_fold_maybe_rvalue (x, true); +} + } // namespace Rust diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h index 57440af632d..524569a985e 100644 --- a/gcc/rust/backend/rust-tree.h +++ b/gcc/rust/backend/rust-tree.h @@ -1346,6 +1346,26 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX]; of the enumeration is completed by finish_enum. */ #define ENUM_UNDERLYING_TYPE(TYPE) TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE)) +/* Nonzero if this type is volatile-qualified. */ +#define CP_TYPE_VOLATILE_P(NODE) \ + ((rs_type_quals (NODE) & TYPE_QUAL_VOLATILE) != 0) + +/* Nonzero means that this type is either complete or being defined, so we + can do lookup in it. */ +#define COMPLETE_OR_OPEN_TYPE_P(NODE) \ + (COMPLETE_TYPE_P (NODE) || (CLASS_TYPE_P (NODE) && TYPE_BEING_DEFINED (NODE))) + +/* Indicates when overload resolution may resolve to a pointer to + member function. [expr.unary.op]/3 */ +#define PTRMEM_OK_P(NODE) \ + TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) + +/* Returns nonzero iff NODE is a declaration for the global function + `main'. */ +#define DECL_MAIN_P(NODE) \ + (DECL_NAME (NODE) != NULL_TREE && MAIN_NAME_P (DECL_NAME (NODE)) \ + && flag_hosted) + #if defined ENABLE_TREE_CHECKING #define LANG_DECL_MIN_CHECK(NODE) \ @@ -1512,6 +1532,77 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX]; // Above macros are copied from gcc/cp/name-lookup.cc +/* Places where an lvalue, or modifiable lvalue, may be required. + Used to select diagnostic messages in lvalue_error and + readonly_error. */ +enum lvalue_use +{ + lv_assign, + lv_increment, + lv_decrement, + lv_addressof, + lv_asm +}; + +/* A class for recording information about access failures (e.g. private + fields), so that we can potentially supply a fix-it hint about + an accessor (from a context in which the constness of the object + is known). */ + +class access_failure_info +{ +public: + access_failure_info () + : m_was_inaccessible (false), m_basetype_path (NULL_TREE), + m_decl (NULL_TREE), m_diag_decl (NULL_TREE) + {} + + void record_access_failure (tree basetype_path, tree decl, tree diag_decl); + + bool was_inaccessible_p () const { return m_was_inaccessible; } + tree get_decl () const { return m_decl; } + tree get_diag_decl () const { return m_diag_decl; } + tree get_any_accessor (bool const_p) const; + void maybe_suggest_accessor (bool const_p) const; + static void add_fixit_hint (rich_location *richloc, tree accessor); + +private: + bool m_was_inaccessible; + tree m_basetype_path; + tree m_decl; + tree m_diag_decl; +}; + +/* The various kinds of access check during parsing. */ +enum deferring_kind +{ + dk_no_deferred = 0, /* Check access immediately */ + dk_deferred = 1, /* Deferred check */ + dk_no_check = 2 /* No access check */ +}; + +/* The representation of a deferred access check. */ + +struct GTY (()) deferred_access_check +{ + /* The base class in which the declaration is referenced. */ + tree binfo; + /* The declaration whose access must be checked. */ + tree decl; + /* The declaration that should be used in the error message. */ + tree diag_decl; + /* The location of this access. */ + location_t loc; +}; + +struct GTY (()) tree_template_info +{ + struct tree_base base; + tree tmpl; + tree args; + vec *deferred_access_checks; +}; + /* The various kinds of lvalues we distinguish. */ enum cp_lvalue_kind_flags { @@ -2819,6 +2910,18 @@ extern bool null_member_pointer_value_p (tree); extern tree fold_builtin_is_corresponding_member (location_t, int, tree *); +extern tree cp_fold_rvalue (tree); + +extern tree +maybe_constant_value (tree, tree = NULL_TREE, bool = false); + +extern tree lvalue_type (tree); + +extern void lvalue_error (location_t, enum lvalue_use); + +extern tree +cp_fold_maybe_rvalue (tree, bool); + // forked from gcc/cp/cp-tree.h enum @@ -3043,6 +3146,8 @@ set_implicit_rvalue_p (tree ot) namespace Compile { extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); + +extern bool potential_constant_expression (tree); } } // namespace Rust