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);