From patchwork Sun Jul 28 14:16:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alejandro Colomar X-Patchwork-Id: 1965722 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20201202 header.b=dWe7RNIi; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WX3T81tn3z1ybY for ; Mon, 29 Jul 2024 00:16:44 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E05433858414 for ; Sun, 28 Jul 2024 14:16:39 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from sin.source.kernel.org (sin.source.kernel.org [IPv6:2604:1380:40e1:4800::1]) by sourceware.org (Postfix) with ESMTPS id 7AEBF3857340 for ; Sun, 28 Jul 2024 14:16:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7AEBF3857340 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=kernel.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kernel.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7AEBF3857340 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2604:1380:40e1:4800::1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722176172; cv=none; b=S+aHXPLG8fnPdiAEnSOgCA4aNW3Ydw80VOkFUbt2N41vnzgW1t162z/kwGr9t1XnF4iGx/GZU1TNU9y9pQH+GTbFGhx9XN2RSmdRe3NMzJXT5bKLhzJfcAXAdjfjcD+MMnWb7aZuSB63kRT13RUMy9OzBPG5tUUJlVz9bgvvn38= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722176172; c=relaxed/simple; bh=BhVhl7afu+VVPnsbHtXZqQfqJ+JNgBEv7bXo8Yi6KMI=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=HwqN8egGRubiPowHNsZ6r30olMexv9hIc0CLQfQCcSZitGkQRW/iDvy1j9nL9R7oPHZXHV//ytEUZJmNN+uTLJmNFU/Tvr0dbjqYL9hzLLZPcGFf6ckym9ivAg3R3DcCemsJ4HAeJWlRvSamysIvqTKdmoqCUqscq2tcyij9sIc= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 09998CE076E; Sun, 28 Jul 2024 14:16:05 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2027EC32782; Sun, 28 Jul 2024 14:16:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722176164; bh=BhVhl7afu+VVPnsbHtXZqQfqJ+JNgBEv7bXo8Yi6KMI=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=dWe7RNIi39VM3ewrtBafoFX2LSjbWOMNfEDVXTuihOQ+cOb/TUV3fPjoQ31VgOTTf VEwC3X248N4klBtZgpjFcKz6PyDlPMtDVhRdLtBhsQMxbF0dqS4y8RhV2bH3jlR2rE FvmJy8hrqyV41nbekSjnqydBByvbI1mKFHRbLwJuL4CeF3RIOvol4E5GFhyyYINlj9 FHuOWHBJ31VVwM/cr8azbYkd9vPgYVr/m7E8u4VvmalcO5zQVjJIqisyMy49ZxtYiL 0Ubxjs3S1aWsSQOxK7TAIJZrnCuZ0KM6QPwJesHQd3Mara7i9j1gxLXFZyY7Cle8Z2 T9vyNnpla0lWQ== Date: Sun, 28 Jul 2024 16:16:01 +0200 From: Alejandro Colomar To: gcc-patches@gcc.gnu.org Cc: Alejandro Colomar , Gabriel Ravier , Martin Uecker , Joseph Myers Subject: [RFC v1 2/2] c: Add _Lengthof() operator Message-ID: <20240728141547.302478-3-alx@kernel.org> X-Mailer: git-send-email 2.45.2 References: <20240728141547.302478-1-alx@kernel.org> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20240728141547.302478-1-alx@kernel.org> X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This operator is similar to sizeof() but can only be applied to an array, and returns its length (number of elements). FUTURE DIRECTIONS: We could make it work with array parameters to functions, and somehow magically return the length designator of the array, regardless of it being really a pointer. Link: Cc: Gabriel Ravier Cc: Martin Uecker Cc: Joseph Myers Signed-off-by: Alejandro Colomar --- gcc/Makefile.in | 1 + gcc/c-family/c-common.cc | 20 ++++++++++ gcc/c-family/c-common.def | 4 ++ gcc/c-family/c-common.h | 2 + gcc/c/c-parser.cc | 35 ++++++++++++---- 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, 181 insertions(+), 8 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..c9020a7faef 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 | D_EXT }, { "_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, D_EXT }, { "long", RID_LONG, 0 }, { "mutable", RID_MUTABLE, D_CXXONLY | D_CXXWARN }, { "namespace", RID_NAMESPACE, D_CXXONLY | D_CXXWARN }, @@ -4070,6 +4072,24 @@ 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) +{ + enum tree_code type_code; + + 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; + } + + return array_type_nelts_top (type); +} /* 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..eb1d8b932fd 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -74,7 +74,17 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "analyzer/analyzer-language.h" #include "toplev.h" + +#define c_parser_sizeof_expression(parser) \ +( \ + c_parser_sizeof_or_lengthof_expression (parser, RID_SIZEOF) \ +) +#define c_parser_lengthof_expression(parser) \ +( \ + c_parser_sizeof_or_lengthof_expression (parser, RID_LENGTHOF) \ +) + /* We need to walk over decls with incomplete struct/union/enum types after parsing the whole translation unit. In finish_decl(), if the decl is static, has incomplete @@ -1687,7 +1697,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,6 +9874,8 @@ c_parser_unary_expression (c_parser *parser) case CPP_KEYWORD: switch (c_parser_peek_token (parser)->keyword) { + case RID_LENGTHOF: + return c_parser_lengthof_expression (parser); case RID_SIZEOF: return c_parser_sizeof_expression (parser); case RID_ALIGNOF: @@ -9903,12 +9915,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 +9965,16 @@ c_parser_sizeof_expression (c_parser *parser) } /* sizeof ( type-name ). */ if (scspecs) - error_at (expr_loc, "storage class specifier in %"); + 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 %"); + "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 +9987,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, "% 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 +. */ + +#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,