From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 13CC23856DC0; Mon, 29 Aug 2022 15:32:54 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 13CC23856DC0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1661787174; bh=VgMnQ2VB0MLRE6dpWyLGLhVN3q7iVj8exQvQLVJjoIM=; h=From:To:Subject:Date:From; b=Lo3gkjEeUxrPpgbPAZb/2i8A4htOKOtnIEpoZgCwA+H9QP+CNV0fdmBm4P+S9lKTg WS5dYLhczwSKSdLjXgmd4sf+B4Jy/kGwHYVzjIOho6xbjnZXS3NTaC5+0r7GzDIuvU AZuOpc1wRAEA9yuCrWj9IH9GO8D2oADttyNkYeNQ= 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] gccrs const folding port: start porting cxx_eval_array_reference() X-Act-Checkin: gcc X-Git-Author: Faisal Abbas <90.abbasfaisal@gmail.com> X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: a5b583d021a4cbbfafecc28445709afe32cdd1c8 X-Git-Newrev: 7c13a2e5873f5919c00b5f37b6db52af8d0f7300 Message-Id: <20220829153254.13CC23856DC0@sourceware.org> Date: Mon, 29 Aug 2022 15:32:54 +0000 (GMT) List-Id: https://gcc.gnu.org/g:7c13a2e5873f5919c00b5f37b6db52af8d0f7300 commit 7c13a2e5873f5919c00b5f37b6db52af8d0f7300 Author: Faisal Abbas <90.abbasfaisal@gmail.com> Date: Sat Jul 16 17:01:47 2022 +0100 gccrs const folding port: start porting cxx_eval_array_reference() Following functions are ported in this changeset: - array_index_cmp - unshare_constructor - find_array_ctor_elt - reduced_constant_expression_p - verify_constant - diag_array_subscript - get_array_or_vector_nelts - eval_and_check_array_index - extract_string_elt - free_constructor - cv_unqualified - make_tree_vector - release_tree_vector - vec_safe_push Following structs, classes and enums are ported in this changeset: - releasing_vec Signed-off-by: Faisal Abbas <90.abbasfaisal@gmail.com> Diff: --- gcc/rust/backend/rust-constexpr.cc | 549 +++++++++++++++++++++++++++++++++++-- gcc/rust/backend/rust-tree.cc | 68 +++++ gcc/rust/backend/rust-tree.h | 53 ++++ 3 files changed, 653 insertions(+), 17 deletions(-) diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc index 53c6ef6a668..8fb378fbda5 100644 --- a/gcc/rust/backend/rust-constexpr.cc +++ b/gcc/rust/backend/rust-constexpr.cc @@ -25,10 +25,27 @@ #include "print-tree.h" #include "gimplify.h" #include "tree-iterator.h" +#include "varasm.h" + +#define VERIFY_CONSTANT(X) \ + do \ + { \ + if (verify_constant ((X), ctx->quiet, non_constant_p, overflow_p)) \ + return t; \ + } \ + while (0) namespace Rust { namespace Compile { +static bool +verify_constant (tree, bool, bool *, bool *); + +static HOST_WIDE_INT +find_array_ctor_elt (tree ary, tree dindex, bool insert = false); +static int +array_index_cmp (tree key, tree index); + struct constexpr_global_ctx { HOST_WIDE_INT constexpr_ops_count; @@ -38,7 +55,19 @@ struct constexpr_global_ctx struct constexpr_ctx { + /* The part of the context that needs to be unique to the whole + cxx_eval_outermost_constant_expr invocation. */ constexpr_global_ctx *global; + + /* Whether we should error on a non-constant expression or fail quietly. + This flag needs to be here, but some of the others could move to global + if they get larger than a word. */ + bool quiet; + /* The object we're building the CONSTRUCTOR for. */ + tree object; + /* The CONSTRUCTOR we're currently building up for an aggregate + initializer. */ + tree ctor; }; static tree @@ -51,19 +80,20 @@ static void non_const_var_error (location_t loc, tree r); static tree -constexpr_expression (const constexpr_ctx *ctx, tree); +constexpr_expression (const constexpr_ctx *ctx, tree, bool, bool *, bool *, + tree * = NULL); static tree constexpr_fn_retval (const constexpr_ctx *ctx, tree r); static tree -eval_store_expression (const constexpr_ctx *ctx, tree r); +eval_store_expression (const constexpr_ctx *ctx, tree r, bool, bool *, bool *); static tree eval_call_expression (const constexpr_ctx *ctx, tree r); static tree -eval_binary_expression (const constexpr_ctx *ctx, tree r); +eval_binary_expression (const constexpr_ctx *ctx, tree r, bool, bool *, bool *); static tree get_function_named_in_call (tree t); @@ -72,15 +102,20 @@ tree fold_expr (tree expr) { constexpr_global_ctx global_ctx; - constexpr_ctx ctx = {&global_ctx}; + constexpr_ctx ctx = {&global_ctx, false}; + bool non_constant_p = false; + bool overflow_p = false; - tree folded = constexpr_expression (&ctx, expr); + tree folded + = constexpr_expression (&ctx, expr, false, &non_constant_p, &overflow_p); rust_assert (folded != NULL_TREE); return folded; } static tree -constexpr_expression (const constexpr_ctx *ctx, tree t) +constexpr_expression (const constexpr_ctx *ctx, tree t, bool lval, + bool *non_constant_p, bool *overflow_p, + tree *jump_target /* = NULL */) { location_t loc = EXPR_LOCATION (t); @@ -165,7 +200,7 @@ constexpr_expression (const constexpr_ctx *ctx, tree t) case LTGT_EXPR: case RANGE_EXPR: case COMPLEX_EXPR: - r = eval_binary_expression (ctx, t); + r = eval_binary_expression (ctx, t, false, non_constant_p, overflow_p); break; case CALL_EXPR: @@ -174,11 +209,12 @@ constexpr_expression (const constexpr_ctx *ctx, tree t) case RETURN_EXPR: rust_assert (TREE_OPERAND (t, 0) != NULL_TREE); - r = constexpr_expression (ctx, TREE_OPERAND (t, 0)); + r = constexpr_expression (ctx, TREE_OPERAND (t, 0), false, non_constant_p, + overflow_p); break; case MODIFY_EXPR: - r = eval_store_expression (ctx, t); + r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p); break; default: @@ -189,7 +225,8 @@ constexpr_expression (const constexpr_ctx *ctx, tree t) } static tree -eval_store_expression (const constexpr_ctx *ctx, tree t) +eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval, + bool *non_constant_p, bool *overflow_p) { tree init = TREE_OPERAND (t, 1); if (TREE_CLOBBER_P (init)) @@ -219,7 +256,8 @@ eval_store_expression (const constexpr_ctx *ctx, tree t) object = probe; else { - probe = constexpr_expression (ctx, probe); + probe = constexpr_expression (ctx, probe, lval, non_constant_p, + overflow_p); evaluated = true; } break; @@ -232,14 +270,15 @@ eval_store_expression (const constexpr_ctx *ctx, tree t) /* Subroutine of cxx_eval_constant_expression. Like cxx_eval_unary_expression, except for binary expressions. */ static tree -eval_binary_expression (const constexpr_ctx *ctx, tree t) +eval_binary_expression (const constexpr_ctx *ctx, tree t, bool lval, + bool *non_constant_p, bool *overflow_p) { tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; - lhs = constexpr_expression (ctx, orig_lhs); - rhs = constexpr_expression (ctx, orig_rhs); + lhs = constexpr_expression (ctx, orig_lhs, lval, non_constant_p, overflow_p); + rhs = constexpr_expression (ctx, orig_rhs, lval, non_constant_p, overflow_p); location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -285,9 +324,12 @@ constexpr_fn_retval (const constexpr_ctx *ctx, tree body) return expr; } - case RETURN_EXPR: - return constexpr_expression (ctx, body); - + case RETURN_EXPR: { + bool non_constant_p = false; + bool overflow_p = false; + return constexpr_expression (ctx, body, false, &non_constant_p, + &overflow_p); + } case DECL_EXPR: { tree decl = DECL_EXPR_DECL (body); if (TREE_CODE (decl) == USING_DECL @@ -437,5 +479,478 @@ var_in_maybe_constexpr_fn (tree t) return (DECL_FUNCTION_SCOPE_P (t) && maybe_constexpr_fn (DECL_CONTEXT (t))); } +// forked from gcc/cp/constexpr.cc array_index_cmp + +/* Returns less than, equal to, or greater than zero if KEY is found to be + less than, to match, or to be greater than the constructor_elt's INDEX. */ + +static int +array_index_cmp (tree key, tree index) +{ + gcc_assert (TREE_CODE (key) == INTEGER_CST); + + switch (TREE_CODE (index)) + { + case INTEGER_CST: + return tree_int_cst_compare (key, index); + case RANGE_EXPR: { + tree lo = TREE_OPERAND (index, 0); + tree hi = TREE_OPERAND (index, 1); + if (tree_int_cst_lt (key, lo)) + return -1; + else if (tree_int_cst_lt (hi, key)) + return 1; + else + return 0; + } + default: + gcc_unreachable (); + } +} + +// forked from gcc/cp/constexpr.cc unshare_constructor + +/* If T is a CONSTRUCTOR, return an unshared copy of T and any + sub-CONSTRUCTORs. Otherwise return T. + + We use this whenever we initialize an object as a whole, whether it's a + parameter, a local variable, or a subobject, so that subsequent + modifications don't affect other places where it was used. */ + +tree +unshare_constructor (tree t MEM_STAT_DECL) +{ + if (!t || TREE_CODE (t) != CONSTRUCTOR) + return t; + auto_vec ptrs; + ptrs.safe_push (&t); + while (!ptrs.is_empty ()) + { + tree *p = ptrs.pop (); + tree n = copy_node (*p PASS_MEM_STAT); + CONSTRUCTOR_ELTS (n) + = vec_safe_copy (CONSTRUCTOR_ELTS (*p) PASS_MEM_STAT); + *p = n; + vec *v = CONSTRUCTOR_ELTS (n); + constructor_elt *ce; + for (HOST_WIDE_INT i = 0; vec_safe_iterate (v, i, &ce); ++i) + if (ce->value && TREE_CODE (ce->value) == CONSTRUCTOR) + ptrs.safe_push (&ce->value); + } + return t; +} + +// forked from gcc/cp/constexpr.cc find_array_ctor_elt + +/* Returns the index of the constructor_elt of ARY which matches DINDEX, or -1 + if none. If INSERT is true, insert a matching element rather than fail. */ + +static HOST_WIDE_INT +find_array_ctor_elt (tree ary, tree dindex, bool insert) +{ + if (tree_int_cst_sgn (dindex) < 0) + return -1; + + unsigned HOST_WIDE_INT i = tree_to_uhwi (dindex); + vec *elts = CONSTRUCTOR_ELTS (ary); + unsigned HOST_WIDE_INT len = vec_safe_length (elts); + + unsigned HOST_WIDE_INT end = len; + unsigned HOST_WIDE_INT begin = 0; + + /* If the last element of the CONSTRUCTOR has its own index, we can assume + that the same is true of the other elements and index directly. */ + if (end > 0) + { + tree cindex = (*elts)[end - 1].index; + if (cindex == NULL_TREE) + { + /* Verify that if the last index is missing, all indexes + are missing. */ + if (flag_checking) + for (unsigned int j = 0; j < len - 1; ++j) + gcc_assert ((*elts)[j].index == NULL_TREE); + if (i < end) + return i; + else + { + begin = end; + if (i == end) + /* If the element is to be added right at the end, + make sure it is added with cleared index too. */ + dindex = NULL_TREE; + else if (insert) + /* Otherwise, in order not to break the assumption + that CONSTRUCTOR either has all indexes or none, + we need to add indexes to all elements. */ + for (unsigned int j = 0; j < len; ++j) + (*elts)[j].index = build_int_cst (TREE_TYPE (dindex), j); + } + } + else if (TREE_CODE (cindex) == INTEGER_CST + && compare_tree_int (cindex, end - 1) == 0) + { + if (i < end) + return i; + else + begin = end; + } + } + + /* Otherwise, find a matching index by means of a binary search. */ + while (begin != end) + { + unsigned HOST_WIDE_INT middle = (begin + end) / 2; + constructor_elt &elt = (*elts)[middle]; + tree idx = elt.index; + + int cmp = array_index_cmp (dindex, idx); + if (cmp < 0) + end = middle; + else if (cmp > 0) + begin = middle + 1; + else + { + if (insert && TREE_CODE (idx) == RANGE_EXPR) + { + /* We need to split the range. */ + constructor_elt e; + tree lo = TREE_OPERAND (idx, 0); + tree hi = TREE_OPERAND (idx, 1); + tree value = elt.value; + dindex = fold_convert (sizetype, dindex); + if (tree_int_cst_lt (lo, dindex)) + { + /* There are still some lower elts; shorten the range. */ + tree new_hi + = int_const_binop (MINUS_EXPR, dindex, size_one_node); + if (tree_int_cst_equal (lo, new_hi)) + /* Only one element left, no longer a range. */ + elt.index = lo; + else + TREE_OPERAND (idx, 1) = new_hi; + /* Append the element we want to insert. */ + ++middle; + e.index = dindex; + e.value = unshare_constructor (value); + vec_safe_insert (CONSTRUCTOR_ELTS (ary), middle, e); + } + else + /* No lower elts, the range elt is now ours. */ + elt.index = dindex; + + if (tree_int_cst_lt (dindex, hi)) + { + /* There are still some higher elts; append a range. */ + tree new_lo + = int_const_binop (PLUS_EXPR, dindex, size_one_node); + if (tree_int_cst_equal (new_lo, hi)) + e.index = hi; + else + e.index = build2 (RANGE_EXPR, sizetype, new_lo, hi); + e.value = unshare_constructor (value); + vec_safe_insert (CONSTRUCTOR_ELTS (ary), middle + 1, e); + } + } + return middle; + } + } + + if (insert) + { + constructor_elt e = {dindex, NULL_TREE}; + vec_safe_insert (CONSTRUCTOR_ELTS (ary), end, e); + return end; + } + + return -1; +} + +// forked from gcc/cp/constexpr.cc reduced_constant_expression_p + +/* Return true if T is a valid constant initializer. If a CONSTRUCTOR + initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be + cleared. + FIXME speed this up, it's taking 16% of compile time on sieve testcase. */ + +bool +reduced_constant_expression_p (tree t) +{ + if (t == NULL_TREE) + return false; + + switch (TREE_CODE (t)) + { + case PTRMEM_CST: + /* Even if we can't lower this yet, it's constant. */ + return true; + + case CONSTRUCTOR: + /* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */ + tree field; + if (CONSTRUCTOR_NO_CLEARING (t)) + { + if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE) + /* An initialized vector would have a VECTOR_CST. */ + return false; + else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) + { + /* There must be a valid constant initializer at every array + index. */ + tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t))); + tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t))); + tree cursor = min; + for (auto &e : CONSTRUCTOR_ELTS (t)) + { + if (!reduced_constant_expression_p (e.value)) + return false; + if (array_index_cmp (cursor, e.index) != 0) + return false; + if (TREE_CODE (e.index) == RANGE_EXPR) + cursor = TREE_OPERAND (e.index, 1); + cursor = int_const_binop (PLUS_EXPR, cursor, size_one_node); + } + if (find_array_ctor_elt (t, max) == -1) + return false; + goto ok; + } + else if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE) + { + if (CONSTRUCTOR_NELTS (t) == 0) + /* An initialized union has a constructor element. */ + return false; + /* And it only initializes one member. */ + field = NULL_TREE; + } + else + field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t))); + } + else + field = NULL_TREE; + for (auto &e : CONSTRUCTOR_ELTS (t)) + { + /* If VAL is null, we're in the middle of initializing this + element. */ + if (!reduced_constant_expression_p (e.value)) + return false; + /* Empty class field may or may not have an initializer. */ + for (; field && e.index != field; + field = next_initializable_field (DECL_CHAIN (field))) + if (!is_really_empty_class (TREE_TYPE (field), + /*ignore_vptr*/ false)) + return false; + if (field) + field = next_initializable_field (DECL_CHAIN (field)); + } + /* There could be a non-empty field at the end. */ + for (; field; field = next_initializable_field (DECL_CHAIN (field))) + if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/ false)) + return false; + ok: + if (CONSTRUCTOR_NO_CLEARING (t)) + /* All the fields are initialized. */ + CONSTRUCTOR_NO_CLEARING (t) = false; + return true; + + default: + /* FIXME are we calling this too much? */ + return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE; + } +} + +// forked from gcc/cp/constexpr.cc verify_constant + +/* Some expressions may have constant operands but are not constant + themselves, such as 1/0. Call this function to check for that + condition. + + We only call this in places that require an arithmetic constant, not in + places where we might have a non-constant expression that can be a + component of a constant expression, such as the address of a constexpr + variable that might be dereferenced later. */ + +static bool +verify_constant (tree t, bool allow_non_constant, bool *non_constant_p, + bool *overflow_p) +{ + if (!*non_constant_p && !reduced_constant_expression_p (t) && t != void_node) + { + if (!allow_non_constant) + error ("%q+E is not a constant expression", t); + *non_constant_p = true; + } + if (TREE_OVERFLOW_P (t)) + { + if (!allow_non_constant) + { + permerror (input_location, "overflow in constant expression"); + /* If we're being permissive (and are in an enforcing + context), ignore the overflow. */ + if (flag_permissive) + return *non_constant_p; + } + *overflow_p = true; + } + return *non_constant_p; +} + +// forked in gcc/cp/constexpr.cc diag_array_subscript + +/* Under the control of CTX, issue a detailed diagnostic for + an out-of-bounds subscript INDEX into the expression ARRAY. */ + +static void +diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, + tree index) +{ + if (!ctx->quiet) + { + tree arraytype = TREE_TYPE (array); + + /* Convert the unsigned array subscript to a signed integer to avoid + printing huge numbers for small negative values. */ + tree sidx = fold_convert (ssizetype, index); + STRIP_ANY_LOCATION_WRAPPER (array); + if (DECL_P (array)) + { + if (TYPE_DOMAIN (arraytype)) + error_at (loc, + "array subscript value %qE is outside the bounds " + "of array %qD of type %qT", + sidx, array, arraytype); + else + error_at (loc, + "nonzero array subscript %qE is used with array %qD of " + "type %qT with unknown bounds", + sidx, array, arraytype); + inform (DECL_SOURCE_LOCATION (array), "declared here"); + } + else if (TYPE_DOMAIN (arraytype)) + error_at (loc, + "array subscript value %qE is outside the bounds " + "of array type %qT", + sidx, arraytype); + else + error_at (loc, + "nonzero array subscript %qE is used with array of type %qT " + "with unknown bounds", + sidx, arraytype); + } +} + +// forked from gcc/cp/constexpr.cc get_array_or_vector_nelts + +/* Return the number of elements for TYPE (which is an ARRAY_TYPE or + a VECTOR_TYPE). */ + +static tree +get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, + bool *non_constant_p, bool *overflow_p) +{ + tree nelts; + if (TREE_CODE (type) == ARRAY_TYPE) + { + if (TYPE_DOMAIN (type)) + nelts = array_type_nelts_top (type); + else + nelts = size_zero_node; + } + else if (VECTOR_TYPE_P (type)) + nelts = size_int (TYPE_VECTOR_SUBPARTS (type)); + else + gcc_unreachable (); + + /* For VLAs, the number of elements won't be an integer constant. */ + nelts = constexpr_expression (ctx, nelts, false, non_constant_p, overflow_p); + return nelts; +} + +// forked from gcc/cp/constexpr.cc eval_and_check_array_index + +/* Subroutine of cxx_eval_array_reference. T is an ARRAY_REF; evaluate the + subscript, diagnose any problems with it, and return the result. */ + +static tree +eval_and_check_array_index (const constexpr_ctx *ctx, tree t, + bool allow_one_past, bool *non_constant_p, + bool *overflow_p) +{ + location_t loc = rs_expr_loc_or_input_loc (t); + tree ary = TREE_OPERAND (t, 0); + t = TREE_OPERAND (t, 1); + tree index + = constexpr_expression (ctx, t, allow_one_past, non_constant_p, overflow_p); + VERIFY_CONSTANT (index); + + if (!tree_fits_shwi_p (index) || tree_int_cst_sgn (index) < 0) + { + diag_array_subscript (loc, ctx, ary, index); + *non_constant_p = true; + return t; + } + + tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p, + overflow_p); + VERIFY_CONSTANT (nelts); + if (allow_one_past ? !tree_int_cst_le (index, nelts) + : !tree_int_cst_lt (index, nelts)) + { + diag_array_subscript (loc, ctx, ary, index); + *non_constant_p = true; + return t; + } + + return index; +} + +// forked from gcc/cp/constexpr.cc extract_string_elt + +/* Extract element INDEX consisting of CHARS_PER_ELT chars from + STRING_CST STRING. */ + +static tree +extract_string_elt (tree string, unsigned chars_per_elt, unsigned index) +{ + tree type = cv_unqualified (TREE_TYPE (TREE_TYPE (string))); + tree r; + + if (chars_per_elt == 1) + r = build_int_cst (type, TREE_STRING_POINTER (string)[index]); + else + { + const unsigned char *ptr + = ((const unsigned char *) TREE_STRING_POINTER (string) + + index * chars_per_elt); + r = native_interpret_expr (type, ptr, chars_per_elt); + } + return r; +} + +// forked from gcc/cp/constexpr.cc free_constructor + +/* If T is a CONSTRUCTOR, ggc_free T and any sub-CONSTRUCTORs. */ + +static void +free_constructor (tree t) +{ + if (!t || TREE_CODE (t) != CONSTRUCTOR) + return; + releasing_vec ctors; + vec_safe_push (ctors, t); + while (!ctors->is_empty ()) + { + tree c = ctors->pop (); + if (vec *elts = CONSTRUCTOR_ELTS (c)) + { + constructor_elt *ce; + for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i) + if (TREE_CODE (ce->value) == CONSTRUCTOR) + vec_safe_push (ctors, ce->value); + ggc_free (elts); + } + ggc_free (c); + } +} + } // namespace Compile } // namespace Rust diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc index 52dae395973..9221f7bb081 100644 --- a/gcc/rust/backend/rust-tree.cc +++ b/gcc/rust/backend/rust-tree.cc @@ -3154,4 +3154,72 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p, return true; } + +// forked from gcc/cp/tree.cc cv_unqualified + +/* Return TYPE with const and volatile removed. */ + +tree +cv_unqualified (tree type) +{ + int quals; + + if (type == error_mark_node) + return type; + + quals = rs_type_quals (type); + quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); + return rs_build_qualified_type (type, quals); +} + +/* The C and C++ parsers both use vectors to hold function arguments. + For efficiency, we keep a cache of unused vectors. This is the + cache. */ + +typedef vec *tree_gc_vec; +static GTY ((deletable)) vec *tree_vector_cache; + +// forked from gcc/c-family/c-common.c make_tree_vector + +/* Return a new vector from the cache. If the cache is empty, + allocate a new vector. These vectors are GC'ed, so it is OK if the + pointer is not released.. */ + +vec * +make_tree_vector (void) +{ + if (tree_vector_cache && !tree_vector_cache->is_empty ()) + return tree_vector_cache->pop (); + else + { + /* Passing 0 to vec::alloc returns NULL, and our callers require + that we always return a non-NULL value. The vector code uses + 4 when growing a NULL vector, so we do too. */ + vec *v; + vec_alloc (v, 4); + return v; + } +} + +// forked from gcc/c-family/c-common.c release_tree_vector + +/* Release a vector of trees back to the cache. */ + +void +release_tree_vector (vec *vec) +{ + if (vec != NULL) + { + if (vec->allocated () >= 16) + /* Don't cache vecs that have expanded more than once. On a p64 + target, vecs double in alloc size with each power of 2 elements, e.g + at 16 elements the alloc increases from 128 to 256 bytes. */ + vec_free (vec); + else + { + vec->truncate (0); + vec_safe_push (tree_vector_cache, vec); + } + } +} } // namespace Rust diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h index b838e402c32..b93e9364990 100644 --- a/gcc/rust/backend/rust-tree.h +++ b/gcc/rust/backend/rust-tree.h @@ -2009,6 +2009,59 @@ gnu_vector_type_p (const_tree type) return TREE_CODE (type) == VECTOR_TYPE && !TYPE_INDIVISIBLE_P (type); } +extern vec * +make_tree_vector (void); + +extern void +release_tree_vector (vec *); + +/* Simplified unique_ptr clone to release a tree vec on exit. */ + +class releasing_vec +{ +public: + typedef vec vec_t; + + releasing_vec (vec_t *v) : v (v) {} + releasing_vec () : v (make_tree_vector ()) {} + + /* Copy ops are deliberately declared but not defined, + copies must always be elided. */ + releasing_vec (const releasing_vec &); + releasing_vec &operator= (const releasing_vec &); + + vec_t &operator* () const { return *v; } + vec_t *operator-> () const { return v; } + vec_t *get () const { return v; } + operator vec_t * () const { return v; } + vec_t **operator& () { return &v; } + + /* Breaks pointer/value consistency for convenience. This takes ptrdiff_t + rather than unsigned to avoid ambiguity with the built-in operator[] + (bootstrap/91828). */ + tree &operator[] (ptrdiff_t i) const { return (*v)[i]; } + + tree *begin () { return ::begin (v); } + tree *end () { return ::end (v); } + + void release () + { + release_tree_vector (v); + v = NULL; + } + + ~releasing_vec () { release_tree_vector (v); } + +private: + vec_t *v; +}; + +inline tree * +vec_safe_push (releasing_vec &r, const tree &t CXX_MEM_STAT_INFO) +{ + return vec_safe_push (*&r, t PASS_MEM_STAT); +} + } // namespace Rust #endif // RUST_TREE