Hi! I'm trying to implement a _Lengthof() operator that is similar to sizeof(), but gets the number of elements of an array. I'm having trouble getting the number of elements for VLAs. The main work is done here: +tree +c_lengthof_type (location_t loc, tree type) +{ + tree nelts_minus_one; + enum tree_code type_code; + unsigned HOST_WIDE_INT nelts; + + type_code = TREE_CODE (type); + if (type_code != ARRAY_TYPE) + { + error_at (loc, "invalid application of %<_Lengthof%> to type %qT", type); + return error_mark_node; + } + + nelts_minus_one = array_type_nelts (type); + nelts = tree_to_uhwi (nelts_minus_one) + 1; + + return size_int (nelts); +} I would directly return array_type_nelts(type), which works for both normal arrays and VLAs, but that's off-by-one so I need to do the trick with tree_to_uhwi() to get an integer, then add one, then get back to a tree. However, tree_to_uhwi() asserts that I use an integer constant, which results in an ICE when trying to use _Lengthof() on a VLA. Is there any other way to get the actual number of elements of an array, which isn't off-by-one and also works for VLAs? Cheers, Alex P.S.: I've attached the work-in-progress patch I'm working on, in case anyone wants to have a look at it. -- <https://www.alejandro-colomar.es/>
From a476a8ea8ee207d970a6086e77c6a8c1d4ebc83b Mon Sep 17 00:00:00 2001 From: Alejandro Colomar <alx@xxxxxxxxxx> Date: Sat, 27 Jul 2024 19:04:13 +0200 Subject: [PATCH v0.1] lengthof WIP Signed-off-by: Alejandro Colomar <alx@xxxxxxxxxx> --- gcc/Makefile.in | 1 + gcc/c-family/c-common.cc | 25 ++++++++++++ gcc/c-family/c-common.def | 4 ++ gcc/c-family/c-common.h | 2 + gcc/c/c-parser.cc | 27 ++++++++----- gcc/c/c-tree.h | 4 ++ gcc/c/c-typeck.cc | 84 +++++++++++++++++++++++++++++++++++++++ gcc/cp/operators.def | 1 + gcc/ginclude/stdlength.h | 35 ++++++++++++++++ gcc/target.h | 3 ++ 10 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 gcc/ginclude/stdlength.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f4bb4a88cf3..88496edbdc8 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -477,6 +477,7 @@ USER_H = $(srcdir)/ginclude/float.h \ $(srcdir)/ginclude/stdalign.h \ $(srcdir)/ginclude/stdatomic.h \ $(srcdir)/ginclude/stdckdint.h \ + $(srcdir)/ginclude/stdlength.h \ $(EXTRA_HEADERS) USER_H_INC_NEXT_PRE = @user_headers_inc_next_pre@ diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index e7e371fd26f..8fb782c1ff1 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -407,6 +407,7 @@ const struct c_common_resword c_common_reswords[] = { "_Decimal64", RID_DFLOAT64, D_CONLY }, { "_Decimal128", RID_DFLOAT128, D_CONLY }, { "_Fract", RID_FRACT, D_CONLY | D_EXT }, + { "_Lengthof", RID_LENGTHOF, D_CONLY }, { "_Accum", RID_ACCUM, D_CONLY | D_EXT }, { "_Sat", RID_SAT, D_CONLY | D_EXT }, { "_Static_assert", RID_STATIC_ASSERT, D_CONLY }, @@ -523,6 +524,7 @@ const struct c_common_resword c_common_reswords[] = { "if", RID_IF, 0 }, { "inline", RID_INLINE, D_EXT89 }, { "int", RID_INT, 0 }, + { "lengthof", RID_LENGTHOF, 0 }, { "long", RID_LONG, 0 }, { "mutable", RID_MUTABLE, D_CXXONLY | D_CXXWARN }, { "namespace", RID_NAMESPACE, D_CXXONLY | D_CXXWARN }, @@ -4070,6 +4072,29 @@ c_alignof_expr (location_t loc, tree expr) return fold_convert_loc (loc, size_type_node, t); } + +/* Implement the _Lengthof keyword: Return the length of an array, + that is, the number of elements in the array. */ + +tree +c_lengthof_type (location_t loc, tree type) +{ + tree nelts_minus_one; + enum tree_code type_code; + unsigned HOST_WIDE_INT nelts; + + type_code = TREE_CODE (type); + if (type_code != ARRAY_TYPE) + { + error_at (loc, "invalid application of %<_Lengthof%> to type %qT", type); + return error_mark_node; + } + + nelts_minus_one = array_type_nelts (type); + nelts = tree_to_uhwi (nelts_minus_one) + 1; + + return size_int (nelts); +} /* Handle C and C++ default attributes. */ diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def index 5de96e5d4a8..46e128072b5 100644 --- a/gcc/c-family/c-common.def +++ b/gcc/c-family/c-common.def @@ -50,6 +50,10 @@ DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", tcc_expression, 1) number. */ DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3) +/* Represents a 'sizeof' expression during C++ template expansion, + or for the purpose of -Wsizeof-pointer-memaccess warning. */ +DEFTREECODE (LENGTHOF_EXPR, "lengthof_expr", tcc_expression, 1) + /* Represents a 'sizeof' expression during C++ template expansion, or for the purpose of -Wsizeof-pointer-memaccess warning. */ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1) diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index ccaea27c2b9..f815a4cf3bc 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -105,6 +105,7 @@ enum rid /* C extensions */ RID_ASM, RID_TYPEOF, RID_TYPEOF_UNQUAL, RID_ALIGNOF, RID_ATTRIBUTE, + RID_LENGTHOF, RID_VA_ARG, RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE, @@ -885,6 +886,7 @@ extern tree c_common_truthvalue_conversion (location_t, tree); extern void c_apply_type_quals_to_decl (int, tree); extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int); extern tree c_alignof_expr (location_t, tree); +extern tree c_lengthof_type (location_t, tree); /* Print an error message for invalid operands to arith operation CODE. NOP_EXPR is used as a special case (see truthvalue_conversion). */ extern void binary_op_error (rich_location *, enum tree_code, tree, tree); diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 12c5ed5d92c..744b0173634 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1687,7 +1687,7 @@ static struct c_expr c_parser_binary_expression (c_parser *, struct c_expr *, tree); static struct c_expr c_parser_cast_expression (c_parser *, struct c_expr *); static struct c_expr c_parser_unary_expression (c_parser *); -static struct c_expr c_parser_sizeof_expression (c_parser *); +static struct c_expr c_parser_sizeof_or_lengthof_expression (c_parser *, enum rid); static struct c_expr c_parser_alignof_expression (c_parser *); static struct c_expr c_parser_postfix_expression (c_parser *); static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *, @@ -9864,8 +9864,10 @@ c_parser_unary_expression (c_parser *parser) case CPP_KEYWORD: switch (c_parser_peek_token (parser)->keyword) { + case RID_LENGTHOF: + return c_parser_sizeof_or_lengthof_expression (parser, RID_LENGTHOF); case RID_SIZEOF: - return c_parser_sizeof_expression (parser); + return c_parser_sizeof_or_lengthof_expression (parser, RID_SIZEOF); case RID_ALIGNOF: return c_parser_alignof_expression (parser); case RID_BUILTIN_HAS_ATTRIBUTE: @@ -9903,12 +9905,13 @@ c_parser_unary_expression (c_parser *parser) /* Parse a sizeof expression. */ static struct c_expr -c_parser_sizeof_expression (c_parser *parser) +c_parser_sizeof_or_lengthof_expression (c_parser *parser, enum rid rid) { + const char *op_name = (rid == RID_SIZEOF) ? "sizeof" : "_Lengthof"; struct c_expr expr; struct c_expr result; location_t expr_loc; - gcc_assert (c_parser_next_token_is_keyword (parser, RID_SIZEOF)); + gcc_assert (c_parser_next_token_is_keyword (parser, rid)); location_t start; location_t finish = UNKNOWN_LOCATION; @@ -9952,13 +9955,16 @@ c_parser_sizeof_expression (c_parser *parser) } /* sizeof ( type-name ). */ if (scspecs) - error_at (expr_loc, "storage class specifier in %<sizeof%>"); + error_at (expr_loc, "storage class specifier in %qs", op_name); if (type_name->specs->alignas_p) error_at (type_name->specs->locations[cdw_alignas], - "alignment specified for type name in %<sizeof%>"); + "alignment specified for type name in %qs", op_name); c_inhibit_evaluation_warnings--; in_sizeof--; - result = c_expr_sizeof_type (expr_loc, type_name); + if (rid == RID_LENGTHOF) + result = c_expr_lengthof_type (expr_loc, type_name); + else + result = c_expr_sizeof_type (expr_loc, type_name); } else { @@ -9971,8 +9977,11 @@ c_parser_sizeof_expression (c_parser *parser) mark_exp_read (expr.value); if (TREE_CODE (expr.value) == COMPONENT_REF && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1))) - error_at (expr_loc, "%<sizeof%> applied to a bit-field"); - result = c_expr_sizeof_expr (expr_loc, expr); + error_at (expr_loc, "%qs applied to a bit-field", op_name); + if (rid == RID_LENGTHOF) + result = c_expr_lengthof_expr (expr_loc, expr); + else + result = c_expr_sizeof_expr (expr_loc, expr); } if (finish == UNKNOWN_LOCATION) finish = start; diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 15da875a029..102fcfefea6 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -736,6 +736,7 @@ extern int c_type_dwarf_attribute (const_tree, int); /* in c-typeck.cc */ extern int in_alignof; extern int in_sizeof; +extern int in_lengthof; extern int in_typeof; extern bool c_in_omp_for; extern bool c_omp_array_section_p; @@ -786,6 +787,9 @@ extern tree build_external_ref (location_t, tree, bool, tree *); extern void pop_maybe_used (bool); extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr); extern struct c_expr c_expr_sizeof_type (location_t, struct c_type_name *); +extern struct c_expr c_expr_lengthof_expr (location_t, struct c_expr); +extern struct c_expr c_expr_lengthof_type (location_t loc, + struct c_type_name *); extern struct c_expr parser_build_unary_op (location_t, enum tree_code, struct c_expr); extern struct c_expr parser_build_binary_op (location_t, diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 7e0f01ed22b..543254406f5 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -3453,6 +3453,90 @@ c_expr_sizeof_type (location_t loc, struct c_type_name *t) return ret; } +/* Return the result of _Lengthof applied to EXPR. */ + +struct c_expr +c_expr_lengthof_expr (location_t loc, struct c_expr expr) +{ + struct c_expr ret; + if (expr.value == error_mark_node) + { + ret.value = error_mark_node; + ret.original_code = ERROR_MARK; + ret.original_type = NULL; + ret.m_decimal = 0; + pop_maybe_used (false); + } + else + { + bool expr_const_operands = true; + + tree folded_expr = c_fully_fold (expr.value, require_constant_value, + &expr_const_operands); + ret.value = c_lengthof_type (loc, TREE_TYPE (folded_expr)); + c_last_sizeof_arg = expr.value; + c_last_sizeof_loc = loc; + ret.original_code = LENGTHOF_EXPR; + ret.original_type = NULL; + ret.m_decimal = 0; + if (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr))) + { + /* _Lengthof is evaluated when given a vla. */ + ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value), + folded_expr, ret.value); + C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !expr_const_operands; + SET_EXPR_LOCATION (ret.value, loc); + } + pop_maybe_used (C_TYPE_VARIABLE_SIZE (TREE_TYPE (folded_expr))); + } + return ret; +} + +/* Return the result of _Lengthof applied to T, a structure for the type + name passed to _lengthof (rather than the type itself). LOC is the + location of the original expression. */ + +struct c_expr +c_expr_lengthof_type (location_t loc, struct c_type_name *t) +{ + tree type; + struct c_expr ret; + tree type_expr = NULL_TREE; + bool type_expr_const = true; + type = groktypename (t, &type_expr, &type_expr_const); + ret.value = c_lengthof_type (loc, type); + c_last_sizeof_arg = type; + c_last_sizeof_loc = loc; + ret.original_code = LENGTHOF_EXPR; + ret.original_type = NULL; + ret.m_decimal = 0; + if (type == error_mark_node) + { + ret.value = error_mark_node; + ret.original_code = ERROR_MARK; + } + else + if ((type_expr || TREE_CODE (ret.value) == INTEGER_CST) + && C_TYPE_VARIABLE_SIZE (type)) + { + /* If the type is a [*] array, it is a VLA but is represented as + having a size of zero. In such a case we must ensure that + the result of _Lengthof does not get folded to a constant by + c_fully_fold, because if the length is evaluated the result is + not constant and so constraints on zero or negative size + arrays must not be applied when this _Lengthof call is inside + another array declarator. */ + if (!type_expr) + type_expr = integer_zero_node; + ret.value = build2 (C_MAYBE_CONST_EXPR, TREE_TYPE (ret.value), + type_expr, ret.value); + C_MAYBE_CONST_EXPR_NON_CONST (ret.value) = !type_expr_const; + } + pop_maybe_used (type != error_mark_node + ? C_TYPE_VARIABLE_SIZE (type) : false); + return ret; +} + /* Build a function call to function FUNCTION with parameters PARAMS. The function call is at LOC. PARAMS is a list--a chain of TREE_LIST nodes--in which the diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def index d8878923602..ab1447a3386 100644 --- a/gcc/cp/operators.def +++ b/gcc/cp/operators.def @@ -91,6 +91,7 @@ DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG_UNARY) /* These are extensions. */ DEF_OPERATOR ("alignof", ALIGNOF_EXPR, "az", OVL_OP_FLAG_UNARY) +DEF_OPERATOR ("lengthof", LENGTHOF_EXPR, "lz", OVL_OP_FLAG_UNARY) DEF_OPERATOR ("__imag__", IMAGPART_EXPR, "v18__imag__", OVL_OP_FLAG_UNARY) DEF_OPERATOR ("__real__", REALPART_EXPR, "v18__real__", OVL_OP_FLAG_UNARY) diff --git a/gcc/ginclude/stdlength.h b/gcc/ginclude/stdlength.h new file mode 100644 index 00000000000..551de92ecb1 --- /dev/null +++ b/gcc/ginclude/stdlength.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2024 Free Software Foundation, Inc. + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#ifndef _STDLENGTH_H +#define _STDLENGTH_H + +#if (!defined __cplusplus) + +#define lengthof _Lengthof + +#define __lengthof_is_defined 1 + +#endif + +#endif /* stdlength.h */ diff --git a/gcc/target.h b/gcc/target.h index c1f99b97b86..79890ae9944 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -245,6 +245,9 @@ enum type_context_kind { /* Directly measuring the alignment of T. */ TCTX_ALIGNOF, + /* Directly measuring the length of array T. */ + TCTX_LENGTHOF, + /* Creating objects of type T with static storage duration. */ TCTX_STATIC_STORAGE, -- 2.45.2
Attachment:
signature.asc
Description: PGP signature