From patchwork Thu Oct 7 22:14:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45972 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9EB71385782C for ; Thu, 7 Oct 2021 22:15:42 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from quail.birch.relay.mailchannels.net (quail.birch.relay.mailchannels.net [23.83.209.151]) by sourceware.org (Postfix) with ESMTPS id 404893858428 for ; Thu, 7 Oct 2021 22:14:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 404893858428 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 95D4D542399; Thu, 7 Oct 2021 22:14:47 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-17-22.trex.outbound.svc.cluster.local [100.96.17.22]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 148755421D1; Thu, 7 Oct 2021 22:14:47 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.17.22 (trex/6.4.3); Thu, 07 Oct 2021 22:14:47 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Madly-Plucky: 3009b32121dc4f30_1633644887334_1358701832 X-MC-Loop-Signature: 1633644887334:2400994570 X-MC-Ingress-Time: 1633644887333 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id 6DB4583497; Thu, 7 Oct 2021 15:14:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=uyjD330FVgNMM6xjoK9 tA++HnDw=; b=ne4eAbApuBFMh67xqvgsRxwdK5Gc6dsTC22kTBvXaVFnw1gK6l/ 4cd5PIqP2U7zlxqY3aHX813BF1yfkMnGToikEqliqnKIANY7lKg+q6e3jdTLp+Kj pYX1024yt4Ut/obn/8dj5QMl5pCzSAnke7A+h+T6X5ERrzGFdogTgErY= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 901F07F2D7; Thu, 7 Oct 2021 15:14:43 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Date: Fri, 8 Oct 2021 03:44:25 +0530 Message-Id: <20211007221432.1029249-2-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Recognize the __builtin_dynamic_object_size builtin, but simply replace it with -1 or 0 for now. gcc/ChangeLog: * builtins.c (expand_builtin, fold_builtin_2): Add BUILT_IN_DYN_OBJECT_SIZE. (fold_builtin_dyn_object_size): New function. (valid_object_size_args): New function. (fold_builtin_object_size): Use it. * builtins.def (BUILT_IN_DYN_OBJECT_SIZE): New builtin. --- gcc/builtins.c | 63 +++++++++++++++++++++++++++++++++++++++--------- gcc/builtins.def | 1 + 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index 3e57eb03af0..894d62359b4 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -180,6 +180,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode, static void maybe_emit_chk_warning (tree, enum built_in_function); static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); static tree fold_builtin_object_size (tree, tree); +static tree fold_builtin_dyn_object_size (tree, tree); unsigned HOST_WIDE_INT target_newline; unsigned HOST_WIDE_INT target_percent; @@ -7910,6 +7911,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, return const0_rtx; case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_DYN_OBJECT_SIZE: return expand_builtin_object_size (exp); case BUILT_IN_MEMCPY_CHK: @@ -9318,6 +9320,9 @@ fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1) case BUILT_IN_OBJECT_SIZE: return fold_builtin_object_size (arg0, arg1); + case BUILT_IN_DYN_OBJECT_SIZE: + return fold_builtin_dyn_object_size (arg0, arg1); + case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: return fold_builtin_atomic_always_lock_free (arg0, arg1); @@ -9964,7 +9969,10 @@ fold_builtin_next_arg (tree exp, bool va_start_p) } -/* Expand a call EXP to __builtin_object_size. */ +/* Expand a call EXP to __builtin_object_size or + __builtin_dynamic_object_size. If the builtin survived up to this point + then it means we have failed to get an object size, so replace the call with + 0 or -1 depending on the object size type argument. */ static rtx expand_builtin_object_size (tree exp) @@ -10250,27 +10258,40 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) access_write_only); } -/* Fold a call to __builtin_object_size with arguments PTR and OST, - if possible. */ +/* Validate arguments to __builtin_object_size and + __builtin_dynamic_object_size. If both arguments are valid, return the + object size type in OSTP. */ -static tree -fold_builtin_object_size (tree ptr, tree ost) +static bool +valid_object_size_args (const tree ptr, tree ost, int *ostp) { - unsigned HOST_WIDE_INT bytes; - int object_size_type; - if (!validate_arg (ptr, POINTER_TYPE) || !validate_arg (ost, INTEGER_TYPE)) - return NULL_TREE; + return false; STRIP_NOPS (ost); if (TREE_CODE (ost) != INTEGER_CST || tree_int_cst_sgn (ost) < 0 || compare_tree_int (ost, 3) > 0) - return NULL_TREE; + return false; - object_size_type = tree_to_shwi (ost); + *ostp = tree_to_shwi (ost); + + return true; +} + +/* Fold a call to __builtin_object_size with arguments PTR and OST, + if possible. */ + +static tree +fold_builtin_object_size (tree ptr, tree ost) +{ + unsigned HOST_WIDE_INT bytes; + int object_size_type; + + if (!valid_object_size_args (ptr, ost, &object_size_type)) + return NULL_TREE; /* __builtin_object_size doesn't evaluate side-effects in its arguments; if there are any side-effects, it returns (size_t) -1 for types 0 and 1 @@ -10297,6 +10318,26 @@ fold_builtin_object_size (tree ptr, tree ost) return NULL_TREE; } +/* Fold a call to __builtin_dynamic_object_size with arguments PTR and OST, + if possible. */ + +static tree +fold_builtin_dyn_object_size (tree ptr, tree ost) +{ + int object_size_type; + + if (!valid_object_size_args (ptr, ost, &object_size_type)) + return NULL_TREE; + + /* __builtin_dynamic_object_size doesn't evaluate side-effects in its + arguments; if there are any side-effects, it returns (size_t) -1 for types + 0 and 1 and (size_t) 0 for types 2 and 3. */ + if (TREE_SIDE_EFFECTS (ptr)) + return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0); + + return NULL_TREE; +} + /* Builtins with folding operations that operate on "..." arguments need special handling; we need to store the arguments in a convenient data structure before attempting any folding. Fortunately there are diff --git a/gcc/builtins.def b/gcc/builtins.def index 45a09b4d42d..ae94caab921 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq") /* Object size checking builtins. */ DEF_GCC_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_DYN_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) From patchwork Thu Oct 7 22:14:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45973 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8E8B9385780D for ; Thu, 7 Oct 2021 22:16:12 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from dog.birch.relay.mailchannels.net (dog.birch.relay.mailchannels.net [23.83.209.48]) by sourceware.org (Postfix) with ESMTPS id 8E079385781D for ; Thu, 7 Oct 2021 22:14:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8E079385781D Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id ACE6312267D; Thu, 7 Oct 2021 22:14:57 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-11-21.trex.outbound.svc.cluster.local [100.96.11.21]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 4EA211225FE; Thu, 7 Oct 2021 22:14:56 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.11.21 (trex/6.4.3); Thu, 07 Oct 2021 22:14:57 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Troubled-Bubble: 172817f90d76ae3a_1633644897496_1476800747 X-MC-Loop-Signature: 1633644897496:1321568983 X-MC-Ingress-Time: 1633644897496 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id D344683499; Thu, 7 Oct 2021 15:14:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=TRawM3aZCDMLum51L+b BOOe2rKs=; b=e/hR9zq5+eK6+BQbJqbTFXLRhfoXsLmH2jdcsxRCEgk4Tuq9h15 wk0aMJpYme0W7peO7nYOI4iphI1Qf799YmJ2dCGNvr6gxAO5fyc2LyUVGKZ+H6Vf lx6MlxztgCfakZ0KW5xuKw/n9+wOdP6jv1G5nmxn1Qq/EIaJCzpljOWw= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 5F3217F2D7; Thu, 7 Oct 2021 15:14:46 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 2/8] tree-dynamic-object-size: New pass Date: Fri, 8 Oct 2021 03:44:26 +0530 Message-Id: <20211007221432.1029249-3-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" A new pass is added to execute just before the tree-object-size pass to recognize and simplify __builtin_dynamic_object_size. Some key ideas (such as multipass object size collection to detect reference loops) have been taken from tree-object-size but is distinct from it to ensure minimal impact on existing code. At the moment, the pass only recognizes allocators and passthrough functions to attempt to derive object size expressions, and replaces the call site with those expressions. On failure, it replaces the __builtin_dynamic_object_size with __builtin_object_size as a fallback. gcc/ChangeLog: * Makefile.in (OBJS): Add tree-dynamic-object-size.o. (PLUGIN_HEADERS): Add tree-dynamic-object-size.h. * tree-dynamic-object-size.c: New file. * tree-dynamic-object-size.h: New file. * builtins.c: Use it. (fold_builtin_dyn_object_size): Call compute_builtin_dyn_object_size for __builtin_dynamic_object_size builtin. (passes.def): Add pass_dynamic_object_sizes. * tree-pass.h: Add ake_pass_dynamic_object_sizes. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c: New test. Signed-off-by: Siddhesh Poyarekar --- gcc/Makefile.in | 19 +- gcc/builtins.c | 8 + gcc/doc/extend.texi | 11 + gcc/passes.def | 3 + .../gcc.dg/builtin-dynamic-object-size-0.c | 166 ++++++ gcc/tree-dynamic-object-size.c | 517 ++++++++++++++++++ gcc/tree-dynamic-object-size.h | 25 + gcc/tree-pass.h | 2 + 8 files changed, 742 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c create mode 100644 gcc/tree-dynamic-object-size.c create mode 100644 gcc/tree-dynamic-object-size.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f36ffa4740b..5189dcfcc0d 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1608,6 +1608,7 @@ OBJS = \ tree-diagnostic.o \ tree-diagnostic-path.o \ tree-dump.o \ + tree-dynamic-object-size.o \ tree-eh.o \ tree-emutls.o \ tree-if-conv.o \ @@ -3647,15 +3648,15 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ ssa-iterators.h $(RESOURCE_H) tree-cfgcleanup.h attribs.h calls.h \ cfgexpand.h diagnostic-color.h gcc-symtab.h gimple-builder.h gimple-low.h \ gimple-walk.h gimplify-me.h pass_manager.h print-rtl.h stmt.h \ - tree-dfa.h tree-hasher.h tree-nested.h tree-object-size.h tree-outof-ssa.h \ - tree-parloops.h tree-ssa-address.h tree-ssa-coalesce.h tree-ssa-dom.h \ - tree-ssa-loop.h tree-ssa-loop-ivopts.h tree-ssa-loop-manip.h \ - tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \ - tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \ - hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h \ - lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \ - pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \ - $(EXPR_H) + tree-dfa.h tree-dynamic-object-size.h tree-hasher.h tree-nested.h \ + tree-object-size.h tree-outof-ssa.h tree-parloops.h tree-ssa-address.h \ + tree-ssa-coalesce.h tree-ssa-dom.h tree-ssa-loop.h tree-ssa-loop-ivopts.h \ + tree-ssa-loop-manip.h tree-ssa-loop-niter.h tree-ssa-ter.h \ + tree-ssa-threadedge.h tree-ssa-threadupdate.h inchash.h wide-int.h signop.h \ + hash-map.h hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h \ + cfgcleanup.h lcm.h cfgloopmanip.h file-prefix-map.h builtins.def \ + $(INSN_ATTR_H) pass-instances.def params.list \ + $(srcdir)/../include/gomp-constants.h $(EXPR_H) # generate the 'build fragment' b-header-vars s-header-vars: Makefile diff --git a/gcc/builtins.c b/gcc/builtins.c index 894d62359b4..d015029765b 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see #include "calls.h" #include "varasm.h" #include "tree-object-size.h" +#include "tree-dynamic-object-size.h" #include "tree-ssa-strlen.h" #include "realmpfr.h" #include "cfgrtl.h" @@ -10325,6 +10326,7 @@ static tree fold_builtin_dyn_object_size (tree ptr, tree ost) { int object_size_type; + tree bytes; if (!valid_object_size_args (ptr, ost, &object_size_type)) return NULL_TREE; @@ -10335,6 +10337,12 @@ fold_builtin_dyn_object_size (tree ptr, tree ost) if (TREE_SIDE_EFFECTS (ptr)) return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0); + /* If object size expression cannot be evaluated yet, delay folding until + later. Maybe subsequent passes will help determining it. */ + if (TREE_CODE (ptr) == SSA_NAME + && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes)) + return bytes; + return NULL_TREE; } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 133b82eef38..082d167cd65 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -12777,6 +12777,17 @@ assert (__builtin_object_size (q, 1) == sizeof (var.b)); @end smallexample @end deftypefn +@deftypefn {Built-in Function} {size_t} __builtin_dynamic_object_size (const void * @var{ptr}, int @var{type}) +is similar to @code{__builtin_object_size} in that it returns a number of bytes +from @var{ptr} to the end of the object @var{ptr} pointer points to, except +that the size returned may not be a constant. This results in successful +evaluation of object size estimates in a wider range of use cases and can be +more precise than @code{__builtin_object_size}, but it incurs a performance +penalty since it may add a runtime overhead on size computation. Semantics of +@var{type} as well as return values in case it is not possible to determine +which objects @var{ptr} points to at compile time are the same as in the case +of @code{__builtin_object_size}. + There are built-in functions added for many common string operation functions, e.g., for @code{memcpy} @code{__builtin___memcpy_chk} built-in is provided. This built-in has an additional last argument, diff --git a/gcc/passes.def b/gcc/passes.def index c11c237f6d2..7dc55cc6ba0 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_all_early_optimizations); PUSH_INSERT_PASSES_WITHIN (pass_all_early_optimizations) NEXT_PASS (pass_remove_cgraph_callee_edges); + NEXT_PASS (pass_early_dynamic_object_sizes); NEXT_PASS (pass_early_object_sizes); /* Don't record nonzero bits before IPA to avoid using too much memory. */ @@ -198,6 +199,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_ccp, true /* nonzero_p */); /* After CCP we rewrite no longer addressed locals into SSA form if possible. */ + NEXT_PASS (pass_dynamic_object_sizes); NEXT_PASS (pass_object_sizes); NEXT_PASS (pass_post_ipa_warn); NEXT_PASS (pass_complete_unrolli); @@ -379,6 +381,7 @@ along with GCC; see the file COPYING3. If not see /* Perform simple scalar cleanup which is constant/copy propagation. */ NEXT_PASS (pass_ccp, true /* nonzero_p */); NEXT_PASS (pass_post_ipa_warn); + NEXT_PASS (pass_dynamic_object_sizes); NEXT_PASS (pass_object_sizes); /* Fold remaining builtins. */ NEXT_PASS (pass_fold_builtins); diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c new file mode 100644 index 00000000000..620e8cbc611 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -0,0 +1,166 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +#define abort __builtin_abort + +void * +__attribute__ ((alloc_size (1))) +__attribute__ ((__nothrow__ , __leaf__)) +__attribute__ ((noinline)) +alloc_func (size_t sz) +{ + return __builtin_malloc (sz); +} + +void * +__attribute__ ((alloc_size (1, 2))) +__attribute__ ((__nothrow__ , __leaf__)) +__attribute__ ((noinline)) +calloc_func (size_t cnt, size_t sz) +{ + return __builtin_calloc (cnt, sz); +} + +void * +__attribute__ ((noinline)) +unknown_allocator (size_t cnt, size_t sz) +{ + return __builtin_calloc (cnt, sz); +} + +size_t +__attribute__ ((noinline)) +test_unknown (size_t cnt, size_t sz) +{ + void *ret = unknown_allocator (cnt, sz); + return __builtin_dynamic_object_size (ret, 0); +} + +/* Malloc-like allocator. */ + +size_t +__attribute__ ((noinline)) +test_malloc (size_t sz) +{ + void *ret = alloc_func (sz); + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_malloc (size_t sz) +{ + void *ret = __builtin_malloc (sz); + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_malloc_cond (int cond) +{ + void *ret = __builtin_malloc (cond ? 32 : 64); + return __builtin_dynamic_object_size (ret, 0); +} + +/* Calloc-like allocator. */ + +size_t +__attribute__ ((noinline)) +test_calloc (size_t cnt, size_t sz) +{ + void *ret = calloc_func (cnt, sz); + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_calloc (size_t cnt, size_t sz) +{ + void *ret = __builtin_calloc (cnt, sz); + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_calloc_cond (int cond1, int cond2) +{ + void *ret = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16); + return __builtin_dynamic_object_size (ret, 0); +} + +/* Passthrough functions. */ + +size_t +__attribute__ ((noinline)) +test_passthrough (size_t sz, char *in) +{ + char *bin = __builtin_malloc (sz); + char *dest = __builtin_memcpy (bin, in, sz); + + return __builtin_dynamic_object_size (dest, 0); +} + +/* Variable length arrays. */ +size_t +__attribute__ ((noinline)) +test_dynarray (size_t sz) +{ + char bin[sz]; + + return __builtin_dynamic_object_size (bin, 0); +} + +size_t +__attribute__ ((noinline)) +test_dynarray_cond (int cond) +{ + char bin[cond ? 8 : 16]; + + return __builtin_dynamic_object_size (bin, 0); +} + +int +main (int argc, char **argv) +{ + unsigned nfails = 0; + +#define FAIL() ({ \ + __builtin_printf ("Failure at line: %d\n", __LINE__); \ + nfails++; \ +}) + + size_t outsz = test_unknown (32, 42); + if (outsz != -1 && outsz != 32) + FAIL (); + if (test_malloc (2048) != 2048) + FAIL (); + if (test_builtin_malloc (2048) != 2048) + FAIL (); + if (test_builtin_malloc_cond (1) != 32) + FAIL (); + if (test_builtin_malloc_cond (0) != 64) + FAIL (); + if (test_calloc (2048, 4) != 2048 * 4) + FAIL (); + if (test_builtin_calloc (2048, 8) != 2048 * 8) + FAIL (); + if (test_builtin_calloc_cond (0, 0) != 64 * 16) + FAIL (); + if (test_builtin_calloc_cond (1, 1) != 32 * 1024) + FAIL (); + if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0]) + != __builtin_strlen (argv[0]) + 1) + FAIL (); + if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0])) + FAIL (); + if (test_dynarray_cond (0) != 16) + FAIL (); + if (test_dynarray_cond (1) != 8) + FAIL (); + + if (nfails > 0) + __builtin_abort (); + + return 0; +} diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c new file mode 100644 index 00000000000..8e52dd46c03 --- /dev/null +++ b/gcc/tree-dynamic-object-size.c @@ -0,0 +1,517 @@ +/* __builtin_dynamic_object_size (ptr, object_size_type) computation + Copyright (C) 2021 Siddhesh Poyarekar + +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 +. */ + +/* __builtin_dynamic_object_size returns richer object size information with + the intent of improving precision of checks that depend on object sizes. It + is a drop-in replacement for __builtin_object_size due to having the + following common semantics: + + * Both take the same arguments. + * Like __builtin_object_size, __builtin_dynamic_object_size also provides an + estimate (either lower or higher, based on the second argument) of the + object size and not the precise object size. + * On failure, both return either (size_t)-1 or (size_t)0 depending on the + second byte of the TYPE argument. + + There are minor semantic differences in both builtins: + + * On success, __builtin_dynamic_object_size is more likely to return the + closest object size, since it may return one of the following: + - An expression that evaluates to the exact object size + - When the exact size is not available, an expression that evaluates to + the maximum or minimum estimate of the size of the object. Currently + this is a constant since the pass simplifies + __builtin_dynamic_object_size to __builtin_object_size if it cannot + determine a size expression. However in future it could be a + non-constant expression. + + See definition of collect_object_sizes_for to know what patterns are + currently recognized. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "ssa.h" +#include "gimple-pretty-print.h" +#include "fold-const.h" +#include "tree-object-size.h" +#include "gimple-fold.h" +#include "gimple-iterator.h" +#include "tree-cfg.h" +#include "stringpool.h" +#include "attribs.h" +#include "builtins.h" +#include "print-tree.h" + +struct object_size_info +{ + int object_size_type; + bitmap visited; +}; + +static tree alloc_object_size (const gcall *); +static tree pass_through_call (const gcall *); +static void collect_object_sizes_for (struct object_size_info *, tree); + +/* object_sizes[0] is upper bound for number of bytes till the end of + the object. + object_sizes[1] is upper bound for number of bytes till the end of + the subobject (innermost array or field with address taken). + object_sizes[2] is lower bound for number of bytes till the end of + the object and object_sizes[3] lower bound for subobject. */ +static vec object_sizes[4]; + +/* Bitmaps what object sizes have been computed already. */ +static bitmap computed[4]; + +/* Compute __builtin_dynamic_object_size for CALL, which is a GIMPLE_CALL. + Handles calls to functions declared with attribute alloc_size. + OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size. + If unknown, return NULL_TREE. */ + +static tree +alloc_object_size (const gcall *call) +{ + gcc_assert (is_gimple_call (call)); + + tree calltype; + tree callfn = gimple_call_fndecl (call); + + if (callfn) + calltype = TREE_TYPE (callfn); + else + calltype = gimple_call_fntype (call); + + if (!calltype) + return NULL_TREE; + + /* Set to positions of alloc_size arguments. */ + int arg1 = -1, arg2 = -1; + tree alloc_size = lookup_attribute ("alloc_size", + TYPE_ATTRIBUTES (calltype)); + if (alloc_size && TREE_VALUE (alloc_size)) + { + tree p = TREE_VALUE (alloc_size); + + arg1 = TREE_INT_CST_LOW (TREE_VALUE (p))-1; + if (TREE_CHAIN (p)) + arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1; + } + else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL) + && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn))) + arg1 = 0; + + if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call) + || arg2 >= (int)gimple_call_num_args (call)) + return NULL_TREE; + + if (arg2 >= 0) + { + tree ret = fold_build2 (MULT_EXPR, sizetype, + fold_convert (sizetype, gimple_call_arg (call, + arg1)), + fold_convert (sizetype, gimple_call_arg (call, + arg2))); + return STRIP_NOPS (ret); + } + else if (arg1 >= 0) + { + tree ret = fold_convert (sizetype, gimple_call_arg (call, arg1)); + return STRIP_NOPS (ret); + } + + return NULL_TREE; +} + + +/* If object size is propagated from one of function's arguments directly + to its return value, return that argument for GIMPLE_CALL statement CALL. + Otherwise return NULL. */ + +static tree +pass_through_call (const gcall *call) +{ + unsigned rf = gimple_call_return_flags (call); + if (rf & ERF_RETURNS_ARG) + { + unsigned argnum = rf & ERF_RETURN_ARG_MASK; + if (argnum < gimple_call_num_args (call)) + return gimple_call_arg (call, argnum); + } + + /* __builtin_assume_aligned is intentionally not marked RET1. */ + if (gimple_call_builtin_p (call, BUILT_IN_ASSUME_ALIGNED)) + return gimple_call_arg (call, 0); + + return NULL_TREE; +} + +/* Compute object size estimate for PTR and set *PSIZE to the resulting value. + OBJECT_SIZE_TYPE is the second argument to __builtin_dynamic_object_size. + Returns true on success and false when the object size could not be + determined. */ + +bool +compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize) +{ + gcc_assert (object_size_type >= 0 && object_size_type <= 3); + + /* Set to unknown and overwrite just before returning if the size + could be determined. */ + *psize = NULL_TREE; + + if (TREE_CODE (ptr) != SSA_NAME + || !POINTER_TYPE_P (TREE_TYPE (ptr))) + return false; + + if (computed[object_size_type] == NULL) + return false; + + if (bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr))) + goto out; + + struct object_size_info osi; + bitmap_iterator bi; + unsigned int i; + + if (num_ssa_names > object_sizes[object_size_type].length ()) + object_sizes[object_size_type].safe_grow (num_ssa_names, true); + if (dump_file) + { + fprintf (dump_file, "Computing %s dynamic %sobject size for ", + (object_size_type & 2) ? "minimum" : "maximum", + (object_size_type & 1) ? "sub" : ""); + print_generic_expr (dump_file, ptr, dump_flags); + fprintf (dump_file, ":\n"); + } + + osi.visited = BITMAP_ALLOC (NULL); + osi.object_size_type = object_size_type; + + collect_object_sizes_for (&osi, ptr); + + /* Debugging dumps. */ + if (dump_file) + { + EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi) + if (object_sizes[object_size_type][i] != NULL_TREE) + { + print_generic_expr (dump_file, ssa_name (i), + dump_flags); + fprintf (dump_file, ": %s dynamic %sobject size ", + (object_size_type & 2) ? "minimum" : "maximum", + (object_size_type & 1) ? "sub" : ""); + print_generic_expr (dump_file, object_sizes[object_size_type][i], + dump_flags); + fprintf (dump_file, ":\n"); + } + } + + BITMAP_FREE (osi.visited); + +out: + *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)]; + return *psize != NULL_TREE; +} + + +/* Use size of ORIG for DEST and return it. */ + +static void +set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig) +{ + collect_object_sizes_for (osi, orig); + object_sizes[osi->object_size_type][SSA_NAME_VERSION (dest)] = + object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)]; +} + + +/* Compute object_sizes for PTR, defined to the result of a call. */ + +static void +call_object_size (struct object_size_info *osi, tree ptr, gcall *call) +{ + unsigned int varno = SSA_NAME_VERSION (ptr); + + gcc_assert (is_gimple_call (call)); + + object_sizes[osi->object_size_type][varno] = alloc_object_size (call); +} + + +/* Compute object sizes for VAR. + + - For allocation GIMPLE_CALL like malloc or calloc object size is the size + of the allocation. + - For a memcpy like GIMPLE_CALL that always returns one of its arguments, + the object size is object size of that argument. */ + +static void +collect_object_sizes_for (struct object_size_info *osi, tree var) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (var); + gimple *stmt; + + if (bitmap_bit_p (computed[object_size_type], varno)) + return; + + if (bitmap_set_bit (osi->visited, varno)) + object_sizes[object_size_type][varno] = NULL_TREE; + else + { + /* No dependency loop handling at the moment. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found a dependency loop at "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, "\n"); + } + return; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Visiting use-def links for "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, "\n"); + } + + stmt = SSA_NAME_DEF_STMT (var); + + switch (gimple_code (stmt)) + { + case GIMPLE_CALL: + { + gcall *call_stmt = as_a (stmt); + tree arg = pass_through_call (call_stmt); + if (arg) + { + if (TREE_CODE (arg) == SSA_NAME) + set_object_size_ssa (osi, var, arg); + } + else + call_object_size (osi, var, call_stmt); + break; + } + + /* Bail out for all other cases. */ + case GIMPLE_NOP: + case GIMPLE_PHI: + case GIMPLE_ASSIGN: + case GIMPLE_ASM: + break; + + default: + gcc_unreachable (); + } + + bitmap_set_bit (computed[object_size_type], varno); +} + + +/* Initialize data structures for the object size computation. */ + +void +init_dynamic_object_sizes (void) +{ + int object_size_type; + + if (computed[0]) + return; + + for (object_size_type = 0; object_size_type <= 3; object_size_type++) + { + object_sizes[object_size_type].safe_grow (num_ssa_names, true); + computed[object_size_type] = BITMAP_ALLOC (NULL); + } +} + + +/* Destroy data structures after the object size computation. */ + +void +fini_dynamic_object_sizes (void) +{ + int object_size_type; + + for (object_size_type = 0; object_size_type <= 3; object_size_type++) + { + object_sizes[object_size_type].release (); + BITMAP_FREE (computed[object_size_type]); + } +} + +unsigned int +dynamic_object_sizes_execute (function *fun, bool lower_to_bos) +{ + basic_block bb; + + init_dynamic_object_sizes (); + + FOR_EACH_BB_FN (bb, fun) + { + gimple_stmt_iterator i; + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + { + gimple *call = gsi_stmt (i); + + if (!gimple_call_builtin_p (call, BUILT_IN_DYN_OBJECT_SIZE)) + continue; + + tree lhs = gimple_call_lhs (call); + if (!lhs) + continue; + + unsigned numargs = gimple_call_num_args (call); + tree *args = XALLOCAVEC (tree, numargs); + args[0] = gimple_call_arg (call, 0); + args[1] = gimple_call_arg (call, 1); + + location_t loc = EXPR_LOC_OR_LOC (args[0], input_location); + tree result_type = gimple_call_return_type (as_a (call)); + tree result = fold_builtin_call_array (loc, result_type, + gimple_call_fn (call), + numargs, args); + + if (result) + { + /* fold_builtin_call_array may wrap the result inside a + NOP_EXPR. */ + STRIP_NOPS (result); + gimplify_and_update_call_from_tree (&i, result); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Simplified builtin in\n "); + print_gimple_stmt (dump_file, call, 0, dump_flags); + fprintf (dump_file, " to "); + print_generic_expr (dump_file, result); + fprintf (dump_file, "\n"); + } + } + else if (lower_to_bos) + { + /* If we could not find a suitable size expression, lower to + __builtin_object_size so that we may at least get a constant + lower or higher estimate. */ + tree bosfn = builtin_decl_implicit (BUILT_IN_OBJECT_SIZE); + gimple_call_set_fndecl (call, bosfn); + gimple_call_set_arg (call, 0, args[0]); + gimple_call_set_arg (call, 1, args[1]); + update_stmt (call); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_expr (dump_file, args[0], dump_flags); + fprintf (dump_file, + ": Simplified to __builtin_object_size\n"); + } + } + } + } + + fini_dynamic_object_sizes (); + return 0; +} + +/* Evaluate __builtin_dynamic_object_size () builtins early. */ + +namespace { + +const pass_data pass_data_early_dynamic_object_sizes = +{ + GIMPLE_PASS, /* type */ + "early_dynobjsz", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_early_dynamic_object_sizes : public gimple_opt_pass +{ +public: + pass_early_dynamic_object_sizes (gcc::context *ctxt) + : gimple_opt_pass (pass_data_early_dynamic_object_sizes, ctxt) + {} + + /* opt_pass methods: */ + opt_pass * clone () { return new pass_early_dynamic_object_sizes (m_ctxt); } + virtual unsigned int execute (function *fun) + { + return dynamic_object_sizes_execute (fun, false); + } +}; // class pass_early_dynamic_object_sizes + +} // anon namespace + +gimple_opt_pass * +make_pass_early_dynamic_object_sizes (gcc::context *ctxt) +{ + return new pass_early_dynamic_object_sizes (ctxt); +} + +/* Evaluate __builtin_dynamic_object_size () builtins, simplifying to + __builtin_object_size () if a size expression cannot be produced. */ + +namespace { + +const pass_data pass_data_dynamic_object_sizes = +{ + GIMPLE_PASS, /* type */ + "dynobjsz", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_dynamic_object_sizes : public gimple_opt_pass +{ +public: + pass_dynamic_object_sizes (gcc::context *ctxt) + : gimple_opt_pass (pass_data_dynamic_object_sizes, ctxt) + {} + + /* opt_pass methods: */ + opt_pass * clone () { return new pass_dynamic_object_sizes (m_ctxt); } + virtual unsigned int execute (function *fun) + { + return dynamic_object_sizes_execute (fun, true); + } +}; // class pass_dynamic_object_sizes + +} // anon namespace + +gimple_opt_pass * +make_pass_dynamic_object_sizes (gcc::context *ctxt) +{ + return new pass_dynamic_object_sizes (ctxt); +} diff --git a/gcc/tree-dynamic-object-size.h b/gcc/tree-dynamic-object-size.h new file mode 100644 index 00000000000..145b4b88bca --- /dev/null +++ b/gcc/tree-dynamic-object-size.h @@ -0,0 +1,25 @@ +/* Declarations for tree-dynamic-object-size.c. + Copyright (C) 2021 Siddhesh Poyarekar + +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 +. */ + +#ifndef GCC_TREE_DYNAMIC_OBJECT_SIZE_H +#define GCC_TREE_DYNAMIC_OBJECT_SIZE_H + +extern bool compute_builtin_dyn_object_size (tree, int, tree *); + +#endif // GCC_TREE_DYNAMIC_OBJECT_SIZE_H diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 84477a47b88..d81460d0703 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -430,7 +430,9 @@ extern gimple_opt_pass *make_pass_oacc_loop_designation (gcc::context *ctxt); extern gimple_opt_pass *make_pass_omp_oacc_neuter_broadcast (gcc::context *ctxt); extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt); extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_dynamic_object_sizes (gcc::context *ctxt); extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_early_dynamic_object_sizes (gcc::context *ctxt); extern gimple_opt_pass *make_pass_early_object_sizes (gcc::context *ctxt); extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt); extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt); From patchwork Thu Oct 7 22:14:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45974 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0392E3858001 for ; Thu, 7 Oct 2021 22:16:50 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from antelope.elm.relay.mailchannels.net (antelope.elm.relay.mailchannels.net [23.83.212.4]) by sourceware.org (Postfix) with ESMTPS id 010643857C5D for ; Thu, 7 Oct 2021 22:15:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 010643857C5D Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 823CB362007; Thu, 7 Oct 2021 22:15:01 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-18-141.trex.outbound.svc.cluster.local [100.96.18.141]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 0F7943619D2; Thu, 7 Oct 2021 22:15:01 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.18.141 (trex/6.4.3); Thu, 07 Oct 2021 22:15:01 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Eight-Exultant: 02ae02d461db395a_1633644901317_2194276729 X-MC-Loop-Signature: 1633644901316:1896629120 X-MC-Ingress-Time: 1633644901316 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id B8EC47F2D7; Thu, 7 Oct 2021 15:15:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=sI4Vw+f7DfsHfbUQw5c g3Gsvw/E=; b=xJSN6wRUQoSLYccUvRwDXguanM5/kqu+MmrNacNy1IawyzbKpvu /bIfZYVJUL26dpIB8qxiqzV0sOgLeoQ5R21Ri9PVsKBeHxso0k854oRLXNBKPOZM j9ibWPvJC3S/T5Gk+p0xaTAcAtLE2rzfVKhop97icKD0I8HYhU2oNkx0= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 041898348D; Thu, 7 Oct 2021 15:14:56 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 3/8] tree-dynamic-object-size: Handle GIMPLE_PHI Date: Fri, 8 Oct 2021 03:44:27 +0530 Message-Id: <20211007221432.1029249-4-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3037.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, PDS_OTHER_BAD_TLD, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Where the target object is a PHI node, set the size to be a PHI node with sizes of the target object. All object size information is now split into an SSA name and its size expression. This allows non-SSA size expressions to be evaluated all at once in the end and generated statements inserted into the right places before returning the SSA tree representing the size. This change also adds support for dependency loops since that is a common occurrence with PHI nodes. Dependency loop handling is single pass unlike tree-object-size due to the separation of size SSA names and actual expressions, which allow for placeholders and unresolved dependency loop counting in case of self-reference. The downside though is that if there's an unresolved dependency loop and the object size computation fails, all of the computations are scrapped. Ideally only computations involved in the unresolved dependency loop should be scrapped but I don't know if it's worth the extra state that would have to be managed. We could revisit this if it ends up being a bottleneck. gcc/ChangeLog: * tree-dynamic-object-size.c: Include gimplify-me.h. (object_size_info): New member deploop. (object_size_exprs): New vector of object size expressions. (emit_size_stmts, emit_size_phi_stmts, eval_size_expr, gimplify_size_exprs): New functions. (compute_builtin_dyn_object_size): New argument FUN. Grow object_size_exprs and call gimplify_size_exprs. (maybe_update_dependency_loop, cache_object_size, cache_object_size_copy): New functions. (set_object_size_ssa, call_object_size): Call cache_object_size_copy. (collect_object_sizes_for): Handle GIMPLE_PHI. (init_dynamic_object_sizes): Allocate object_size_exprs. (fini_dynamic_object_sizes): Deallocate object_size_exprs. * gcc/tree-dynamic-object-size.h (compute_builtin_dyn_object_size): New argument. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_builtin_malloc_condphi, test_builtin_malloc_condphi2, test_builtin_malloc_condphi3, test_builtin_calloc_condphi, test_deploop): New tests. (main): Call them. Signed-off-by: Siddhesh Poyarekar --- .../gcc.dg/builtin-dynamic-object-size-0.c | 92 +++++++ gcc/tree-dynamic-object-size.c | 258 +++++++++++++++++- gcc/tree-dynamic-object-size.h | 3 +- 3 files changed, 339 insertions(+), 14 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index 620e8cbc611..7098ef485c6 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -63,6 +63,48 @@ test_builtin_malloc_cond (int cond) return __builtin_dynamic_object_size (ret, 0); } +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi (int cond) +{ + void *ret; + + if (cond) + ret = __builtin_malloc (32); + else + ret = __builtin_malloc (64); + + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi2 (int cond, size_t in) +{ + void *ret; + + if (cond) + ret = __builtin_malloc (in); + else + ret = __builtin_malloc (64); + + return __builtin_dynamic_object_size (ret, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2) +{ + void *ret; + + if (cond) + ret = __builtin_malloc (in); + else + ret = __builtin_malloc (in2); + + return __builtin_dynamic_object_size (ret, 0); +} + /* Calloc-like allocator. */ size_t @@ -89,6 +131,21 @@ test_builtin_calloc_cond (int cond1, int cond2) return __builtin_dynamic_object_size (ret, 0); } +size_t +__attribute__ ((noinline)) +test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond) +{ + struct + { + int a; + char b; + } bin[cnt]; + + char *ch = __builtin_calloc (cnt, sz); + + return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0); +} + /* Passthrough functions. */ size_t @@ -120,6 +177,19 @@ test_dynarray_cond (int cond) return __builtin_dynamic_object_size (bin, 0); } +size_t +__attribute__ ((noinline)) +test_deploop (size_t sz, size_t cond) +{ + char *bin = __builtin_alloca (32); + + for (size_t i = 0; i < sz; i++) + if (i == cond) + bin = __builtin_alloca (sz); + + return __builtin_dynamic_object_size (bin, 0); +} + int main (int argc, char **argv) { @@ -141,6 +211,18 @@ main (int argc, char **argv) FAIL (); if (test_builtin_malloc_cond (0) != 64) FAIL (); + if (test_builtin_malloc_condphi (1) != 32) + FAIL (); + if (test_builtin_malloc_condphi (0) != 64) + FAIL (); + if (test_builtin_malloc_condphi2 (1, 128) != 128) + FAIL (); + if (test_builtin_malloc_condphi2 (0, 128) != 64) + FAIL (); + if (test_builtin_malloc_condphi3 (1, 128, 256) != 128) + FAIL (); + if (test_builtin_malloc_condphi3 (0, 128, 256) != 256) + FAIL (); if (test_calloc (2048, 4) != 2048 * 4) FAIL (); if (test_builtin_calloc (2048, 8) != 2048 * 8) @@ -149,6 +231,12 @@ main (int argc, char **argv) FAIL (); if (test_builtin_calloc_cond (1, 1) != 32 * 1024) FAIL (); + if (test_builtin_calloc_condphi (128, 1, 1) != 128) + FAIL (); + if (test_builtin_calloc_condphi (128, 1, 0) == 128) + FAIL (); + if (test_builtin_calloc_condphi (128, 1, 0) == -1) + FAIL (); if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0]) != __builtin_strlen (argv[0]) + 1) FAIL (); @@ -158,6 +246,10 @@ main (int argc, char **argv) FAIL (); if (test_dynarray_cond (1) != 8) FAIL (); + if (test_deploop (128, 4) != 128) + FAIL (); + if (test_deploop (128, 129) != 32) + FAIL (); if (nfails > 0) __builtin_abort (); diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index 8e52dd46c03..483d6782d49 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -62,11 +62,14 @@ along with GCC; see the file COPYING3. If not see #include "attribs.h" #include "builtins.h" #include "print-tree.h" +#include "gimplify-me.h" struct object_size_info { + struct function *fun; int object_size_type; bitmap visited; + unsigned deploop; }; static tree alloc_object_size (const gcall *); @@ -78,9 +81,15 @@ static void collect_object_sizes_for (struct object_size_info *, tree); object_sizes[1] is upper bound for number of bytes till the end of the subobject (innermost array or field with address taken). object_sizes[2] is lower bound for number of bytes till the end of - the object and object_sizes[3] lower bound for subobject. */ + the object and object_sizes[3] lower bound for subobject. + + These are merely SSA names of the sizes. The actual size expressions are in + object_size_exprs and they need not be SSA. */ static vec object_sizes[4]; +/* The actual size expressions, indexed by the object SSA names. */ +static vecobject_size_exprs[4]; + /* Bitmaps what object sizes have been computed already. */ static bitmap computed[4]; @@ -166,13 +175,123 @@ pass_through_call (const gcall *call) return NULL_TREE; } +static void +emit_size_stmts (gimple *stmt, tree size_ssa, tree size_expr) +{ + gimple_seq seq = NULL; + + if (!is_gimple_variable (size_expr)) + size_expr = force_gimple_operand (size_expr, &seq, true, NULL); + + gassign *assign = gimple_build_assign (size_ssa, size_expr); + gimple_seq_add_stmt (&seq, assign); + + /* Define object size right after the object is defined. */ + gimple_stmt_iterator i = gsi_for_stmt (stmt); + gsi_insert_seq_after (&i, seq, GSI_CONTINUE_LINKING); +} + +static void +emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes) +{ + gphi *newphi = create_phi_node (size_ssa, gimple_bb (stmt)); + gphi *obj_phi = as_a (stmt); + + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + { + if (!is_gimple_variable (sizes[i])) + { + gimple_seq seq; + edge e = gimple_phi_arg_edge (obj_phi, i); + sizes[i] = force_gimple_operand (sizes[i], &seq, true, NULL); + + /* Put the size definition before the last statement of the source + block of the PHI edge. This ensures that any branches at the end + of the source block remain the last statement. We are OK even if + the last statement is the definition of the object since it will + succeed any definitions that contribute to its size and the size + expression will succeed them too. */ + gimple_stmt_iterator gsi = gsi_last_bb (e->src); + gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING); + } + + add_phi_arg (newphi, sizes[i], + gimple_phi_arg_edge (obj_phi, i), + gimple_phi_arg_location (obj_phi, i)); + } +} + +static void +eval_size_expr (tree var, tree size, tree *size_expr) +{ + if (size_expr != NULL) + { + gcc_assert (*size_expr != error_mark_node); + + gimple *stmt = SSA_NAME_DEF_STMT (var); + + if (gimple_code (stmt) == GIMPLE_PHI) + { + emit_size_phi_stmts (stmt, size, size_expr); + delete[] size_expr; + } + else + { + emit_size_stmts (stmt, size, *size_expr); + delete size_expr; + } + } +} + +static void +gimplify_size_exprs (object_size_info *osi, tree ptr) +{ + bitmap_iterator bi; + unsigned i; + + /* If the size lookup was not successful and we were left with unresolved + loop dependencies, then invalidate all size computations. This is + suboptimal and should eventually try to remove only size expressions that + depend on the unresolved dependencies, but it's unclear whether + maintaining the extra state to manage that is worthwhile. */ + if (object_sizes[osi->object_size_type][SSA_NAME_VERSION (ptr)] == NULL_TREE + && osi->deploop) + { + for (int object_size_type = 0; object_size_type <= 3; object_size_type++) + { + EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi) + { + release_ssa_name (object_sizes[object_size_type][i]); + if (gimple_code (SSA_NAME_DEF_STMT (ssa_name (i))) == GIMPLE_PHI + && object_size_exprs[object_size_type][i]) + delete [] object_size_exprs[object_size_type][i]; + else + delete object_size_exprs[object_size_type][i]; + object_size_exprs[object_size_type][i] = NULL; + } + bitmap_clear (computed[object_size_type]); + } + return; + } + + /* Gimplify and emit code for all computed size expressions. */ + for (int object_size_type = 0; object_size_type <= 3; object_size_type++) + EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi) + { + eval_size_expr (ssa_name (i), object_sizes[object_size_type][i], + object_size_exprs[object_size_type][i]); + object_size_exprs[object_size_type][i] = NULL; + } +} + /* Compute object size estimate for PTR and set *PSIZE to the resulting value. OBJECT_SIZE_TYPE is the second argument to __builtin_dynamic_object_size. Returns true on success and false when the object size could not be determined. */ bool -compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize) +compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize, + struct function *fun) { gcc_assert (object_size_type >= 0 && object_size_type <= 3); @@ -195,7 +314,10 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize) unsigned int i; if (num_ssa_names > object_sizes[object_size_type].length ()) - object_sizes[object_size_type].safe_grow (num_ssa_names, true); + { + object_sizes[object_size_type].safe_grow (num_ssa_names, true); + object_size_exprs[object_size_type].safe_grow (num_ssa_names, true); + } if (dump_file) { fprintf (dump_file, "Computing %s dynamic %sobject size for ", @@ -206,9 +328,12 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize) } osi.visited = BITMAP_ALLOC (NULL); + osi.deploop = 0; osi.object_size_type = object_size_type; + osi.fun = fun != NULL ? fun : cfun; collect_object_sizes_for (&osi, ptr); + gimplify_size_exprs (&osi, ptr); /* Debugging dumps. */ if (dump_file) @@ -234,6 +359,73 @@ out: return *psize != NULL_TREE; } +static void +maybe_update_dependency_loop (struct object_size_info *osi, unsigned name, + tree sz) +{ + if (sz == error_mark_node) + osi->deploop++; + else if (object_sizes[osi->object_size_type][name] + && (*object_size_exprs[osi->object_size_type][name] + == error_mark_node)) + osi->deploop--; +} + +/* Add object size to the cache so that it can be reused. */ + +static void +cache_object_size (struct object_size_info *osi, unsigned name, tree *sz) +{ + int object_size_type = osi->object_size_type; + struct function *fun = osi->fun; + + gcc_assert (sz); + + maybe_update_dependency_loop (osi, name, *sz); + + /* Reuse SSA name if it was created for a dependency loop. */ + if (object_sizes[object_size_type][name] != NULL_TREE) + gcc_assert (*object_size_exprs[object_size_type][name] == error_mark_node); + else + object_sizes[object_size_type][name] = make_ssa_name_fn (fun, sizetype, 0); + object_size_exprs[object_size_type][name] = sz; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Caching size for "); + print_generic_expr (dump_file, ssa_name (name), dump_flags); + fprintf (dump_file, ":: Object size: "); + print_generic_expr (dump_file, object_sizes[object_size_type][name], + dump_flags); + fprintf (dump_file, " = "); + print_generic_expr (dump_file, + *object_size_exprs[object_size_type][name], + dump_flags); + fprintf (dump_file, "\n"); + } + + bitmap_set_bit (computed[object_size_type], name); +} + +/* Copy SZ and then call cache_object_size above. */ + +static void +cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz) +{ + int object_size_type = osi->object_size_type; + + if (sz == NULL_TREE) + { + if (object_sizes[object_size_type][name] != NULL_TREE) + release_ssa_name (object_sizes[object_size_type][name]); + object_sizes[object_size_type][name] = NULL_TREE; + object_size_exprs[object_size_type][name] = NULL; + bitmap_set_bit (computed[object_size_type], name); + return; + } + + cache_object_size (osi, name, new tree (sz)); +} /* Use size of ORIG for DEST and return it. */ @@ -241,8 +433,9 @@ static void set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig) { collect_object_sizes_for (osi, orig); - object_sizes[osi->object_size_type][SSA_NAME_VERSION (dest)] = - object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)]; + + tree sz = object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)]; + cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz); } @@ -251,11 +444,10 @@ set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig) static void call_object_size (struct object_size_info *osi, tree ptr, gcall *call) { - unsigned int varno = SSA_NAME_VERSION (ptr); - gcc_assert (is_gimple_call (call)); - object_sizes[osi->object_size_type][varno] = alloc_object_size (call); + cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), + alloc_object_size (call)); } @@ -264,7 +456,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) - For allocation GIMPLE_CALL like malloc or calloc object size is the size of the allocation. - For a memcpy like GIMPLE_CALL that always returns one of its arguments, - the object size is object size of that argument. */ + the object size is object size of that argument. + - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI + node of the object. */ static void collect_object_sizes_for (struct object_size_info *osi, tree var) @@ -280,7 +474,10 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) object_sizes[object_size_type][varno] = NULL_TREE; else { - /* No dependency loop handling at the moment. */ + /* Add an SSA name but mark the expression as being an error_mark_node. + When we go back up the stack, the error_mark_node should get + overwritten by a proper expression. */ + cache_object_size (osi, varno, &error_mark_node); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Found a dependency loop at "); @@ -315,18 +512,51 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) break; } + case GIMPLE_PHI: + { + unsigned i, num_args = gimple_phi_num_args (stmt); + tree *sizes = new tree[num_args] (); + + /* Bail out if any of the PHI arguments are non-SSA expressions or + if size of an argument cannot be determined. */ + for (i = 0; i < gimple_phi_num_args (stmt); i++) + { + tree rhs = gimple_phi_arg_def (stmt, i); + + if (TREE_CODE (rhs) != SSA_NAME) + break; + + collect_object_sizes_for (osi, rhs); + tree sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)]; + + if (sz == NULL_TREE) + break; + + sizes[i] = sz; + } + + /* Record all possible sizes to build our PHI node later. */ + if (i == gimple_phi_num_args (stmt)) + { + cache_object_size (osi, varno, sizes); + break; + } + else + delete[] sizes; + } + /* FALLTHROUGH */ + /* Bail out for all other cases. */ case GIMPLE_NOP: - case GIMPLE_PHI: case GIMPLE_ASSIGN: case GIMPLE_ASM: + cache_object_size_copy (osi, varno, NULL_TREE); break; default: gcc_unreachable (); } - - bitmap_set_bit (computed[object_size_type], varno); + gcc_assert (bitmap_bit_p (computed[object_size_type], varno)); } @@ -343,6 +573,7 @@ init_dynamic_object_sizes (void) for (object_size_type = 0; object_size_type <= 3; object_size_type++) { object_sizes[object_size_type].safe_grow (num_ssa_names, true); + object_size_exprs[object_size_type].safe_grow (num_ssa_names, true); computed[object_size_type] = BITMAP_ALLOC (NULL); } } @@ -358,6 +589,7 @@ fini_dynamic_object_sizes (void) for (object_size_type = 0; object_size_type <= 3; object_size_type++) { object_sizes[object_size_type].release (); + object_size_exprs[object_size_type].release (); BITMAP_FREE (computed[object_size_type]); } } diff --git a/gcc/tree-dynamic-object-size.h b/gcc/tree-dynamic-object-size.h index 145b4b88bca..a4e0236e43e 100644 --- a/gcc/tree-dynamic-object-size.h +++ b/gcc/tree-dynamic-object-size.h @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_TREE_DYNAMIC_OBJECT_SIZE_H #define GCC_TREE_DYNAMIC_OBJECT_SIZE_H -extern bool compute_builtin_dyn_object_size (tree, int, tree *); +extern bool compute_builtin_dyn_object_size (tree, int, tree *, + struct function * = NULL); #endif // GCC_TREE_DYNAMIC_OBJECT_SIZE_H From patchwork Thu Oct 7 22:14:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45976 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 79F2E385742F for ; Thu, 7 Oct 2021 22:17:50 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from quail.birch.relay.mailchannels.net (quail.birch.relay.mailchannels.net [23.83.209.151]) by sourceware.org (Postfix) with ESMTPS id 66D3E3857825 for ; Thu, 7 Oct 2021 22:15:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 66D3E3857825 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 0D9F0922E92; Thu, 7 Oct 2021 22:15:12 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-18-141.trex.outbound.svc.cluster.local [100.96.18.141]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 08799922E91; Thu, 7 Oct 2021 22:15:08 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.18.141 (trex/6.4.3); Thu, 07 Oct 2021 22:15:12 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Cellar-Macabre: 76f9bfeb1ea48ef1_1633644908323_3643191777 X-MC-Loop-Signature: 1633644908323:3941454572 X-MC-Ingress-Time: 1633644908323 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id BAB5683495; Thu, 7 Oct 2021 15:15:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=xoCTAd2pCRh0oKsJbHZ VOPSANtU=; b=LizX6vtaIbGzWmbKrKDT1fcM+z7sgnoZ2xVwqrqQjM9rsf5ghlO X22Jx6ZJMYJsde1/ohgjZE1W7W38++V6VvuMESr7EU8Tj8N5AON/LCUZoGAaHDpC MhUvyjq+Uhc2OlO26sZkKmzRKuP5mK7S0C7rJcqchlhmWeInWUk7Feb0= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 5D2748348D; Thu, 7 Oct 2021 15:15:01 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 4/8] tree-dynamic-object-size: Support ADDR_EXPR Date: Fri, 8 Oct 2021 03:44:28 +0530 Message-Id: <20211007221432.1029249-5-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3039.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This is the beginnings of support for ADDR_EXPR objects and is partly based on the implementation in tree-object-size, splitting out functions for readability. One key difference from constant-size ADDR_EXPR computation is the computation of residual sizes based on offset. This pass attempts to compute an expression with guards to try and not overflow. This is however still rudimentary and a subsequent patch for subobject support makes it more comprehensive by handling negative offsets as well. The size expressions based on offsets may look arbitrarily complex but in practice most parts of the expression tend to fold away due to being constants. Despite that it is still a potential bottleneck and may need some artifical backstop (such as bailing out on computation if the size expression nests more than an arbitrarily chosen N levels) to reduce compile time as well as avoid adding too much of a performance overhead to generated code. gcc/ChangeLog: * builtins.c (fold_builtin_dyn_object_size): Handle ADDR_EXPR. * tree-dynamic-object-size.c (compute_object_offset, size_for_offset, get_whole_var, whole_var_size, addr_dyn_object_size): New functions. (compute_builtin_dyn_object_size): Handle ADDR_EXPR. (expr_object_size): New function. (collect_object_sizes_for): Use it. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_builtin_malloc_condphi4, test_builtin_malloc_condphi5, test_passthrough_nonssa): New tests. (main): Call them. Signed-off-by: Siddhesh Poyarekar --- gcc/builtins.c | 2 +- .../gcc.dg/builtin-dynamic-object-size-0.c | 37 +++ gcc/tree-dynamic-object-size.c | 255 +++++++++++++++++- 3 files changed, 287 insertions(+), 7 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index d015029765b..c1e23324552 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -10339,7 +10339,7 @@ fold_builtin_dyn_object_size (tree ptr, tree ost) /* If object size expression cannot be evaluated yet, delay folding until later. Maybe subsequent passes will help determining it. */ - if (TREE_CODE (ptr) == SSA_NAME + if ((TREE_CODE (ptr) == SSA_NAME || TREE_CODE (ptr) == ADDR_EXPR) && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes)) return bytes; diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index 7098ef485c6..22c86190341 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -105,6 +105,25 @@ test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2) return __builtin_dynamic_object_size (ret, 0); } +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi4 (size_t sz, int cond) +{ + char *a = __builtin_malloc (sz); + char b[sz / 2]; + + return __builtin_dynamic_object_size (cond ? b : (void *) &a, 0); +} + +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi5 (size_t sz, int cond, char *c) +{ + char *a = __builtin_malloc (sz); + + return __builtin_dynamic_object_size (cond ? c : (void *) &a, 0); +} + /* Calloc-like allocator. */ size_t @@ -158,6 +177,16 @@ test_passthrough (size_t sz, char *in) return __builtin_dynamic_object_size (dest, 0); } +size_t +__attribute__ ((noinline)) +test_passthrough_nonssa (char *in) +{ + char bin[__builtin_strlen (in) + 1]; + char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1); + + return __builtin_dynamic_object_size (dest, 0); +} + /* Variable length arrays. */ size_t __attribute__ ((noinline)) @@ -223,6 +252,12 @@ main (int argc, char **argv) FAIL (); if (test_builtin_malloc_condphi3 (0, 128, 256) != 256) FAIL (); + if (test_builtin_malloc_condphi4 (128, 1) != 64) + FAIL (); + if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *)) + FAIL (); + if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1) + FAIL (); if (test_calloc (2048, 4) != 2048 * 4) FAIL (); if (test_builtin_calloc (2048, 8) != 2048 * 8) @@ -240,6 +275,8 @@ main (int argc, char **argv) if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0]) != __builtin_strlen (argv[0]) + 1) FAIL (); + if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1) + FAIL (); if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0])) FAIL (); if (test_dynarray_cond (0) != 16) diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index 483d6782d49..6cc63abd010 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -93,6 +93,212 @@ static vecobject_size_exprs[4]; /* Bitmaps what object sizes have been computed already. */ static bitmap computed[4]; +bool compute_builtin_dyn_object_size (tree, int, tree *, + struct function *fun = NULL); + +/* Compute offset of EXPR within VAR. Return error_mark_node if unknown. */ + +static tree +compute_object_offset (const_tree expr, const_tree var) +{ + enum tree_code code = PLUS_EXPR; + tree base, off, t; + + if (expr == var) + return size_zero_node; + + switch (TREE_CODE (expr)) + { + case COMPONENT_REF: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + t = TREE_OPERAND (expr, 1); + off = fold_build2 (PLUS_EXPR, sizetype, DECL_FIELD_OFFSET (t), + size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t)) + / BITS_PER_UNIT)); + break; + + case REALPART_EXPR: + CASE_CONVERT: + case VIEW_CONVERT_EXPR: + case NON_LVALUE_EXPR: + return compute_object_offset (TREE_OPERAND (expr, 0), var); + + case IMAGPART_EXPR: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + off = TYPE_SIZE_UNIT (TREE_TYPE (expr)); + break; + + case ARRAY_REF: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + t = TREE_OPERAND (expr, 1); + tree low_bound, unit_size; + low_bound = array_ref_low_bound (CONST_CAST_TREE (expr)); + unit_size = array_ref_element_size (CONST_CAST_TREE (expr)); + if (! integer_zerop (low_bound)) + t = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound); + if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0) + { + code = MINUS_EXPR; + t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t); + } + t = fold_convert (sizetype, t); + off = fold_build2 (MULT_EXPR, sizetype, unit_size, t); + break; + + case MEM_REF: + gcc_assert (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR); + return wide_int_to_tree (sizetype, mem_ref_offset (expr)); + + default: + return error_mark_node; + } + + return fold_build2 (code, sizetype, base, off); +} + +/* Given an object size SZ and offset OFF into it, compute the usable object + size. The expression returns 0 for all offsets that invoke undefined + behaviour. */ + +static tree +size_for_offset (tree sz, tree off) +{ + /* A MEM_REF offset may be a pointer, where we need to figure out the + multiplier based on the base type. */ + if (POINTER_TYPE_P (TREE_TYPE (off))) + { + tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off))); + + if (typesize) + { + /* SZ < TYPESIZE ? SZ : TYPESIZE * MIN (SZ / TYPESIZE, OFF) */ + tree cond = fold_build2 (LT_EXPR, sizetype, sz, typesize); + + tree tmp = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize); + tmp = fold_build2 (MIN_EXPR, sizetype, tmp, off); + tmp = fold_build2 (MULT_EXPR, sizetype, tmp, typesize); + + off = fold_build3 (COND_EXPR, sizetype, cond, sz, tmp); + + return fold_build2 (MINUS_EXPR, sizetype, sz, off); + } + else + off = fold_convert (sizetype, off); + } + + off = fold_build2 (MIN_EXPR, sizetype, sz, off); + return fold_build2 (MINUS_EXPR, sizetype, sz, off); +} + +/* Peek through ADDR_EXPR operands to get the definition of the whole variable + PTR points in. Write the result expression into PT_VARP and its size into + PT_VAR_SIZEP. Return true if the object is found. */ + +static tree +get_whole_var (const_tree ptr) +{ + tree pt_var; + + pt_var = TREE_OPERAND (ptr, 0); + while (handled_component_p (pt_var)) + pt_var = TREE_OPERAND (pt_var, 0); + + return pt_var; +} + +static bool +whole_var_size (struct object_size_info *osi, tree pt_var, + int object_size_type, tree *pt_var_sizep) +{ + tree pt_var_size = NULL_TREE; + int subobject = object_size_type & 1; + int min = object_size_type & 2; + + if (TREE_CODE (pt_var) == MEM_REF) + { + tree var = TREE_OPERAND (pt_var, 0); + if (!osi || subobject || TREE_CODE (var) != SSA_NAME) + compute_builtin_dyn_object_size (var, min, &pt_var_size); + else + { + collect_object_sizes_for (osi, var); + pt_var_size = object_sizes[object_size_type][SSA_NAME_VERSION (var)]; + } + + if (pt_var_size != NULL_TREE) + { + tree offset = wide_int_to_tree (size_type_node, + mem_ref_offset (pt_var)); + + pt_var_size = size_for_offset (pt_var_size, offset); + } + } + else if (DECL_P (pt_var)) + { + pt_var_size = decl_init_size (pt_var, min); + if (!pt_var_size) + return false; + } + else if (TREE_CODE (pt_var) == STRING_CST) + pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var)); + else + return false; + + *pt_var_sizep = pt_var_size; + return true; +} + +/* Compute an object size estimate for PTR, which is a ADDR_EXPR. + OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size. + If unknown, return false, setting PSIZE to NULL_TREE. */ + +static bool +addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, + int object_size_type, tree *psize) +{ + tree pt_var, pt_var_size = NULL_TREE, bytes; + + gcc_assert (TREE_CODE (ptr) == ADDR_EXPR); + + /* Set to unknown and overwrite just before returning if the size + could be determined. */ + *psize = NULL_TREE; + + pt_var = get_whole_var (ptr); + + if (!pt_var) + return false; + + if (!whole_var_size (osi, pt_var, object_size_type, &pt_var_size)) + return false; + + if (!pt_var_size) + return false; + + /* PTR points to a subobject of whole variable PT_VAR. */ + if (pt_var != TREE_OPERAND (ptr, 0)) + { + bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); + if (bytes != error_mark_node) + bytes = size_for_offset (pt_var_size, bytes); + } + else + bytes = pt_var_size; + + *psize = bytes == error_mark_node ? NULL_TREE : bytes; + return true; +} + + /* Compute __builtin_dynamic_object_size for CALL, which is a GIMPLE_CALL. Handles calls to functions declared with attribute alloc_size. OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size. @@ -299,6 +505,13 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize, could be determined. */ *psize = NULL_TREE; + if (TREE_CODE (ptr) == ADDR_EXPR) + /* We assume that the caller will gimplify the expression. If the + expression depends on any SSA objects, its size expression is gimplified + and returned, so the expression will definitely not depend on the cached + objects. */ + return addr_dyn_object_size (NULL, ptr, object_size_type, psize); + if (TREE_CODE (ptr) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (ptr))) return false; @@ -359,6 +572,27 @@ out: return *psize != NULL_TREE; } +/* Compute object_sizes an object defined to VALUE, which is not an + SSA_NAME. */ + +static void +expr_object_size (struct object_size_info *osi, tree value, tree *sz) +{ + int object_size_type = osi->object_size_type; + tree bytes = NULL_TREE; + + if (TREE_CODE (value) == WITH_SIZE_EXPR) + value = TREE_OPERAND (value, 0); + + if (TREE_CODE (value) == ADDR_EXPR) + addr_dyn_object_size (osi, value, object_size_type, &bytes); + + if (bytes) + STRIP_NOPS (bytes); + + *sz = bytes; +} + static void maybe_update_dependency_loop (struct object_size_info *osi, unsigned name, tree sz) @@ -506,6 +740,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) { if (TREE_CODE (arg) == SSA_NAME) set_object_size_ssa (osi, var, arg); + else + { + tree ret; + expr_object_size (osi, arg, &ret); + cache_object_size_copy (osi, varno, ret); + } } else call_object_size (osi, var, call_stmt); @@ -517,17 +757,20 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) unsigned i, num_args = gimple_phi_num_args (stmt); tree *sizes = new tree[num_args] (); - /* Bail out if any of the PHI arguments are non-SSA expressions or - if size of an argument cannot be determined. */ + /* Bail out if the size of any of the PHI arguments cannot be + determined. */ for (i = 0; i < gimple_phi_num_args (stmt); i++) { tree rhs = gimple_phi_arg_def (stmt, i); + tree sz; if (TREE_CODE (rhs) != SSA_NAME) - break; - - collect_object_sizes_for (osi, rhs); - tree sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)]; + expr_object_size (osi, rhs, &sz); + else + { + collect_object_sizes_for (osi, rhs); + sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)]; + } if (sz == NULL_TREE) break; From patchwork Thu Oct 7 22:14:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45975 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 39AFF3857818 for ; Thu, 7 Oct 2021 22:17:20 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from crocodile.elm.relay.mailchannels.net (crocodile.elm.relay.mailchannels.net [23.83.212.45]) by sourceware.org (Postfix) with ESMTPS id 273593857836 for ; Thu, 7 Oct 2021 22:15:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 273593857836 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 3787D361847; Thu, 7 Oct 2021 22:15:12 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-11-21.trex.outbound.svc.cluster.local [100.96.11.21]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 841C836226A; Thu, 7 Oct 2021 22:15:11 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.11.21 (trex/6.4.3); Thu, 07 Oct 2021 22:15:12 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Troubled-Spill: 3aa152d4033e26b8_1633644912049_3281265868 X-MC-Loop-Signature: 1633644912049:3530385356 X-MC-Ingress-Time: 1633644912049 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id E974783497; Thu, 7 Oct 2021 15:15:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=vOrUWgLEXQrQ5pPtcYD k/dfCZ+w=; b=CMuQPkopncbyNLkyvctpdNu7AcMVMsw5FijSdAXcuKw92qwvkc/ jIOyWynKSDK4sb5yahScZgYmFGE5MN+I5XXJmUQXSCaQkOVlWdDpcI01+NsWOws/ WgIdwq6iNsTYN81jQxkKmlG68Jz4b2GPwIkzvyaHHW3PVXqhg8nXmLF4= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 211EF8348D; Thu, 7 Oct 2021 15:15:05 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 5/8] tree-dynamic-object-size: Handle GIMPLE_ASSIGN Date: Fri, 8 Oct 2021 03:44:29 +0530 Message-Id: <20211007221432.1029249-6-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3039.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Follow assignment operations and pointer-plus operations to get the effective object size. Unlike tree-object-size, this pass handles negative offsets correctly and returns a size within the bounds of the parent object; offsets > SIZE_MAX / 2 are considered negative to emulate ssize_t. To make negative offsets work, all object size computations store the "whole variable" size to ensure that the negative offsets don't go beyond bounds of the object within which PTR points. As mentioned in the previous patch, this makes the size_for_offset expressions quite complex, but the aim at the moment is to be more correct than fast. We can tweak this later if it is found to have a noticeable performance impact. When gcc is invoked without optimization, attempt to minimally recognize GIMPLE_ASSIGN and ADDR_EXPR expressions so that __builtin_dynamic_object_size is at least minimally useful without optimization. gcc/ChangeLog: * tree-dynamic-object-size.c (object_whole_sizes, object_whole_size_exprs): New vectors. (size_for_offset): Support negative offsets. (whole_var_size): Adjust. (addr_dyn_object_size): New argument wholesizep. (emit_size_stmts): New arguments wholesize_ssa and wholesize_expr. Emit statements for whole size. (emit_size_phi_stmts): Likewise. (size_bound_expr): New function. (eval_size_expr): Call it. New arguments wholesize and wholesize_expr. (gimplify_size_exprs): Adjust for whole sizes. (compute_builtin_dyn_object_size): Do simple object size computation for unoptimized case. (expr_object_size, cache_object_size,cache_object_size_copy, set_object_size_ssa, call_object_size): Adjust for wholesize. (make_object_size_name, plus_stmt_object_size): New functions. (collect_object_sizes_for): Support GIMPLE_ASSIGN. Adjust for wholesize. (init_dynamic_object_sizes): Allocate object_wholesizes and object_wholesize_exprs. (fini_dynamic_object_sizes): Deallocate object_wholesizes and object_wholesize_exprs. gcc/testsuite/ChangeLog * gcc.dg/builtin-dynamic-object-size-0.c (dynarray_struct): New struct. (test_dynarray_struct, test_substring, test_substring_ptrplus): New tests. (main): Call them. Signed-off-by: Siddhesh Poyarekar --- .../gcc.dg/builtin-dynamic-object-size-0.c | 50 ++ gcc/tree-dynamic-object-size.c | 435 +++++++++++++++--- 2 files changed, 413 insertions(+), 72 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index 22c86190341..3c2c4c84264 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -219,6 +219,42 @@ test_deploop (size_t sz, size_t cond) return __builtin_dynamic_object_size (bin, 0); } +/* Address expressions. */ + +struct dynarray_struct +{ + long a; + char c[16]; + int b; +}; + +size_t +__attribute__ ((noinline)) +test_dynarray_struct (size_t sz, size_t off) +{ + struct dynarray_struct bin[sz]; + + return __builtin_dynamic_object_size (&bin[off].c, 0); +} + +size_t +__attribute__ ((noinline)) +test_substring (size_t sz, size_t off) +{ + char str[sz]; + + return __builtin_dynamic_object_size (&str[off], 0); +} + +size_t +__attribute__ ((noinline)) +test_substring_ptrplus (size_t sz, size_t off) +{ + int str[sz]; + + return __builtin_dynamic_object_size (str + off, 0); +} + int main (int argc, char **argv) { @@ -279,6 +315,20 @@ main (int argc, char **argv) FAIL (); if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0])) FAIL (); + if (test_dynarray_struct (42, 4) != + ((42 - 4) * sizeof (struct dynarray_struct) + - __builtin_offsetof (struct dynarray_struct, c))) + FAIL (); + if (test_dynarray_struct (42, 48) != 0) + FAIL (); + if (test_substring (128, 4) != 128 - 4) + FAIL (); + if (test_substring (128, 142) != 0) + FAIL (); + if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int)) + FAIL (); + if (test_substring_ptrplus (128, 142) != 0) + FAIL (); if (test_dynarray_cond (0) != 16) FAIL (); if (test_dynarray_cond (1) != 8) diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index 6cc63abd010..f143a64777c 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -83,13 +83,18 @@ static void collect_object_sizes_for (struct object_size_info *, tree); object_sizes[2] is lower bound for number of bytes till the end of the object and object_sizes[3] lower bound for subobject. - These are merely SSA names of the sizes. The actual size expressions are in + object_sizes are SSA names of the sizes. The actual size expressions are in object_size_exprs and they need not be SSA. */ static vec object_sizes[4]; - -/* The actual size expressions, indexed by the object SSA names. */ static vecobject_size_exprs[4]; +/* Indexed by object_size_type and SSA name versions of the object, + object_whole_sizes and object_whole_size_exprs have SSA names and + expressions of the whole objects in which the pointer points + respectively. */ +static vec object_whole_sizes[4]; +static vecobject_whole_size_exprs[4]; + /* Bitmaps what object sizes have been computed already. */ static bitmap computed[4]; @@ -166,37 +171,102 @@ compute_object_offset (const_tree expr, const_tree var) } /* Given an object size SZ and offset OFF into it, compute the usable object - size. The expression returns 0 for all offsets that invoke undefined - behaviour. */ + size. For cases where the offset breaches object bounds, the expression + returns 0. + + For positive offset (we asume > SIZE_MAX / 2 as negative to emulate + ssize_t) we assume that the offset, when scaled by typesize, is within + bounds of sz. That is: + + sz > typesize && off <= sz / typesize + + For negative offsets, we ensure that the magnitude of the offset is within + bounds of the lower part of the object. This ensures that underflows result + in zero size. That is: + + (off > SIZE_MAX / 2 && wholesize - min(wholesize, size) > typesize + && -off <= (wholesize - min(wholesize, size)) / typesize) + + The whole thing is then capped to wholesize to cater for situations where + wholesize may have less memory allocated to it than needed, making subobject + sizes incorrect. Effectively, the expression we generate is: + + MIN(((sz > typesize && off <= sz / typesize) + || (off > SIZE_MAX / 2 && wholesize - min(wholesize, size) > typesize + && -off <= (wholesize - min(wholesize, size)) / typesize)) + ? sz - off * typesize : 0, wholesize) + + This is a fairly complex expression that may result in slow code in corner + cases but in most standard cases, many of those elements would be constant + (typesize certainly is) and is expected to be folded away. */ static tree -size_for_offset (tree sz, tree off) +size_for_offset (tree sz, tree off, tree wholesize, bool neg_offsets = false) { + tree typesize = NULL_TREE; + tree cond = NULL_TREE; + tree max_off = NULL_TREE; + tree tmp = NULL_TREE; + /* A MEM_REF offset may be a pointer, where we need to figure out the multiplier based on the base type. */ if (POINTER_TYPE_P (TREE_TYPE (off))) + typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off))); + + /* Expression for positive offset. */ + if (typesize) { - tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off))); + cond = fold_build2 (GT_EXPR, sizetype, sz, typesize); + max_off = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize); + } + else + max_off = sz; - if (typesize) - { - /* SZ < TYPESIZE ? SZ : TYPESIZE * MIN (SZ / TYPESIZE, OFF) */ - tree cond = fold_build2 (LT_EXPR, sizetype, sz, typesize); + tmp = fold_build2 (LE_EXPR, sizetype, off, max_off); + cond = cond ? fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond, tmp) : tmp; - tree tmp = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize); - tmp = fold_build2 (MIN_EXPR, sizetype, tmp, off); - tmp = fold_build2 (MULT_EXPR, sizetype, tmp, typesize); + /* Negative offsets. */ + if (wholesize && wholesize != sz && neg_offsets) + { + /* off > SIZE_MAX / 2 */ + max_off = fold_build2 (RSHIFT_EXPR, sizetype, TYPE_MAX_VALUE (sizetype), + size_one_node); + tree cond_neg = fold_build2 (GT_EXPR, sizetype, off, max_off); - off = fold_build3 (COND_EXPR, sizetype, cond, sz, tmp); + tree neg_part = fold_build2 (MIN_EXPR, sizetype, wholesize, sz); + neg_part = fold_build2 (MINUS_EXPR, sizetype, wholesize, neg_part); - return fold_build2 (MINUS_EXPR, sizetype, sz, off); + if (typesize) + { + /* && wholesize - min(wholesize, size) > typesize */ + tmp = fold_build2 (GT_EXPR, sizetype, neg_part, typesize); + cond_neg = fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond_neg, tmp); + max_off = fold_build2 (EXACT_DIV_EXPR, sizetype, neg_part, typesize); } else - off = fold_convert (sizetype, off); + max_off = neg_part; + + /* && -off <= (wholesize - min(wholesize, size)) / typesize */ + tmp = fold_build2 (MINUS_EXPR, sizetype, size_zero_node, off); + tmp = fold_build2 (LE_EXPR, sizetype, tmp, max_off); + cond_neg = fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond_neg, tmp); + + cond = fold_build2 (TRUTH_ORIF_EXPR, sizetype, cond, cond_neg); } - off = fold_build2 (MIN_EXPR, sizetype, sz, off); - return fold_build2 (MINUS_EXPR, sizetype, sz, off); + off = typesize ? fold_build2 (MULT_EXPR, sizetype, sz, off) : off; + + tree res = fold_build2 (MINUS_EXPR, sizetype, sz, off); + res = fold_build3 (COND_EXPR, sizetype, cond, res, size_zero_node); + + /* Finally, cap the return value to wholesize. This could typically happen + if the whole object did not get any actual storage, e.g. char var[0] or + less storage, e.g. + struct some_large_struct = __builtin_malloc (smolval). */ + if (wholesize) + res = fold_build2 (MIN_EXPR, sizetype, wholesize, res); + + return res; } /* Peek through ADDR_EXPR operands to get the definition of the whole variable @@ -239,7 +309,7 @@ whole_var_size (struct object_size_info *osi, tree pt_var, tree offset = wide_int_to_tree (size_type_node, mem_ref_offset (pt_var)); - pt_var_size = size_for_offset (pt_var_size, offset); + pt_var_size = size_for_offset (pt_var_size, offset, pt_var_size); } } else if (DECL_P (pt_var)) @@ -263,7 +333,8 @@ whole_var_size (struct object_size_info *osi, tree pt_var, static bool addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, - int object_size_type, tree *psize) + int object_size_type, tree *psize, + tree *wholesizep = NULL) { tree pt_var, pt_var_size = NULL_TREE, bytes; @@ -289,12 +360,14 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, { bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); if (bytes != error_mark_node) - bytes = size_for_offset (pt_var_size, bytes); + bytes = size_for_offset (pt_var_size, bytes, pt_var_size); } else bytes = pt_var_size; *psize = bytes == error_mark_node ? NULL_TREE : bytes; + if (wholesizep) + *wholesizep = pt_var_size; return true; } @@ -381,35 +454,63 @@ pass_through_call (const gcall *call) return NULL_TREE; } + static void -emit_size_stmts (gimple *stmt, tree size_ssa, tree size_expr) +emit_size_stmts (gimple *stmt, tree wholesize_ssa, + tree wholesize_expr, tree size_ssa, tree size_expr) { gimple_seq seq = NULL; + /* Gimplify the wholesize and size expressions. */ + if (!is_gimple_variable (wholesize_expr)) + wholesize_expr = force_gimple_operand (wholesize_expr, &seq, true, NULL); if (!is_gimple_variable (size_expr)) - size_expr = force_gimple_operand (size_expr, &seq, true, NULL); + { + gimple_seq s; + size_expr = force_gimple_operand (size_expr, &s, true, NULL); + gimple_seq_add_seq (&seq, s); + } - gassign *assign = gimple_build_assign (size_ssa, size_expr); + /* Assign them to their SSAs. */ + gassign *assign = gimple_build_assign (wholesize_ssa, wholesize_expr); + gimple_seq_add_stmt (&seq, assign); + assign = gimple_build_assign (size_ssa, size_expr); gimple_seq_add_stmt (&seq, assign); - /* Define object size right after the object is defined. */ + /* Define object size right before the object is defined, assuming that any + statements involved in evaluation of the object size expression precede + the definition statement. For parameters, we don't have a definition + statement, so insert into the first code basic block. */ gimple_stmt_iterator i = gsi_for_stmt (stmt); - gsi_insert_seq_after (&i, seq, GSI_CONTINUE_LINKING); + gsi_insert_seq_before (&i, seq, GSI_CONTINUE_LINKING); } static void -emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes) +emit_size_phi_stmts (gimple *stmt, tree wholesize_ssa, tree *wholesizes, + tree size_ssa, tree *sizes) { - gphi *newphi = create_phi_node (size_ssa, gimple_bb (stmt)); + gphi *wholephi = create_phi_node (wholesize_ssa, gimple_bb (stmt)); + gphi *phi = create_phi_node (size_ssa, gimple_bb (stmt)); gphi *obj_phi = as_a (stmt); for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) { + gimple_seq seq = NULL; + + /* If we built an expression, we will need to build statements + and insert them on the edge right away. */ + if (!is_gimple_variable (wholesizes[i])) + wholesizes[i] = force_gimple_operand (wholesizes[i], &seq, true, NULL); if (!is_gimple_variable (sizes[i])) { - gimple_seq seq; + gimple_seq s; + sizes[i] = force_gimple_operand (sizes[i], &s, true, NULL); + gimple_seq_add_seq (&seq, s); + } + + if (seq) + { edge e = gimple_phi_arg_edge (obj_phi, i); - sizes[i] = force_gimple_operand (sizes[i], &seq, true, NULL); /* Put the size definition before the last statement of the source block of the PHI edge. This ensures that any branches at the end @@ -421,14 +522,28 @@ emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes) gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING); } - add_phi_arg (newphi, sizes[i], + add_phi_arg (wholephi, wholesizes[i], + gimple_phi_arg_edge (obj_phi, i), + gimple_phi_arg_location (obj_phi, i)); + + add_phi_arg (phi, sizes[i], gimple_phi_arg_edge (obj_phi, i), gimple_phi_arg_location (obj_phi, i)); } } +/* Set upper bound to SIZE_MAX - 1 so that equality with (size_t) -1 fails. */ + +static tree +size_bound_expr (tree sz) +{ + tree max = size_binop (MINUS_EXPR, TYPE_MAX_VALUE (sizetype), size_one_node); + return fold_build2 (MIN_EXPR, sizetype, max, sz); +} + static void -eval_size_expr (tree var, tree size, tree *size_expr) +eval_size_expr (tree var, tree wholesize, tree *wholesize_expr, + tree size, tree *size_expr) { if (size_expr != NULL) { @@ -438,12 +553,16 @@ eval_size_expr (tree var, tree size, tree *size_expr) if (gimple_code (stmt) == GIMPLE_PHI) { - emit_size_phi_stmts (stmt, size, size_expr); + emit_size_phi_stmts (stmt, wholesize, wholesize_expr, + size, size_expr); + delete[] wholesize_expr; delete[] size_expr; } else { - emit_size_stmts (stmt, size, *size_expr); + emit_size_stmts (stmt, wholesize, *wholesize_expr, size, + size_bound_expr (*size_expr)); + delete wholesize_expr; delete size_expr; } } @@ -467,12 +586,20 @@ gimplify_size_exprs (object_size_info *osi, tree ptr) { EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi) { + release_ssa_name (object_whole_sizes[object_size_type][i]); release_ssa_name (object_sizes[object_size_type][i]); if (gimple_code (SSA_NAME_DEF_STMT (ssa_name (i))) == GIMPLE_PHI && object_size_exprs[object_size_type][i]) - delete [] object_size_exprs[object_size_type][i]; + { + delete [] object_whole_size_exprs[object_size_type][i]; + delete [] object_size_exprs[object_size_type][i]; + } else - delete object_size_exprs[object_size_type][i]; + { + delete object_whole_size_exprs[object_size_type][i]; + delete object_size_exprs[object_size_type][i]; + } + object_whole_size_exprs[object_size_type][i] = NULL; object_size_exprs[object_size_type][i] = NULL; } bitmap_clear (computed[object_size_type]); @@ -484,8 +611,12 @@ gimplify_size_exprs (object_size_info *osi, tree ptr) for (int object_size_type = 0; object_size_type <= 3; object_size_type++) EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi) { - eval_size_expr (ssa_name (i), object_sizes[object_size_type][i], + eval_size_expr (ssa_name (i), + object_whole_sizes[object_size_type][i], + object_whole_size_exprs[object_size_type][i], + object_sizes[object_size_type][i], object_size_exprs[object_size_type][i]); + object_whole_size_exprs[object_size_type][i] = NULL; object_size_exprs[object_size_type][i] = NULL; } } @@ -517,7 +648,32 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize, return false; if (computed[object_size_type] == NULL) - return false; + { + if (optimize || object_size_type & 1) + return false; + + /* Like with __builtin_object_size, when not optimizing, rather than + failing, make a small effort to determine the object size without the + full benefit of the (costly) computation below. */ + gimple *def = SSA_NAME_DEF_STMT (ptr); + if (gimple_code (def) == GIMPLE_ASSIGN) + { + tree_code code = gimple_assign_rhs_code (def); + if (code == POINTER_PLUS_EXPR) + { + tree offset = gimple_assign_rhs2 (def); + ptr = gimple_assign_rhs1 (def); + + if (compute_builtin_dyn_object_size (ptr, object_size_type, + psize)) + { + *psize = size_for_offset (*psize, offset, *psize, true); + return true; + } + } + } + return false; + } if (bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr))) goto out; @@ -530,6 +686,9 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize, { object_sizes[object_size_type].safe_grow (num_ssa_names, true); object_size_exprs[object_size_type].safe_grow (num_ssa_names, true); + object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true); + object_whole_size_exprs[object_size_type].safe_grow (num_ssa_names, + true); } if (dump_file) { @@ -576,21 +735,37 @@ out: SSA_NAME. */ static void -expr_object_size (struct object_size_info *osi, tree value, tree *sz) +expr_object_size (struct object_size_info *osi, tree value, tree *sz, + tree *wholeszp) { int object_size_type = osi->object_size_type; - tree bytes = NULL_TREE; + tree bytes = NULL_TREE, wholesz = NULL_TREE; if (TREE_CODE (value) == WITH_SIZE_EXPR) value = TREE_OPERAND (value, 0); if (TREE_CODE (value) == ADDR_EXPR) - addr_dyn_object_size (osi, value, object_size_type, &bytes); + addr_dyn_object_size (osi, value, object_size_type, &bytes, &wholesz); if (bytes) STRIP_NOPS (bytes); + if (wholesz) + STRIP_NOPS (wholesz); + *sz = bytes; + *wholeszp = wholesz; +} + +static tree +make_object_size_name (tree ssa_name, tree *val, struct function *fun) +{ + if (ssa_name != NULL_TREE) + gcc_assert (*val == error_mark_node); + else + ssa_name = make_ssa_name_fn (fun, sizetype, 0); + + return ssa_name; } static void @@ -608,22 +783,29 @@ maybe_update_dependency_loop (struct object_size_info *osi, unsigned name, /* Add object size to the cache so that it can be reused. */ static void -cache_object_size (struct object_size_info *osi, unsigned name, tree *sz) +cache_object_size (struct object_size_info *osi, unsigned name, tree *sz, + tree *wholesz) { int object_size_type = osi->object_size_type; struct function *fun = osi->fun; - gcc_assert (sz); + gcc_assert (sz && wholesz); maybe_update_dependency_loop (osi, name, *sz); /* Reuse SSA name if it was created for a dependency loop. */ - if (object_sizes[object_size_type][name] != NULL_TREE) - gcc_assert (*object_size_exprs[object_size_type][name] == error_mark_node); - else - object_sizes[object_size_type][name] = make_ssa_name_fn (fun, sizetype, 0); + object_sizes[object_size_type][name] = + make_object_size_name (object_sizes[object_size_type][name], + object_size_exprs[object_size_type][name], fun); object_size_exprs[object_size_type][name] = sz; + /* Likewise for whole object size. */ + object_whole_sizes[object_size_type][name] = + make_object_size_name (object_whole_sizes[object_size_type][name], + object_whole_size_exprs[object_size_type][name], + fun); + object_whole_size_exprs[object_size_type][name] = wholesz; + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Caching size for "); @@ -635,6 +817,14 @@ cache_object_size (struct object_size_info *osi, unsigned name, tree *sz) print_generic_expr (dump_file, *object_size_exprs[object_size_type][name], dump_flags); + fprintf (dump_file, ", Object whole size: "); + print_generic_expr (dump_file, + object_whole_sizes[object_size_type][name], + dump_flags); + fprintf (dump_file, " = "); + print_generic_expr (dump_file, + *object_whole_size_exprs[object_size_type][name], + dump_flags); fprintf (dump_file, "\n"); } @@ -644,21 +834,36 @@ cache_object_size (struct object_size_info *osi, unsigned name, tree *sz) /* Copy SZ and then call cache_object_size above. */ static void -cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz) +cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz, + tree wholesz) { int object_size_type = osi->object_size_type; - if (sz == NULL_TREE) + if (sz == NULL_TREE || wholesz == NULL_TREE) { + /* If we are unable to determine the size of a circular dependency, + release its SSA. */ if (object_sizes[object_size_type][name] != NULL_TREE) - release_ssa_name (object_sizes[object_size_type][name]); + { + release_ssa_name (object_sizes[object_size_type][name]); + release_ssa_name (object_whole_sizes[object_size_type][name]); + } object_sizes[object_size_type][name] = NULL_TREE; object_size_exprs[object_size_type][name] = NULL; + object_whole_sizes[object_size_type][name] = NULL_TREE; + object_whole_size_exprs[object_size_type][name] = NULL; bitmap_set_bit (computed[object_size_type], name); + if (dump_file) + { + fprintf (dump_file, "caching UNKNOWN size for "); + print_generic_expr (dump_file, ssa_name (name), dump_flags); + fprintf (dump_file, "\n"); + } + return; } - cache_object_size (osi, name, new tree (sz)); + cache_object_size (osi, name, new tree (sz), new tree (wholesz)); } /* Use size of ORIG for DEST and return it. */ @@ -669,9 +874,59 @@ set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig) collect_object_sizes_for (osi, orig); tree sz = object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)]; - cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz); + tree wholesz = + object_whole_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)]; + + cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz, wholesz); } +/* Compute object_sizes for VAR, defined to the result of an assignment + with operator POINTER_PLUS_EXPR. Return true if the object size might + need reexamination later. */ + +static void +plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (var); + tree op0, op1, bytes = NULL_TREE, wholevar = NULL_TREE; + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + op0 = gimple_assign_rhs1 (stmt); + op1 = gimple_assign_rhs2 (stmt); + } + else if (gimple_assign_rhs_code (stmt) == ADDR_EXPR) + { + tree rhs = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); + gcc_assert (TREE_CODE (rhs) == MEM_REF); + op0 = TREE_OPERAND (rhs, 0); + op1 = TREE_OPERAND (rhs, 1); + } + else + gcc_unreachable (); + + /* Handle PTR + OFFSET here. */ + if (TREE_CODE (op0) == SSA_NAME) + { + unsigned op0no = SSA_NAME_VERSION (op0); + + collect_object_sizes_for (osi, op0); + if (object_sizes[object_size_type][op0no]) + { + bytes = object_sizes[object_size_type][op0no]; + wholevar = object_whole_sizes[object_size_type][op0no]; + bytes = size_for_offset (bytes, op1, wholevar, true); + } + } + else if (TREE_CODE (op0) == ADDR_EXPR) + { + /* op0 will be ADDR_EXPR here. */ + if (addr_dyn_object_size (osi, op0, object_size_type, &bytes, &wholevar)) + bytes = size_for_offset (bytes, op1, wholevar); + } + cache_object_size_copy (osi, varno, bytes, wholevar); +} /* Compute object_sizes for PTR, defined to the result of a call. */ @@ -680,11 +935,10 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) { gcc_assert (is_gimple_call (call)); - cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), - alloc_object_size (call)); + tree sz = alloc_object_size (call); + cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), sz, sz); } - /* Compute object sizes for VAR. - For allocation GIMPLE_CALL like malloc or calloc object size is the size @@ -692,7 +946,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) - For a memcpy like GIMPLE_CALL that always returns one of its arguments, the object size is object size of that argument. - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI - node of the object. */ + node of the object. + - For GIMPLE_ASSIGN, return the size of the object the SSA name points + to. */ static void collect_object_sizes_for (struct object_size_info *osi, tree var) @@ -705,13 +961,16 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) return; if (bitmap_set_bit (osi->visited, varno)) - object_sizes[object_size_type][varno] = NULL_TREE; + { + object_sizes[object_size_type][varno] = NULL_TREE; + object_whole_sizes[object_size_type][varno] = NULL_TREE; + } else { /* Add an SSA name but mark the expression as being an error_mark_node. When we go back up the stack, the error_mark_node should get overwritten by a proper expression. */ - cache_object_size (osi, varno, &error_mark_node); + cache_object_size (osi, varno, &error_mark_node, &error_mark_node); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Found a dependency loop at "); @@ -732,6 +991,29 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) switch (gimple_code (stmt)) { + case GIMPLE_ASSIGN: + { + tree rhs = gimple_assign_rhs1 (stmt); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR + || (gimple_assign_rhs_code (stmt) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF)) + plus_stmt_object_size (osi, var, stmt); + else if (gimple_assign_single_p (stmt) + || gimple_assign_unary_nop_p (stmt)) + { + if (TREE_CODE (rhs) == SSA_NAME + && POINTER_TYPE_P (TREE_TYPE (rhs))) + set_object_size_ssa (osi, var, rhs); + else + { + tree sz, wholesz; + expr_object_size (osi, rhs, &sz, &wholesz); + cache_object_size_copy (osi, varno, sz, wholesz); + } + } + break; + } + case GIMPLE_CALL: { gcall *call_stmt = as_a (stmt); @@ -742,9 +1024,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) set_object_size_ssa (osi, var, arg); else { - tree ret; - expr_object_size (osi, arg, &ret); - cache_object_size_copy (osi, varno, ret); + tree sz, wholesz; + expr_object_size (osi, arg, &sz, &wholesz); + cache_object_size_copy (osi, varno, sz, wholesz); } } else @@ -756,44 +1038,48 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) { unsigned i, num_args = gimple_phi_num_args (stmt); tree *sizes = new tree[num_args] (); + tree *wholesizes = new tree[num_args] (); /* Bail out if the size of any of the PHI arguments cannot be determined. */ for (i = 0; i < gimple_phi_num_args (stmt); i++) { tree rhs = gimple_phi_arg_def (stmt, i); - tree sz; + tree sz, wholesz; if (TREE_CODE (rhs) != SSA_NAME) - expr_object_size (osi, rhs, &sz); + expr_object_size (osi, rhs, &sz, &wholesz); else { collect_object_sizes_for (osi, rhs); sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)]; + wholesz = + object_whole_sizes[object_size_type][SSA_NAME_VERSION (rhs)]; } - if (sz == NULL_TREE) + if (sz == NULL_TREE || wholesz == NULL_TREE) break; sizes[i] = sz; + wholesizes[i] = wholesz; } /* Record all possible sizes to build our PHI node later. */ if (i == gimple_phi_num_args (stmt)) + cache_object_size (osi, varno, sizes, wholesizes); + else { - cache_object_size (osi, varno, sizes); - break; + delete[] sizes; + delete[] wholesizes; + cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE); } - else - delete[] sizes; + break; } - /* FALLTHROUGH */ /* Bail out for all other cases. */ case GIMPLE_NOP: - case GIMPLE_ASSIGN: case GIMPLE_ASM: - cache_object_size_copy (osi, varno, NULL_TREE); + cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE); break; default: @@ -817,6 +1103,9 @@ init_dynamic_object_sizes (void) { object_sizes[object_size_type].safe_grow (num_ssa_names, true); object_size_exprs[object_size_type].safe_grow (num_ssa_names, true); + object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true); + object_whole_size_exprs[object_size_type].safe_grow (num_ssa_names, + true); computed[object_size_type] = BITMAP_ALLOC (NULL); } } @@ -833,6 +1122,8 @@ fini_dynamic_object_sizes (void) { object_sizes[object_size_type].release (); object_size_exprs[object_size_type].release (); + object_whole_sizes[object_size_type].release (); + object_whole_size_exprs[object_size_type].release (); BITMAP_FREE (computed[object_size_type]); } } From patchwork Thu Oct 7 22:14:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45977 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 81A353857825 for ; Thu, 7 Oct 2021 22:18:20 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from dog.elm.relay.mailchannels.net (dog.elm.relay.mailchannels.net [23.83.212.48]) by sourceware.org (Postfix) with ESMTPS id CB4BD3858002 for ; Thu, 7 Oct 2021 22:15:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org CB4BD3858002 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id D82AD22A0F; Thu, 7 Oct 2021 22:15:13 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-99-46.trex.outbound.svc.cluster.local [100.96.99.46]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 3523522B8A; Thu, 7 Oct 2021 22:15:13 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.99.46 (trex/6.4.3); Thu, 07 Oct 2021 22:15:13 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Abiding-Wipe: 41a601f40e1fb337_1633644913631_3172754537 X-MC-Loop-Signature: 1633644913631:1599478823 X-MC-Ingress-Time: 1633644913631 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id E714D7F2D7; Thu, 7 Oct 2021 15:15:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=IrdDuT0nrePvj6q+8zV NszXEPew=; b=sm8NCfIUxDhH97sNtBDWaLKNKiPUWBMpLrSd4klL4eLx2luS4yU KDuqp58ffyqOZhn8Hxea+xgH68Sr6M7SKdBRIrPF3eoXAbBae7vKQjbJA/FBv0fn A3FLDNu4gkJFYEbGphHjuMUjTSQnkf0JXD+CMxa6bZKbB4WbpexgpcUU= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id DB67D83499; Thu, 7 Oct 2021 15:15:10 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 6/8] tree-dynamic-object-size: Handle function parameters Date: Fri, 8 Oct 2021 03:44:30 +0530 Message-Id: <20211007221432.1029249-7-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3039.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Handle either static sizes in function parameters or hints provided by __attribute__ ((access (...))) to compute sizes for objects. gcc/ChangeLog: * tree-dynamic-object-size.c: Include tree-dfa.h. (emit_size_stmts): New argument osi. Handle GIMPLE_NOP. (eval_size_expr, gimplify_size_exprs): Adjust. (parm_object_size): New function. (collect_object_sizes_for): Handle GIMPLE_NOP. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz): New test. (main): Call it. Signed-off-by: Siddhesh Poyarekar --- .../gcc.dg/builtin-dynamic-object-size-0.c | 22 +++++ gcc/tree-dynamic-object-size.c | 98 +++++++++++++++++-- 2 files changed, 112 insertions(+), 8 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index 3c2c4c84264..c72fa0508db 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -255,6 +255,15 @@ test_substring_ptrplus (size_t sz, size_t off) return __builtin_dynamic_object_size (str + off, 0); } +size_t +__attribute__ ((noinline)) +__attribute__ ((access (__read_write__, 1, 2))) +test_parmsz (void *obj, size_t sz, size_t off) +{ + return __builtin_dynamic_object_size (obj + off, 0); +} + + int main (int argc, char **argv) { @@ -338,6 +347,19 @@ main (int argc, char **argv) if (test_deploop (128, 129) != 32) FAIL (); + if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, -1)!= 0) + FAIL (); + + if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, 0) + != __builtin_strlen (argv[0]) + 1) + FAIL (); + if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, + __builtin_strlen (argv[0]))!= 1) + FAIL (); + if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, + __builtin_strlen (argv[0]) + 2)!= 0) + FAIL (); + if (nfails > 0) __builtin_abort (); diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index f143a64777c..8d7283623dc 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "gimple-iterator.h" #include "tree-cfg.h" +#include "tree-dfa.h" #include "stringpool.h" #include "attribs.h" #include "builtins.h" @@ -456,7 +457,7 @@ pass_through_call (const gcall *call) static void -emit_size_stmts (gimple *stmt, tree wholesize_ssa, +emit_size_stmts (object_size_info *osi, gimple *stmt, tree wholesize_ssa, tree wholesize_expr, tree size_ssa, tree size_expr) { gimple_seq seq = NULL; @@ -481,7 +482,14 @@ emit_size_stmts (gimple *stmt, tree wholesize_ssa, statements involved in evaluation of the object size expression precede the definition statement. For parameters, we don't have a definition statement, so insert into the first code basic block. */ - gimple_stmt_iterator i = gsi_for_stmt (stmt); + gimple_stmt_iterator i; + if (gimple_code (stmt) == GIMPLE_NOP) + { + basic_block first_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (osi->fun)); + i = gsi_start_bb (first_bb); + } + else + i = gsi_for_stmt (stmt); gsi_insert_seq_before (&i, seq, GSI_CONTINUE_LINKING); } @@ -542,8 +550,8 @@ size_bound_expr (tree sz) } static void -eval_size_expr (tree var, tree wholesize, tree *wholesize_expr, - tree size, tree *size_expr) +eval_size_expr (struct object_size_info *osi, tree var, tree wholesize, + tree *wholesize_expr, tree size, tree *size_expr) { if (size_expr != NULL) { @@ -560,7 +568,7 @@ eval_size_expr (tree var, tree wholesize, tree *wholesize_expr, } else { - emit_size_stmts (stmt, wholesize, *wholesize_expr, size, + emit_size_stmts (osi, stmt, wholesize, *wholesize_expr, size, size_bound_expr (*size_expr)); delete wholesize_expr; delete size_expr; @@ -611,7 +619,7 @@ gimplify_size_exprs (object_size_info *osi, tree ptr) for (int object_size_type = 0; object_size_type <= 3; object_size_type++) EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi) { - eval_size_expr (ssa_name (i), + eval_size_expr (osi, ssa_name (i), object_whole_sizes[object_size_type][i], object_whole_size_exprs[object_size_type][i], object_sizes[object_size_type][i], @@ -939,6 +947,71 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), sz, sz); } +/* Find size of an object passed as a parameter to the function. */ + +static void +parm_object_size (struct object_size_info *osi, tree var) +{ + tree parm = SSA_NAME_VAR (var); + unsigned varno = SSA_NAME_VERSION (var); + tree sz = NULL_TREE, wholesz = NULL_TREE; + + if (dump_file) + { + fprintf (dump_file, "Object is a parameter: "); + print_generic_expr (dump_file, parm, dump_flags); + fprintf (dump_file, " which is %s a pointer type\n", + POINTER_TYPE_P (TREE_TYPE (parm)) ? "" : "not"); + } + + if (POINTER_TYPE_P (TREE_TYPE (parm))) + { + /* Look for access attribute. */ + rdwr_map rdwr_idx; + + tree fndecl = osi->fun->decl; + const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl); + tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm))); + + if (access && access->sizarg != UINT_MAX) + { + tree fnargs = DECL_ARGUMENTS (fndecl); + tree arg = NULL_TREE; + unsigned argpos = 0; + + /* Walk through the parameters to pick the size parameter and safely + scale it by the type size. */ + for (arg = fnargs; argpos != access->sizarg && arg; + arg = TREE_CHAIN (arg), ++argpos); + + if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg))) + { + tree arrsz = get_or_create_ssa_default_def (osi->fun, arg); + if (arrsz != NULL_TREE) + { + arrsz = fold_convert (sizetype, arrsz); + if (typesize) + { + tree res = fold_build2 (MULT_EXPR, sizetype, arrsz, + typesize); + tree check = fold_build2 (EXACT_DIV_EXPR, sizetype, + TYPE_MAX_VALUE (sizetype), + typesize); + check = fold_build2 (LT_EXPR, sizetype, arrsz, check); + arrsz = fold_build3 (COND_EXPR, sizetype, check, res, + size_zero_node); + } + } + sz = wholesz = arrsz; + } + } + } + else + expr_object_size (osi, parm, &sz, &wholesz); + + cache_object_size_copy (osi, varno, sz, wholesz); +} + /* Compute object sizes for VAR. - For allocation GIMPLE_CALL like malloc or calloc object size is the size @@ -948,7 +1021,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI node of the object. - For GIMPLE_ASSIGN, return the size of the object the SSA name points - to. */ + to. + - For GIMPLE_NOP, if the variable is a function parameter, compute the size + of the parameter. */ static void collect_object_sizes_for (struct object_size_info *osi, tree var) @@ -1076,8 +1151,15 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) break; } - /* Bail out for all other cases. */ case GIMPLE_NOP: + if (SSA_NAME_VAR (var) && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL) + parm_object_size (osi, var); + else + /* Uninitialized SSA names point nowhere. */ + cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE); + break; + + /* Bail out for all other cases. */ case GIMPLE_ASM: cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE); break; From patchwork Thu Oct 7 22:14:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45978 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C03713857831 for ; Thu, 7 Oct 2021 22:18:57 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from hedgehog.birch.relay.mailchannels.net (hedgehog.birch.relay.mailchannels.net [23.83.209.81]) by sourceware.org (Postfix) with ESMTPS id 87B8F385780B for ; Thu, 7 Oct 2021 22:15:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 87B8F385780B Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id CFA1F342028; Thu, 7 Oct 2021 22:15:17 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-16-65.trex.outbound.svc.cluster.local [100.96.16.65]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 6762E34138A; Thu, 7 Oct 2021 22:15:17 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.16.65 (trex/6.4.3); Thu, 07 Oct 2021 22:15:17 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Shade-Hysterical: 68b71d6a07efe313_1633644917704_2481779754 X-MC-Loop-Signature: 1633644917703:813508260 X-MC-Ingress-Time: 1633644917703 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id 271AE83495; Thu, 7 Oct 2021 15:15:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=pffZTZsIOey9B7rUZnH lWsQ4++4=; b=sW3gLPS8qzfLOkjrJ1IF5c36vxBlRuZjx0JfgbOjl/ieoqiTBR6 5IsagfCzR++TbhQ5a5guK8ahB/nc/FHIzsDhCIB1UTlXvNEoXzFmsqeKV2pNqtsQ 6FJJuWNwogscnuq03S7Vimg8leJp+ZVpenT955MT0xg+Bs0IX80OXQuQ= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id DEC007F2D7; Thu, 7 Oct 2021 15:15:13 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 7/8] tree-dynamic-object-size: Get subobject sizes Date: Fri, 8 Oct 2021 03:44:31 +0530 Message-Id: <20211007221432.1029249-8-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3039.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Adapt subobject computation logic from tree-object-size to make it work with variable sizes. gcc/ChangeLog: * tree-dynamic-object-size.c (build_cond_branch): New function. (compute_object_offset): Use it. (get_closest_subobject): New function. (addr_dyn_object_size): Call it. Support subobject size computation. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_dynarray_struct_subobj): New test. (main): Call it. Signed-off-by: Siddhesh Poyarekar --- .../gcc.dg/builtin-dynamic-object-size-0.c | 34 ++++ gcc/tree-dynamic-object-size.c | 172 ++++++++++++++++-- 2 files changed, 194 insertions(+), 12 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index c72fa0508db..94f8e071e2c 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -237,6 +237,33 @@ test_dynarray_struct (size_t sz, size_t off) return __builtin_dynamic_object_size (&bin[off].c, 0); } +size_t +__attribute__ ((noinline)) +test_dynarray_struct_subobj (size_t sz, size_t off) +{ + struct dynarray_struct bin[sz]; + + return __builtin_dynamic_object_size (&bin[off].c[4], 1); +} + +size_t +__attribute__ ((noinline)) +test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz) +{ + struct dynarray_struct2 + { + long a; + int b; + char c[sz]; + }; + + struct dynarray_struct2 bin; + + *objsz = sizeof (bin); + + return __builtin_dynamic_object_size (&bin.c[off], 1); +} + size_t __attribute__ ((noinline)) test_substring (size_t sz, size_t off) @@ -334,6 +361,13 @@ main (int argc, char **argv) FAIL (); if (test_substring (128, 142) != 0) FAIL (); + if (test_dynarray_struct_subobj (42, 4) != 16 - 4) + FAIL (); + if (test_dynarray_struct_subobj (42, 48) != 0) + FAIL (); + size_t objsz = 0; + if (test_dynarray_struct_subobj2 (42, 4, &objsz) != objsz - 4 - 12) + FAIL (); if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int)) FAIL (); if (test_substring_ptrplus (128, 142) != 0) diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index 8d7283623dc..ebc2fad7a87 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -102,12 +102,21 @@ static bitmap computed[4]; bool compute_builtin_dyn_object_size (tree, int, tree *, struct function *fun = NULL); +static tree +build_cond_branch (tree t, tree low_bound, tree unit_size) +{ + tree br = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound); + br = fold_convert (sizetype, br); + br = fold_build2 (MULT_EXPR, sizetype, unit_size, br); + + return br; +} + /* Compute offset of EXPR within VAR. Return error_mark_node if unknown. */ static tree compute_object_offset (const_tree expr, const_tree var) { - enum tree_code code = PLUS_EXPR; tree base, off, t; if (expr == var) @@ -150,12 +159,16 @@ compute_object_offset (const_tree expr, const_tree var) low_bound = array_ref_low_bound (CONST_CAST_TREE (expr)); unit_size = array_ref_element_size (CONST_CAST_TREE (expr)); if (! integer_zerop (low_bound)) - t = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound); - if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0) { - code = MINUS_EXPR; - t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t); + tree cond = fold_build2 (GE_EXPR, TREE_TYPE (t), t, low_bound); + tree then_br = build_cond_branch (t, low_bound, unit_size); + tree else_br = build_cond_branch (low_bound, t, unit_size); + + then_br = fold_build2 (PLUS_EXPR, sizetype, base, then_br); + else_br = fold_build2 (MINUS_EXPR, sizetype, base, else_br); + return fold_build3 (COND_EXPR, sizetype, cond, then_br, else_br); } + t = fold_convert (sizetype, t); off = fold_build2 (MULT_EXPR, sizetype, unit_size, t); break; @@ -168,7 +181,7 @@ compute_object_offset (const_tree expr, const_tree var) return error_mark_node; } - return fold_build2 (code, sizetype, base, off); + return fold_build2 (PLUS_EXPR, sizetype, base, off); } /* Given an object size SZ and offset OFF into it, compute the usable object @@ -328,6 +341,105 @@ whole_var_size (struct object_size_info *osi, tree pt_var, return true; } +/* Get the most immediate subobject encapsulating the pointer PTR, given the + whole_var object WHOLE_VAR. */ + +static tree +get_closest_subobject (const_tree ptr, tree whole_var) +{ + tree var = TREE_OPERAND (ptr, 0); + + while (var != whole_var + && TREE_CODE (var) != BIT_FIELD_REF + && TREE_CODE (var) != COMPONENT_REF + && TREE_CODE (var) != ARRAY_REF + && TREE_CODE (var) != ARRAY_RANGE_REF + && TREE_CODE (var) != REALPART_EXPR + && TREE_CODE (var) != IMAGPART_EXPR) + var = TREE_OPERAND (var, 0); + + if (var != whole_var && TREE_CODE (var) == ARRAY_REF) + var = TREE_OPERAND (var, 0); + + if (! TYPE_SIZE_UNIT (TREE_TYPE (var))) + var = whole_var; + else if (var != whole_var && TREE_CODE (whole_var) == MEM_REF) + { + tree v = var; + while (v && v != whole_var) + switch (TREE_CODE (v)) + { + case ARRAY_REF: + if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))) + { + tree domain + = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0))); + if (domain && TYPE_MAX_VALUE (domain)) + { + v = NULL_TREE; + break; + } + } + v = TREE_OPERAND (v, 0); + break; + case REALPART_EXPR: + case IMAGPART_EXPR: + v = NULL_TREE; + break; + case COMPONENT_REF: + if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE) + { + v = NULL_TREE; + break; + } + while (v != whole_var && TREE_CODE (v) == COMPONENT_REF) + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != UNION_TYPE + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != QUAL_UNION_TYPE) + break; + else + v = TREE_OPERAND (v, 0); + if (TREE_CODE (v) == COMPONENT_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + == RECORD_TYPE) + { + tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1)); + for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain)) + if (TREE_CODE (fld_chain) == FIELD_DECL) + break; + + if (fld_chain) + { + v = NULL_TREE; + break; + } + v = TREE_OPERAND (v, 0); + } + while (v != whole_var && TREE_CODE (v) == COMPONENT_REF) + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != UNION_TYPE + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != QUAL_UNION_TYPE) + break; + else + v = TREE_OPERAND (v, 0); + if (v != whole_var) + v = NULL_TREE; + else + v = whole_var; + break; + default: + v = whole_var; + break; + } + if (v == whole_var) + var = whole_var; + } + + return var; +} + /* Compute an object size estimate for PTR, which is a ADDR_EXPR. OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size. If unknown, return false, setting PSIZE to NULL_TREE. */ @@ -337,7 +449,8 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, int object_size_type, tree *psize, tree *wholesizep = NULL) { - tree pt_var, pt_var_size = NULL_TREE, bytes; + struct function *fun = osi ? osi->fun : cfun; + tree pt_var, pt_var_size = NULL_TREE, var_size, bytes; gcc_assert (TREE_CODE (ptr) == ADDR_EXPR); @@ -353,22 +466,57 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, if (!whole_var_size (osi, pt_var, object_size_type, &pt_var_size)) return false; - if (!pt_var_size) - return false; + var_size = pt_var_size; /* PTR points to a subobject of whole variable PT_VAR. */ if (pt_var != TREE_OPERAND (ptr, 0)) { - bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); + tree var; + + if (object_size_type & 1) + var = get_closest_subobject (ptr, pt_var); + else + var = pt_var; + + if (var != pt_var) + { + var_size = TYPE_SIZE_UNIT (TREE_TYPE (var)); + if (!TREE_CONSTANT (var_size)) + var_size = get_or_create_ssa_default_def (fun, var_size); + if (!var_size) + return false; + } + else if (!pt_var_size) + return false; + + bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var); if (bytes != error_mark_node) - bytes = size_for_offset (pt_var_size, bytes, pt_var_size); + { + tree cap_size = pt_var_size; + + /* For subobject sizes where the whole object is a structure, + restrict size to the size of the struct less the offset of the + subobject in the struct. */ + if (var != pt_var && pt_var_size && TREE_CODE (pt_var) == MEM_REF) + { + tree off = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); + if (off == error_mark_node) + off = size_zero_node; + else + off = fold_build2 (MIN_EXPR, sizetype, pt_var_size, off); + cap_size = fold_build2 (MINUS_EXPR, sizetype, pt_var_size, off); + } + bytes = size_for_offset (var_size, bytes, cap_size); + } } + else if (!pt_var_size) + return false; else bytes = pt_var_size; *psize = bytes == error_mark_node ? NULL_TREE : bytes; if (wholesizep) - *wholesizep = pt_var_size; + *wholesizep = var_size; return true; } From patchwork Thu Oct 7 22:14:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45979 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EB12B385780F for ; Thu, 7 Oct 2021 22:19:34 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from olivedrab.birch.relay.mailchannels.net (olivedrab.birch.relay.mailchannels.net [23.83.209.135]) by sourceware.org (Postfix) with ESMTPS id 8807B3857C5F for ; Thu, 7 Oct 2021 22:15:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8807B3857C5F Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id CD466341F8B; Thu, 7 Oct 2021 22:15:25 +0000 (UTC) Received: from pdx1-sub0-mail-a17.g.dreamhost.com (100-96-133-192.trex.outbound.svc.cluster.local [100.96.133.192]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 6872E3415D4; Thu, 7 Oct 2021 22:15:25 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a17.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.133.192 (trex/6.4.3); Thu, 07 Oct 2021 22:15:25 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Troubled-Minister: 37aa4e1a201f4163_1633644925693_2981958771 X-MC-Loop-Signature: 1633644925693:4149269451 X-MC-Ingress-Time: 1633644925693 Received: from pdx1-sub0-mail-a17.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTP id 2A5397F2D7; Thu, 7 Oct 2021 15:15:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gotplt.org; h=from:to:cc :subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; s=gotplt.org; bh=ME+PnYbKzg44tjrdKBg AuEpVpOo=; b=JvLS01NfWVhCkZGy4E4uUQTTbC/rcKW5HxFKCJcHPOOk1GgjQy2 jo5dFMUlSJfk01cTIxVqUvZ2RvxaL21trlVsKC0SE3io9qmQPJg5wQanWD/BeRoX X4i2DGXqqCjIr/LVpFkvIWpvsUuG3OnEYbOwcUtT+sywbzPo6kvPyw4w= Received: from rhbox.redhat.com (unknown [1.186.223.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a17.g.dreamhost.com (Postfix) with ESMTPSA id 50DF083495; Thu, 7 Oct 2021 15:15:17 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a17 From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 8/8] tree-dynamic-object-size: Add test wrappers to extend testing Date: Fri, 8 Oct 2021 03:44:32 +0530 Message-Id: <20211007221432.1029249-9-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211007221432.1029249-1-siddhesh@gotplt.org> References: <20211007221432.1029249-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3039.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Wrap most tests for __builtin_object_size with __builtin_dynamic_object_size to get better coverage. At the moment only one test does not work correctly with __builtin_dynamic_object_size, viz. builtin-object-size-5.c. The test expects abort() calls to be optimized away, which does not happen with __builtin_dynamic_object_size since it cannot collapse loop iteration to a constant. gcc/testsuite/ChangeLog: * g++.dg/ext/builtin-dynamic-object-size1.C: New test. * g++.dg/ext/builtin-dynamic-object-size2.C: Likewise. * gcc.dg/builtin-dynamic-alloc-size.c: Likewise. * gcc.dg/builtin-dynamic-object-size-1.c: Likewise. * gcc.dg/builtin-dynamic-object-size-10.c: Likewise. * gcc.dg/builtin-dynamic-object-size-11.c: Likewise. * gcc.dg/builtin-dynamic-object-size-12.c: Likewise. * gcc.dg/builtin-dynamic-object-size-13.c: Likewise. * gcc.dg/builtin-dynamic-object-size-14.c: Likewise. * gcc.dg/builtin-dynamic-object-size-15.c: Likewise. * gcc.dg/builtin-dynamic-object-size-16.c: Likewise. * gcc.dg/builtin-dynamic-object-size-17.c: Likewise. * gcc.dg/builtin-dynamic-object-size-18.c: Likewise. * gcc.dg/builtin-dynamic-object-size-19.c: Likewise. * gcc.dg/builtin-dynamic-object-size-2.c: Likewise. * gcc.dg/builtin-dynamic-object-size-3.c: Likewise. * gcc.dg/builtin-dynamic-object-size-4.c: Likewise. * gcc.dg/builtin-dynamic-object-size-6.c: Likewise. * gcc.dg/builtin-dynamic-object-size-7.c: Likewise. * gcc.dg/builtin-dynamic-object-size-8.c: Likewise. * gcc.dg/builtin-dynamic-object-size-9.c: Likewise. * gcc.dg/builtin-object-size-1.c: Adjust to rebuild with __builtin_dynamic_object_size. * gcc.dg/builtin-object-size-16.c: Likewise. * gcc.dg/builtin-object-size-17.c: Likewise. * gcc.dg/builtin-object-size-2.c: Likewise. * gcc.dg/builtin-object-size-3.c: Likewise. * gcc.dg/builtin-object-size-4.c: Likewise. Signed-off-by: Siddhesh Poyarekar --- .../g++.dg/ext/builtin-dynamic-object-size1.C | 5 + .../g++.dg/ext/builtin-dynamic-object-size2.C | 5 + .../gcc.dg/builtin-dynamic-alloc-size.c | 7 + .../gcc.dg/builtin-dynamic-object-size-1.c | 7 + .../gcc.dg/builtin-dynamic-object-size-10.c | 9 ++ .../gcc.dg/builtin-dynamic-object-size-11.c | 7 + .../gcc.dg/builtin-dynamic-object-size-12.c | 5 + .../gcc.dg/builtin-dynamic-object-size-13.c | 5 + .../gcc.dg/builtin-dynamic-object-size-14.c | 5 + .../gcc.dg/builtin-dynamic-object-size-15.c | 6 + .../gcc.dg/builtin-dynamic-object-size-16.c | 7 + .../gcc.dg/builtin-dynamic-object-size-17.c | 8 + .../gcc.dg/builtin-dynamic-object-size-18.c | 8 + .../gcc.dg/builtin-dynamic-object-size-19.c | 104 ++++++++++++ .../gcc.dg/builtin-dynamic-object-size-2.c | 7 + .../gcc.dg/builtin-dynamic-object-size-3.c | 7 + .../gcc.dg/builtin-dynamic-object-size-4.c | 7 + .../gcc.dg/builtin-dynamic-object-size-6.c | 5 + .../gcc.dg/builtin-dynamic-object-size-7.c | 5 + .../gcc.dg/builtin-dynamic-object-size-8.c | 5 + .../gcc.dg/builtin-dynamic-object-size-9.c | 5 + gcc/testsuite/gcc.dg/builtin-object-size-1.c | 110 +++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-16.c | 10 ++ gcc/testsuite/gcc.dg/builtin-object-size-17.c | 2 + gcc/testsuite/gcc.dg/builtin-object-size-2.c | 126 +++++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-3.c | 148 ++++++++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-4.c | 117 ++++++++++++++ 27 files changed, 742 insertions(+) create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C new file mode 100644 index 00000000000..b11ac200751 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C @@ -0,0 +1,5 @@ +// { dg-do run } +// { dg-options "-O2" } + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size1.C" diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C new file mode 100644 index 00000000000..6e52cf38533 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C @@ -0,0 +1,5 @@ +// { dg-do run } +// { dg-options "-O2" } + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size2.C" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c new file mode 100644 index 00000000000..9d0eadd6be4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c @@ -0,0 +1,7 @@ +/* { dg-do compile } + { dg-require-effective-target alloca } + { dg-additional-options "-O2 -fdump-tree-optimized" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-alloc-size.c" +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c new file mode 100644 index 00000000000..2c7d2128913 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-require-effective-target alloca } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-1.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c new file mode 100644 index 00000000000..6db9ea8c667 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-early_dynobjsz-details -fdump-tree-dynobjsz1-details" } */ +// { dg-skip-if "packed attribute missing for drone_source_packet" { "epiphany-*-*" } } + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-10.c" + +/* { dg-final { scan-tree-dump "spkt.*: maximum dynamic object size _" "early_dynobjsz" } } */ +/* { dg-final { scan-tree-dump ": maximum dynamic subobject size _" "early_dynobjsz" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c new file mode 100644 index 00000000000..65dcec9fcae --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c @@ -0,0 +1,7 @@ +/* PR48985 */ +/* { dg-do run } */ +/* { dg-options "-std=gnu89" } */ +/* { dg-skip-if "packed attribute missing for struct s" { "epiphany-*-*" } } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-11.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c new file mode 100644 index 00000000000..f0ce050a943 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-12.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c new file mode 100644 index 00000000000..555e23522dc --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-13.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c new file mode 100644 index 00000000000..26207200191 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-14.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c new file mode 100644 index 00000000000..ad4d562f777 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-15.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c new file mode 100644 index 00000000000..5aa256dec1a --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-O0" } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +char ax2[]; /* { dg-warning "assumed to have one element" } */ +#include "builtin-object-size-16.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c new file mode 100644 index 00000000000..d88317d111d --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -fdump-tree-ssa" } */ + +char ax2[]; /* { dg-warning "assumed to have one element" } */ +#define __builtin_object_size __builtin_dynamic_object_size +#define DYNAMIC_OBJECT_SIZE +#include "builtin-object-size-17.c" +/* { dg-final { scan-tree-dump-not "failure_on_line" "ssa" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c new file mode 100644 index 00000000000..70c1ebcff21 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* __stpncpy_chk could return buf up to buf + 64, so + the minimum object size might be far smaller than 64. */ +/* { dg-final { scan-tree-dump-not "return 64;" "optimized" } } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-18.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c new file mode 100644 index 00000000000..44141a38607 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c @@ -0,0 +1,104 @@ +/* PR tree-optimization/88372 - alloc_size attribute is ignored + on function pointers { dg-do compile } + { dg-options "-O2 -fdump-tree-optimized" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-18.c" + +typedef __SIZE_TYPE__ size_t; + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) +#define CONCAT(x, y) x ## y +#define CAT(x, y) CONCAT (x, y) +#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +/* Macro to emit a call to function named + call_in_true_branch_not_eliminated_on_line_NNN() + for each call that's expected to be eliminated. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that no such call appears in output. */ +#define ELIM(expr) \ + if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 + +void sink (void*); + +#define T(alloc, n) do { \ + void *p = alloc; \ + sink (p); \ + ELIM (n == __builtin_object_size (p, 0)); \ + ELIM (n == __builtin_object_size (p, 1)); \ + ELIM (n == __builtin_object_size (p, 2)); \ + ELIM (n == __builtin_object_size (p, 3)); \ + } while (0) + + +ATTR (alloc_size (1)) void* (*alloc_1_x)(size_t, size_t); +ATTR (alloc_size (2)) void* (*alloc_x_2)(size_t, size_t); + +/* Verify that things work when attribute alloc_size is applied + to a typedef that is then used to declared a pointer. */ +typedef ATTR (alloc_size (1, 2)) void* (alloc_1_2_t)(size_t, size_t); + +void test_alloc_ptr (alloc_1_2_t *alloc_1_2) +{ + T (alloc_1_x (0, 0), 0); + T (alloc_1_x (1, 0), 1); + T (alloc_1_x (3, 0), 3); + T (alloc_1_x (9, 5), 9); + + T (alloc_x_2 (0, 0), 0); + T (alloc_x_2 (1, 0), 0); + T (alloc_x_2 (0, 1), 1); + T (alloc_x_2 (9, 5), 5); + + T (alloc_1_2 (0, 0), 0); + T (alloc_1_2 (1, 0), 0); + T (alloc_1_2 (0, 1), 0); + T (alloc_1_2 (9, 5), 45); +} + +/* Verify that object size is detected even in indirect calls via + function pointers to built-in allocation functions, even without + explicit use of attribute alloc_size on the pointers. */ + +typedef void *(allocfn_1) (size_t); +typedef void *(allocfn_1_2) (size_t, size_t); + +static inline void * +call_alloc (allocfn_1 *fn1, allocfn_1_2 *fn2, size_t n1, size_t n2) +{ + return fn1 ? fn1 (n1) : fn2 (n1, n2); +} + +static inline void * +call_malloc (size_t n) +{ + return call_alloc (__builtin_malloc, 0, n, 0); +} + +static inline void * +call_calloc (size_t n1, size_t n2) +{ + return call_alloc (0, __builtin_calloc, n1, n2); +} + +void test_builtin_ptr (void) +{ + T (call_malloc (0), 0); + T (call_malloc (1), 1); + T (call_malloc (9), 9); + + T (call_calloc (0, 0), 0); + T (call_calloc (0, 1), 0); + T (call_calloc (1, 0), 0); + T (call_calloc (1, 1), 1); + T (call_calloc (1, 3), 3); + T (call_calloc (2, 3), 6); +} + +/* { dg-final { scan-tree-dump-not "not_eliminated" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c new file mode 100644 index 00000000000..2f07534c11b --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-require-effective-target alloca } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-2.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c new file mode 100644 index 00000000000..29b5b358845 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-require-effective-target alloca } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-3.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c new file mode 100644 index 00000000000..5ff1f16c978 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-require-effective-target alloca } */ + +#define DYNAMIC_OBJECT_SIZE +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-4.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c new file mode 100644 index 00000000000..6a275ce5b37 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-6.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c new file mode 100644 index 00000000000..e2a65994687 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-7.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c new file mode 100644 index 00000000000..e7af383d9b6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-8.c" diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c new file mode 100644 index 00000000000..19021bc2ce9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#define __builtin_object_size __builtin_dynamic_object_size +#include "builtin-object-size-9.c" diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index 8cdae49a6b1..33987dd279f 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -42,9 +42,17 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (q, 0) != (size_t) -1) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 0) + != (x < 0 + ? sizeof (a) - __builtin_offsetof (struct A, a) - 9 + : sizeof (a) - __builtin_offsetof (struct A, c) - 1)) + abort (); +#else if (__builtin_object_size (r, 0) != sizeof (a) - __builtin_offsetof (struct A, a) - 9) abort (); +#endif if (x < 6) r = &w[2].a[1]; else @@ -58,9 +66,17 @@ test1 (void *q, int x) if (__builtin_object_size (&y.b, 0) != sizeof (a) - __builtin_offsetof (struct A, b)) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 0) + != (x < 6 + ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1 + : sizeof (a) - __builtin_offsetof (struct A, a) - 6)) + abort (); +#else if (__builtin_object_size (r, 0) != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1) abort (); +#endif if (x < 20) r = malloc (30); else @@ -119,12 +135,21 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (var, 0) != x + 10) + abort (); + if (__builtin_object_size (var + 10, 0) != x) + abort (); + if (__builtin_object_size (&var[5], 0) != x + 5) + abort (); +#else if (__builtin_object_size (var, 0) != (size_t) -1) abort (); if (__builtin_object_size (var + 10, 0) != (size_t) -1) abort (); if (__builtin_object_size (&var[5], 0) != (size_t) -1) abort (); +#endif if (__builtin_object_size (zerol, 0) != 0) abort (); if (__builtin_object_size (&zerol, 0) != 0) @@ -165,6 +190,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef DYNAMIC_OBJECT_SIZE + size_t dyn_res; +#endif if (sizeof (a) != 20) return; @@ -181,8 +209,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3); + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } + if (__builtin_object_size (r, 0) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 0) != 20) abort (); +#endif r = &buf3[20]; for (i = 0; i < 4; ++i) { @@ -195,13 +241,44 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 20; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } + if (__builtin_object_size (r, 0) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 0) != 15) abort (); +#endif r += 8; +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res -= 8; + if (__builtin_object_size (r, 0) != dyn_res) + abort (); + if (dyn_res >= 6) + { + if (__builtin_object_size (r + 6, 0) != dyn_res - 6) + abort (); + } + else if (__builtin_object_size (r + 6, 0) != 0) + abort (); +#else if (__builtin_object_size (r, 0) != 7) abort (); if (__builtin_object_size (r + 6, 0) != 1) abort (); +#endif r = &buf3[18]; for (i = 0; i < 4; ++i) { @@ -214,8 +291,31 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 18; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4; + } + if (dyn_res >= 12) + { + if (__builtin_object_size (r + 12, 0) != dyn_res - 12) + abort (); + } + else if (__builtin_object_size (r + 12, 0) != 0) + abort (); +#else if (__builtin_object_size (r + 12, 0) != 4) abort (); +#endif } void @@ -358,6 +458,10 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x) + abort (); +#else /* My understanding of ISO C99 6.5.6 is that a conforming program will not end up with p equal to &buf[0] through &buf[7], i.e. calling this function with say @@ -367,6 +471,7 @@ test5 (size_t x) it would be 64 (or conservative (size_t) -1 == unknown). */ if (__builtin_object_size (p, 0) != sizeof (buf) - 8) abort (); +#endif memset (p, ' ', sizeof (buf) - 8 - 4 * 4); } @@ -380,8 +485,13 @@ test6 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x) + abort (); +#else if (__builtin_object_size (p, 0) != sizeof (t) - 8) abort (); +#endif memset (p, ' ', sizeof (t) - 8 - 4 * 4); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c index 48229390bfd..67680020337 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-16.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-16.c @@ -54,7 +54,9 @@ static int nfails; typedef __SIZE_TYPE__ size_t; extern char ax[]; +#ifndef DYNAMIC_OBJECT_SIZE char ax2[]; /* { dg-warning "assumed to have one element" } */ +#endif extern char a0[0]; static char a1[1]; @@ -114,11 +116,19 @@ test_arrays () T ( 4, 2, 4, 2, &a2x2[0][0]); T ( 0, F1 (0), 0, 0, &a2x2 + 1); T ( 2, F1 ( 2), 2, F3 ( 2), &a2x2[0] + 1); +#ifdef DYNAMIC_OBJECT_SIZE + T ( 3, F1 ( 1), 3, F3 ( 1), &a2x2[0][0] + 1); +#else T ( 3, F1 ( 1), 3, F3 ( 3), &a2x2[0][0] + 1); +#endif T ( 15, 15, 15, 15, a3x5); T ( 15, 5, 15, 5, &a3x5[0][0] + 0); +#ifdef DYNAMIC_OBJECT_SIZE + T ( 14, F1 ( 4), 14, F3 ( 4), &a3x5[0][0] + 1); +#else T ( 14, F1 ( 4), 14, F3 (14), &a3x5[0][0] + 1); +#endif T ( 1, 1, 1, 1, a1 + 0); T ( 0, F1 (0), 0, 0, a1 + 1); diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-object-size-17.c index 0497bbf4505..09552a8a963 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-17.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-17.c @@ -49,7 +49,9 @@ typedef __SIZE_TYPE__ size_t; extern char ax[]; +#ifndef DYNAMIC_OBJECT_SIZE char ax2[]; /* { dg-warning "assumed to have one element" } */ +#endif extern char a0[0]; static char a1[1]; diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c index ad2dd296a9a..c7ce5e988ca 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c @@ -32,6 +32,9 @@ test1 (void *q, int x) void *p = &a.a[3], *r; char var[x + 10]; struct A vara[x + 10]; +#ifdef DYNAMIC_OBJECT_SIZE + size_t dyn_res; +#endif if (x < 0) r = &a.a[9]; else @@ -43,8 +46,15 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (q, 1) != (size_t) -1) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (x < 0 + ? __builtin_object_size (r, 1) != sizeof (a.a) - 9 + : __builtin_object_size (r, 1) != sizeof (a.c) - 1) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (a.c) - 1) abort (); +#endif if (x < 6) r = &w[2].a[1]; else @@ -55,36 +65,74 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&y.b, 1) != sizeof (a.b)) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (x < 6 + ? __builtin_object_size (r, 1) != sizeof (a.a) - 1 + : __builtin_object_size (r, 1) != sizeof (a.a) - 6) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (a.a) - 1) abort (); +#endif if (x < 20) r = malloc (30); else r = calloc (2, 16); +#ifdef DYNAMIC_OBJECT_SIZE + if (x < 20 + ? __builtin_object_size (r, 1) != 30 + : __builtin_object_size (r, 1) != 2 * 16) + abort (); +#else /* We may duplicate this test onto the two exit paths. On one path the size will be 32, the other it will be 30. If we don't duplicate this test, then the size will be 32. */ if (__builtin_object_size (r, 1) != 2 * 16 && __builtin_object_size (r, 1) != 30) abort (); +#endif if (x < 20) r = malloc (30); else r = calloc (2, 14); +#ifdef DYNAMIC_OBJECT_SIZE + if (x < 20 + ? __builtin_object_size (r, 1) != 30 + : __builtin_object_size (r, 1) != 2 * 14) + abort (); +#else if (__builtin_object_size (r, 1) != 30) abort (); +#endif if (x < 30) r = malloc (sizeof (a)); else r = &a.a[3]; +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = x < 30 ? sizeof (a) : sizeof (a.a) - 3; + if (__builtin_object_size (r, 1) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (a)) abort (); +#endif r = memcpy (r, "a", 2); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 1) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (a)) abort (); +#endif r = memcpy (r + 2, "b", 2) + 2; +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res -= 4; + if (__builtin_object_size (r, 1) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (a) - 4) abort (); +#endif r = &a.a[4]; r = memset (r, 'a', 2); if (__builtin_object_size (r, 1) != sizeof (a.a) - 4) @@ -123,6 +171,20 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (var, 1) != x + 10) + abort (); + if (__builtin_object_size (var + 10, 1) != x) + abort (); + if (__builtin_object_size (&var[5], 1) != x + 5) + abort (); + if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A)) + abort (); + if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A)) + abort (); + if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A)) + abort (); +#else if (__builtin_object_size (var, 1) != (size_t) -1) abort (); if (__builtin_object_size (var + 10, 1) != (size_t) -1) @@ -135,6 +197,7 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&vara[5], 1) != (size_t) -1) abort (); +#endif if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a)) abort (); if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a)) @@ -185,6 +248,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef DYNAMIC_OBJECT_SIZE + size_t dyn_res; +#endif if (sizeof (a) != 20) return; @@ -201,8 +267,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3); + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a.buf1) - 1; + else if (i == l1) + dyn_res = sizeof (a.buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a.buf1) - 9; + } + if (__builtin_object_size (r, 1) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (buf3)) abort (); +#endif r = &buf3[20]; for (i = 0; i < 4; ++i) { @@ -215,13 +299,50 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 20; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a.buf1) - 7; + else if (i == l1) + dyn_res = sizeof (a.buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a.buf1) - 9; + } + if (__builtin_object_size (r, 1) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (buf3) - 5) abort (); +#endif r += 8; +#ifdef DYNAMIC_OBJECT_SIZE + if (dyn_res >= 8) + { + dyn_res -= 8; + if (__builtin_object_size (r, 1) != dyn_res) + abort (); + + if (dyn_res >= 6) + { + if (__builtin_object_size (r + 6, 1) != dyn_res - 6) + abort (); + } + else if (__builtin_object_size (r + 6, 1) != 0) + abort (); + } + else if (__builtin_object_size (r, 1) != 0) + abort (); +#else if (__builtin_object_size (r, 1) != sizeof (buf3) - 13) abort (); if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19) abort (); +#endif } void @@ -339,8 +460,13 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x) + abort (); +#else if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8) abort (); +#endif memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c index d5ca5047ee9..9533af9f058 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -42,9 +42,17 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (q, 2) != 0) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 2) + != (x < 0 + ? sizeof (a) - __builtin_offsetof (struct A, a) - 9 + : sizeof (a) - __builtin_offsetof (struct A, c) - 1)) + abort (); +#else if (__builtin_object_size (r, 2) != sizeof (a) - __builtin_offsetof (struct A, c) - 1) abort (); +#endif if (x < 6) r = &w[2].a[1]; else @@ -58,9 +66,17 @@ test1 (void *q, int x) if (__builtin_object_size (&y.b, 2) != sizeof (a) - __builtin_offsetof (struct A, b)) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 2) + != (x < 6 + ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1 + : sizeof (a) - __builtin_offsetof (struct A, a) - 6)) + abort (); +#else if (__builtin_object_size (r, 2) != sizeof (a) - __builtin_offsetof (struct A, a) - 6) abort (); +#endif if (x < 20) r = malloc (30); else @@ -71,23 +87,45 @@ test1 (void *q, int x) r = malloc (30); else r = calloc (2, 14); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14)) + abort (); +#else if (__builtin_object_size (r, 2) != 2 * 14) abort (); +#endif if (x < 30) r = malloc (sizeof (a)); else r = &a.a[3]; +#ifdef DYNAMIC_OBJECT_SIZE + size_t objsz = (x < 30 ? sizeof (a) + : sizeof (a) - __builtin_offsetof (struct A, a) - 3); + if (__builtin_object_size (r, 2) != objsz) + abort (); +#else if (__builtin_object_size (r, 2) != sizeof (a) - __builtin_offsetof (struct A, a) - 3) abort (); +#endif r = memcpy (r, "a", 2); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 2) != objsz) + abort (); +#else if (__builtin_object_size (r, 2) != sizeof (a) - __builtin_offsetof (struct A, a) - 3) abort (); +#endif r = memcpy (r + 2, "b", 2) + 2; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 2) != objsz - 4) + abort (); +#else if (__builtin_object_size (r, 2) != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4) abort (); +#endif r = &a.a[4]; r = memset (r, 'a', 2); if (__builtin_object_size (r, 2) @@ -118,12 +156,21 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (var, 2) != x + 10) + abort (); + if (__builtin_object_size (var + 10, 2) != x) + abort (); + if (__builtin_object_size (&var[5], 2) != x + 5) + abort (); +#else if (__builtin_object_size (var, 2) != 0) abort (); if (__builtin_object_size (var + 10, 2) != 0) abort (); if (__builtin_object_size (&var[5], 2) != 0) abort (); +#endif if (__builtin_object_size (zerol, 2) != 0) abort (); if (__builtin_object_size (&zerol, 2) != 0) @@ -164,6 +211,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef DYNAMIC_OBJECT_SIZE + size_t dyn_res; +#endif if (sizeof (a) != 20) return; @@ -180,8 +230,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3); + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } + if (__builtin_object_size (r, 2) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 2) != 3) abort (); +#endif r = &buf3[20]; for (i = 0; i < 4; ++i) { @@ -194,8 +262,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 20; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } + if (__builtin_object_size (r, 2) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 2) != 0) abort (); +#endif r = &buf3[2]; for (i = 0; i < 4; ++i) { @@ -208,13 +294,44 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 2; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4; + } + if (__builtin_object_size (r, 2) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 2) != 15) abort (); +#endif r += 8; +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res -= 8; + if (__builtin_object_size (r, 2) != dyn_res) + abort (); + if (dyn_res >= 6) + { + if (__builtin_object_size (r + 6, 2) != dyn_res - 6) + abort (); + } + else if (__builtin_object_size (r + 6, 2) != 0) + abort (); +#else if (__builtin_object_size (r, 2) != 7) abort (); if (__builtin_object_size (r + 6, 2) != 1) abort (); +#endif r = &buf3[18]; for (i = 0; i < 4; ++i) { @@ -227,8 +344,31 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 18; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + else if (i == l1) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4; + } + if (dyn_res >= 12) + { + if (__builtin_object_size (r + 12, 2) != dyn_res - 12) + abort (); + } + else if (__builtin_object_size (r + 12, 2) != 0) + abort (); +#else if (__builtin_object_size (r + 12, 2) != 0) abort (); +#endif } void @@ -371,7 +511,11 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 2) != 0) +#endif abort (); memset (p, ' ', sizeof (buf) - 8 - 4 * 4); } @@ -386,7 +530,11 @@ test6 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x) +#else if (__builtin_object_size (p, 2) != 0) +#endif abort (); memset (p, ' ', sizeof (t) - 8 - 4 * 4); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c index 9f159e36a0f..256cea0b3fb 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c @@ -43,7 +43,12 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (q, 3) != 0) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) + != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1)) +#else if (__builtin_object_size (r, 3) != sizeof (a.a) - 9) +#endif abort (); if (x < 6) r = &w[2].a[1]; @@ -55,31 +60,57 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&y.b, 3) != sizeof (a.b)) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) + != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6)) +#else if (__builtin_object_size (r, 3) != sizeof (a.a) - 6) +#endif abort (); if (x < 20) r = malloc (30); else r = calloc (2, 16); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16)) +#else if (__builtin_object_size (r, 3) != 30) +#endif abort (); if (x < 20) r = malloc (30); else r = calloc (2, 14); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14)) +#else if (__builtin_object_size (r, 3) != 2 * 14) +#endif abort (); if (x < 30) r = malloc (sizeof (a)); else r = &a.a[3]; +#ifdef DYNAMIC_OBJECT_SIZE + size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3; + if (__builtin_object_size (r, 3) != objsz) +#else if (__builtin_object_size (r, 3) != sizeof (a.a) - 3) +#endif abort (); r = memcpy (r, "a", 2); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) != objsz) +#else if (__builtin_object_size (r, 3) != sizeof (a.a) - 3) +#endif abort (); r = memcpy (r + 2, "b", 2) + 2; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) != objsz - 4) +#else if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4) +#endif abort (); r = &a.a[4]; r = memset (r, 'a', 2); @@ -119,6 +150,23 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&extc[5].c[3], 3) != 0) abort (); +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (var, 3) != x + 10) + abort (); + if (__builtin_object_size (var + 10, 3) != x) + abort (); + if (__builtin_object_size (&var[5], 3) != x + 5) + abort (); + size_t vara_sz = (x + 10) * sizeof (struct A); + if (__builtin_object_size (vara, 3) != vara_sz) + abort (); + vara_sz = x * sizeof (struct A); + if (__builtin_object_size (vara + 10, 3) != vara_sz) + abort (); + vara_sz = (x + 5) * sizeof (struct A); + if (__builtin_object_size (&vara[5], 3) != vara_sz) + abort (); +#else if (__builtin_object_size (var, 3) != 0) abort (); if (__builtin_object_size (var + 10, 3) != 0) @@ -131,6 +179,7 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&vara[5], 3) != 0) abort (); +#endif if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a)) abort (); if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a)) @@ -184,6 +233,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef DYNAMIC_OBJECT_SIZE + size_t dyn_res = 0; +#endif if (sizeof (a) != 20) return; @@ -200,8 +252,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3); + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a.buf1) - 1; + else if (i == l1) + dyn_res = sizeof (a.buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a.buf1) - 9; + } + if (__builtin_object_size (r, 3) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 9) abort (); +#endif r = &buf3[20]; for (i = 0; i < 4; ++i) { @@ -214,8 +284,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 20; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a.buf1) - 7; + else if (i == l1) + dyn_res = sizeof (a.buf2) - 7; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a.buf1) - 9; + } + if (__builtin_object_size (r, 3) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 3) != 0) abort (); +#endif r = &buf3[1]; for (i = 0; i < 4; ++i) { @@ -228,13 +316,38 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[2]; } +#ifdef DYNAMIC_OBJECT_SIZE + dyn_res = sizeof (buf3) - 1; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + dyn_res = sizeof (a.buf1) - 6; + else if (i == l1) + dyn_res = sizeof (a.buf2) - 4; + else if (i == l1 + 1) + dyn_res = sizeof (buf3) - 5; + else if (i == l1 + 2) + dyn_res = sizeof (a.buf1) - 2; + } + if (__builtin_object_size (r, 3) != dyn_res) + abort (); +#else if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6) abort (); +#endif r += 2; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (r, 3) != dyn_res - 2) + abort (); + if (__builtin_object_size (r + 1, 3) != dyn_res - 3) + abort (); +#else if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2) abort (); if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3) abort (); +#endif } void @@ -352,7 +465,11 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef DYNAMIC_OBJECT_SIZE + if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 3) != 0) +#endif abort (); memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4); }