From patchwork Sat Dec 18 12:35:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 1570485 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gotplt.org header.i=@gotplt.org header.a=rsa-sha1 header.s=gotplt.org header.b=xaKLRBv+; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4JGQMV14rmz9sVq for ; Sat, 18 Dec 2021 23:36:38 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ED773385843D for ; Sat, 18 Dec 2021 12:36:35 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from coral.oak.relay.mailchannels.net (coral.oak.relay.mailchannels.net [23.83.215.39]) by sourceware.org (Postfix) with ESMTPS id C10DC385842D for ; Sat, 18 Dec 2021 12:35:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C10DC385842D 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 76C7B8213B1; Sat, 18 Dec 2021 12:35:26 +0000 (UTC) Received: from pdx1-sub0-mail-a305.dreamhost.com (unknown [127.0.0.6]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id BBA48820DDF; Sat, 18 Dec 2021 12:35:25 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a305.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.120.81.136 (trex/6.4.3); Sat, 18 Dec 2021 12:35:26 +0000 X-MC-Relay: Bad X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Eight-Lyrical: 488799f359fad40c_1639830926044_1340365374 X-MC-Loop-Signature: 1639830926044:2350479468 X-MC-Ingress-Time: 1639830926043 Received: from rhbox.redhat.com (unknown [1.6.40.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a305.dreamhost.com (Postfix) with ESMTPSA id 4JGQL35JzVz2Q; Sat, 18 Dec 2021 04:35:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gotplt.org; s=gotplt.org; t=1639830925; bh=JsfuuIWnGKqhTVZTuStoq34m+I0=; h=From:To:Cc:Subject:Date:Content-Transfer-Encoding; b=xaKLRBv+x0xpaIo2u1IOcS/PVmR0u6Lm3wR3fLch4fRgfYUDx4PMXRoXan+N1sxXo uKPL/5V/TM6L1/AuMHQXnUlLt13iLj3d6AyygLpH98NUoNmaDb3+HWbd9Yd+wAcu7r Xb7itynEHgGWHpUIhECVQ7owJ5y79qeAkg92AAng= From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Date: Sat, 18 Dec 2021 18:05:08 +0530 Message-Id: <20211218123511.139456-2-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211218123511.139456-1-siddhesh@gotplt.org> References: <20211109190137.1107736-1-siddhesh@gotplt.org> <20211218123511.139456-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, 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+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Handle GIMPLE_PHI and conditionals specially for dynamic objects, returning PHI/conditional expressions instead of just a MIN/MAX estimate. This makes the returned object size variable for loops and conditionals, so tests need to be adjusted to look for precise size in some cases. builtin-dynamic-object-size-5.c had to be modified to only look for success in maximum object size case and skip over the minimum object size tests because the result is no longer a compile time constant. I also added some simple tests to exercise conditionals with dynamic object sizes. gcc/ChangeLog: * builtins.c (fold_builtin_object_size): Adjust for dynamic size expressions. * tree-object-size.c: Include gimplify-me.h. (struct object_size_info): New member UNKNOWNS. (size_initval_p, size_usable_p, object_sizes_get_raw): New functions. (object_sizes_get): Return suitable gimple variable for object size. (bundle_sizes): New function. (object_sizes_set): Use it and handle dynamic object size expressions. (object_sizes_set_temp): New function. (size_for_offset): Adjust for dynamic size expressions. (emit_phi_nodes, propagate_unknowns, gimplify_size_expressions): New functions. (compute_builtin_object_size): Call gimplify_size_expressions for OST_DYNAMIC. (dynamic_object_size): New function. (cond_expr_object_size): Use it. (phi_dynamic_object_size): New function. (collect_object_sizes_for): Call it for OST_DYNAMIC. Adjust to accommodate dynamic object sizes. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c: New tests. * gcc.dg/builtin-dynamic-object-size-10.c: Add comment. * gcc.dg/builtin-dynamic-object-size-5-main.c: New file. * gcc.dg/builtin-dynamic-object-size-5.c: Use it and change test to dg-do run. * gcc.dg/builtin-object-size-5.c [!N]: Define N. (test1, test2, test3, test4) [__builtin_object_size]: Expect exact result for __builtin_dynamic_object_size. * gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect exact size expressions for __builtin_dynamic_object_size. * gcc.dg/builtin-object-size-2.c [__builtin_object_size]: Likewise. * gcc.dg/builtin-object-size-3.c [__builtin_object_size]: Likewise. * gcc.dg/builtin-object-size-4.c [__builtin_object_size]: Likewise. * gcc.dg/builtin-object-size-5.c [__builtin_object_size]: Likewise. Signed-off-by: Siddhesh Poyarekar --- Changes since v4: - Propagate sameness of size and wholesize through PHI nodes whenever possible. Check and avoid emitting wholesize if it is the same as size. - Bail out and punt to __builtin_object_size if PHI node has any complex edges. Use insert_seq_on_edge to emit size PHI node edge values. - Free allocations in tests. gcc/builtins.c | 6 +- .../gcc.dg/builtin-dynamic-object-size-0.c | 77 +++ .../gcc.dg/builtin-dynamic-object-size-10.c | 2 + .../builtin-dynamic-object-size-5-main.c | 32 ++ .../gcc.dg/builtin-dynamic-object-size-5.c | 7 +- gcc/testsuite/gcc.dg/builtin-object-size-1.c | 119 +++- gcc/testsuite/gcc.dg/builtin-object-size-2.c | 92 ++++ gcc/testsuite/gcc.dg/builtin-object-size-3.c | 121 +++++ gcc/testsuite/gcc.dg/builtin-object-size-4.c | 78 +++ gcc/testsuite/gcc.dg/builtin-object-size-5.c | 22 +- gcc/tree-object-size.c | 509 +++++++++++++++++- 11 files changed, 1030 insertions(+), 35 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c diff --git a/gcc/builtins.c b/gcc/builtins.c index 00f6c5552bf..01c24f42540 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -10285,7 +10285,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode) if (TREE_CODE (ptr) == ADDR_EXPR) { compute_builtin_object_size (ptr, object_size_type, &bytes); - if (int_fits_type_p (bytes, size_type_node)) + if ((object_size_type & OST_DYNAMIC) + || int_fits_type_p (bytes, size_type_node)) return fold_convert (size_type_node, bytes); } else if (TREE_CODE (ptr) == SSA_NAME) @@ -10294,7 +10295,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode) later. Maybe subsequent passes will help determining it. */ if (compute_builtin_object_size (ptr, object_size_type, &bytes) - && int_fits_type_p (bytes, size_type_node)) + && ((object_size_type & OST_DYNAMIC) + || int_fits_type_p (bytes, size_type_node))) return fold_convert (size_type_node, 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 new file mode 100644 index 00000000000..81588cb28a6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -0,0 +1,77 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +#define abort __builtin_abort + +size_t +__attribute__ ((noinline)) +test_builtin_malloc_condphi (int cond) +{ + void *ch; + + if (cond) + ch = __builtin_malloc (32); + else + ch = __builtin_malloc (64); + + size_t ret = __builtin_dynamic_object_size (ch, 0); + + __builtin_free (ch); + return ret; +} + +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); + size_t ret = __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0); + + __builtin_free (ch); + return ret; +} + +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 (64); + + return __builtin_dynamic_object_size (bin, 0); +} + +unsigned nfails = 0; + +#define FAIL() ({ \ + __builtin_printf ("Failure at line: %d\n", __LINE__); \ + nfails++; \ +}) + +int +main (int argc, char **argv) +{ + if (test_builtin_malloc_condphi (1) != 32) + FAIL (); + if (test_builtin_malloc_condphi (0) != 64) + FAIL (); + if (test_builtin_calloc_condphi (128, 1, 0) == 128) + FAIL (); + if (test_deploop (128, 129) != 32) + FAIL (); + + if (nfails > 0) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c index bc880a589ae..3a2d9821a44 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c @@ -5,5 +5,7 @@ #define __builtin_object_size __builtin_dynamic_object_size #include "builtin-object-size-10.c" +/* early_objsz should resolve __builtin_dynamic_object_size like + __builtin_object_size. */ /* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */ /* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c new file mode 100644 index 00000000000..49f459b8cf2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c @@ -0,0 +1,32 @@ +#ifdef N +typedef __SIZE_TYPE__ size_t; + +char buf[N]; + +void test1 (size_t); +void test2 (size_t); +void test3 (size_t); +void test4 (size_t); +void test5 (size_t); +void test6 (size_t); +void test7 (size_t); + +int +main (void) +{ + test1 (42); + test2 (42); + test3 (42); + test4 (42); + test5 (42); + test6 (42); + test7 (42); + return 0; +} +#else +int +main (void) +{ + return 0; +} +#endif diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c index 698b03c34be..80b77dd5249 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c @@ -1,7 +1,6 @@ -/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */ -/* { dg-options "-O2" } */ +/* { dg-do run { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */ +/* { dg-options "-O2 -DN=0x4000000" } */ +/* { dg-additional-sources "builtin-dynamic-object-size-5-main.c" } */ #define __builtin_object_size __builtin_dynamic_object_size #include "builtin-object-size-5.c" - -/* { dg-final { scan-assembler-not "abort" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index 0154f4e9695..265c87ed6fb 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 __builtin_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 __builtin_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 @@ -165,6 +181,7 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; + size_t res; if (sizeof (a) != 20) return; @@ -181,7 +198,24 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } - if (__builtin_object_size (r, 0) != 20) +#ifdef __builtin_object_size + res = sizeof (buf3); + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1; + else if (i == l1) + res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + res = sizeof (buf3) - 5; + else if (i == l1 + 2) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } +#else + res = 20; +#endif + if (__builtin_object_size (r, 0) != res) abort (); r = &buf3[20]; for (i = 0; i < 4; ++i) @@ -195,13 +229,45 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } - if (__builtin_object_size (r, 0) != 15) +#ifdef __builtin_object_size + res = sizeof (buf3) - 20; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7; + else if (i == l1) + res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7; + else if (i == l1 + 1) + res = sizeof (buf3) - 5; + else if (i == l1 + 2) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + } + if (__builtin_object_size (r, 0) != res) + abort (); +#else + res = 15; +#endif + if (__builtin_object_size (r, 0) != res) abort (); r += 8; +#ifdef __builtin_object_size + res -= 8; + if (__builtin_object_size (r, 0) != res) + abort (); + if (res >= 6) + { + if (__builtin_object_size (r + 6, 0) != 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 +280,31 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef __builtin_object_size + res = sizeof (buf3) - 18; + + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9; + else if (i == l1) + res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9; + else if (i == l1 + 1) + res = sizeof (buf3) - 5; + else if (i == l1 + 2) + res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4; + } + if (res >= 12) + { + if (__builtin_object_size (r + 12, 0) != 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 +447,10 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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 +460,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 +474,13 @@ test6 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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); } @@ -436,21 +535,37 @@ test9 (unsigned cond) else p = &buf2[4]; +#ifdef __builtin_object_size + if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10)) + abort (); +#else if (__builtin_object_size (&p[-4], 0) != 10) abort (); +#endif for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond)) + abort (); +#else if (__builtin_object_size (p, 0) != 10) abort (); +#endif p = &y.c[8]; for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 0) + != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond) + abort (); +#else if (__builtin_object_size (p, 0) != sizeof (y)) abort (); +#endif } int diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c index 5cf29291aff..5051fea47c3 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c @@ -43,8 +43,15 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (q, 1) != (size_t) -1) abort (); +#ifdef __builtin_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,8 +62,15 @@ test1 (void *q, int x) abort (); if (__builtin_object_size (&y.b, 1) != sizeof (a.b)) abort (); +#ifdef __builtin_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 @@ -185,6 +199,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef __builtin_object_size + size_t dyn_res; +#endif if (sizeof (a) != 20) return; @@ -201,8 +218,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef __builtin_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 +250,50 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef __builtin_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 __builtin_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 +411,13 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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); } @@ -394,21 +471,36 @@ test8 (unsigned cond) else p = &buf2[4]; +#ifdef __builtin_object_size + if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10)) + abort (); +#else if (__builtin_object_size (&p[-4], 1) != 10) abort (); +#endif for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond)) + abort (); +#else if (__builtin_object_size (p, 1) != 10) abort (); +#endif p = &y.c[8]; for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond) + abort (); +#else if (__builtin_object_size (p, 1) != sizeof (y.c)) abort (); +#endif } int diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c index 3a692c4e3d2..1d92627266b 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -71,23 +71,45 @@ test1 (void *q, int x) r = malloc (30); else r = calloc (2, 14); +#ifdef __builtin_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 __builtin_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 __builtin_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 __builtin_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) @@ -164,6 +186,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef __builtin_object_size + size_t dyn_res; +#endif if (sizeof (a) != 20) return; @@ -180,8 +205,26 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[9]; } +#ifdef __builtin_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) { @@ -208,13 +251,44 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef __builtin_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 __builtin_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 +301,31 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[4]; } +#ifdef __builtin_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 +468,11 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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 +487,11 @@ test6 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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); } @@ -442,22 +547,38 @@ test9 (unsigned cond) else p = &buf2[4]; +#ifdef __builtin_object_size + if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10)) + abort (); +#else if (__builtin_object_size (&p[-4], 2) != 6) abort (); +#endif for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond)) + abort (); +#else if (__builtin_object_size (p, 2) != 2) abort (); +#endif p = &y.c[8]; for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 2) + != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond) + abort (); +#else if (__builtin_object_size (p, 2) != sizeof (y) - __builtin_offsetof (struct A, c) - 8) abort (); +#endif } int diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c index 87381620cc9..9da3537a5f7 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 __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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); @@ -184,6 +215,9 @@ test2 (void) struct B { char buf1[10]; char buf2[10]; } a; char *r, buf3[20]; int i; +#ifdef __builtin_object_size + size_t dyn_res = 0; +#endif if (sizeof (a) != 20) return; @@ -228,13 +262,38 @@ test2 (void) else if (i == l1 + 2) r = &a.buf1[2]; } +#ifdef __builtin_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 __builtin_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 +411,11 @@ test5 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_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); } @@ -407,21 +470,36 @@ test8 (unsigned cond) else p = &buf2[4]; +#ifdef __builtin_object_size + if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10)) + abort (); +#else if (__builtin_object_size (&p[-4], 3) != 6) abort (); +#endif for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond)) + abort (); +#else if (__builtin_object_size (p, 3) != 2) abort (); +#endif p = &y.c[8]; for (unsigned i = cond; i > 0; i--) p--; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond) + abort (); +#else if (__builtin_object_size (p, 3) != sizeof (y.c) - 8) abort (); +#endif } int diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c index 8e63d9c7a5e..cb8f753b039 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c @@ -1,9 +1,13 @@ /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */ /* { dg-options "-O2" } */ +#ifndef N +# define N 0x40000000 +#endif + typedef __SIZE_TYPE__ size_t; extern void abort (void); -extern char buf[0x40000000]; +extern char buf[N]; void test1 (size_t x) @@ -13,7 +17,11 @@ test1 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 0) != sizeof (buf) - 8) +#endif abort (); } @@ -25,7 +33,11 @@ test2 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 1) != sizeof (buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 1) != sizeof (buf) - 8) +#endif abort (); } @@ -37,7 +49,11 @@ test3 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 2) != 0) +#endif abort (); } @@ -49,7 +65,11 @@ test4 (size_t x) for (i = 0; i < x; ++i) p = p + 4; +#ifdef __builtin_object_size + if (__builtin_object_size (p, 3) != sizeof (buf) - 8 - 4 * x) +#else if (__builtin_object_size (p, 3) != 0) +#endif abort (); } diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c index ee9ea1bfbfd..95cb44d9c7e 100644 --- a/gcc/tree-object-size.c +++ b/gcc/tree-object-size.c @@ -35,13 +35,14 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "builtins.h" +#include "gimplify-me.h" struct object_size_info { int object_size_type; unsigned char pass; bool changed; - bitmap visited, reexamine; + bitmap visited, reexamine, unknowns; unsigned int *depths; unsigned int *stack, *tos; }; @@ -74,7 +75,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree, object_sizes[1] is upper bound for the object size and number of bytes till the end of the subobject (innermost array or field with address taken). object_sizes[2] is lower bound for the object size and number of bytes till - the end of the object and object_sizes[3] lower bound for subobject. */ + the end of the object and object_sizes[3] lower bound for subobject. + + For static object sizes, the object size and the bytes till the end of the + object are both INTEGER_CST. In the dynamic case, they are finally either a + gimple variable or an INTEGER_CST. */ static vec object_sizes[OST_END]; /* Bitmaps what object sizes have been computed already. */ @@ -83,6 +88,15 @@ static bitmap computed[OST_END]; /* Maximum value of offset we consider to be addition. */ static unsigned HOST_WIDE_INT offset_limit; +/* Return true if VAL is represents an initial size for OBJECT_SIZE_TYPE. */ + +static inline bool +size_initval_p (tree val, int object_size_type) +{ + return ((object_size_type & OST_MINIMUM) + ? integer_all_onesp (val) : integer_zerop (val)); +} + /* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE. */ static inline bool @@ -92,6 +106,15 @@ size_unknown_p (tree val, int object_size_type) ? integer_zerop (val) : integer_all_onesp (val)); } +/* Return true if VAL is usable as an object size in the object_sizes + vectors. */ + +static inline bool +size_usable_p (tree val) +{ + return TREE_CODE (val) == SSA_NAME || TREE_CODE (val) == INTEGER_CST; +} + /* Return a tree with initial value for OBJECT_SIZE_TYPE. */ static inline tree @@ -136,17 +159,43 @@ object_sizes_unknown_p (int object_size_type, unsigned varno) object_size_type); } -/* Return size for VARNO corresponding to OSI. If WHOLE is true, return the - whole object size. */ +/* Return the raw size expression for VARNO corresponding to OSI. This returns + the TREE_VEC as is and should only be used during gimplification. */ + +static inline object_size +object_sizes_get_raw (struct object_size_info *osi, unsigned varno) +{ + gcc_assert (osi->pass != 0); + return object_sizes[osi->object_size_type][varno]; +} + +/* Return a size tree for VARNO corresponding to OSI. If WHOLE is true, return + the whole object size. Use this for building size expressions based on size + of VARNO. */ static inline tree object_sizes_get (struct object_size_info *osi, unsigned varno, bool whole = false) { + tree ret; + int object_size_type = osi->object_size_type; + if (whole) - return object_sizes[osi->object_size_type][varno].wholesize; + ret = object_sizes[object_size_type][varno].wholesize; else - return object_sizes[osi->object_size_type][varno].size; + ret = object_sizes[object_size_type][varno].size; + + if (object_size_type & OST_DYNAMIC) + { + if (TREE_CODE (ret) == MODIFY_EXPR) + return TREE_OPERAND (ret, 0); + else if (TREE_CODE (ret) == TREE_VEC) + return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1); + else + gcc_checking_assert (size_usable_p (ret)); + } + + return ret; } /* Set size for VARNO corresponding to OSI to VAL. */ @@ -161,28 +210,116 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno, object_sizes[object_size_type][varno].wholesize = wholeval; } +/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type. The + TREE_VEC is returned only in case of PHI nodes. */ + +static tree +bundle_sizes (tree name, tree expr) +{ + gcc_checking_assert (TREE_TYPE (name) == sizetype); + + if (TREE_CODE (expr) == TREE_VEC) + { + TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = name; + return expr; + } + + gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype)); + return build2 (MODIFY_EXPR, sizetype, name, expr); +} + /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or - maximum. */ + maximum. For static sizes, each element of TREE_VEC is always INTEGER_CST + throughout the computation. For dynamic sizes, each element may either be a + gimple variable, a MODIFY_EXPR or a TREE_VEC. The MODIFY_EXPR is for + expressions that need to be gimplified. TREE_VECs are special, they're + emitted only for GIMPLE_PHI and the PHI result variable is the last element + of the vector. */ -static inline bool +static bool object_sizes_set (struct object_size_info *osi, unsigned varno, tree val, tree wholeval) { int object_size_type = osi->object_size_type; object_size osize = object_sizes[object_size_type][varno]; + bool changed = true; tree oldval = osize.size; tree old_wholeval = osize.wholesize; - enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR; + if (object_size_type & OST_DYNAMIC) + { + if (bitmap_bit_p (osi->reexamine, varno)) + { + if (size_unknown_p (val, object_size_type)) + { + oldval = object_sizes_get (osi, varno); + old_wholeval = object_sizes_get (osi, varno, true); + bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval)); + bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval)); + bitmap_clear_bit (osi->reexamine, varno); + } + else + { + val = bundle_sizes (oldval, val); + wholeval = bundle_sizes (old_wholeval, wholeval); + } + } + else + { + gcc_checking_assert (size_initval_p (oldval, object_size_type)); + gcc_checking_assert (size_initval_p (old_wholeval, + object_size_type)); + /* For dynamic object sizes, all object sizes that are not gimple + variables will need to be gimplified. */ + if (wholeval != val && !size_usable_p (wholeval)) + { + bitmap_set_bit (osi->reexamine, varno); + wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval); + } + if (!size_usable_p (val)) + { + bitmap_set_bit (osi->reexamine, varno); + tree newval = bundle_sizes (make_ssa_name (sizetype), val); + if (val == wholeval) + wholeval = newval; + val = newval; + } + /* If the new value is a temporary variable, mark it for + reexamination. */ + else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val)) + bitmap_set_bit (osi->reexamine, varno); + } + } + else + { + enum tree_code code = (object_size_type & OST_MINIMUM + ? MIN_EXPR : MAX_EXPR); - val = size_binop (code, val, oldval); - wholeval = size_binop (code, wholeval, old_wholeval); + val = size_binop (code, val, oldval); + wholeval = size_binop (code, wholeval, old_wholeval); + changed = (tree_int_cst_compare (val, oldval) != 0 + || tree_int_cst_compare (old_wholeval, wholeval) != 0); + } object_sizes[object_size_type][varno].size = val; object_sizes[object_size_type][varno].wholesize = wholeval; - return (tree_int_cst_compare (oldval, val) != 0 - || tree_int_cst_compare (old_wholeval, wholeval) != 0); + + return changed; +} + +/* Set temporary SSA names for object size and whole size to resolve dependency + loops in dynamic size computation. */ + +static inline void +object_sizes_set_temp (struct object_size_info *osi, unsigned varno) +{ + tree val = object_sizes_get (osi, varno); + + if (size_initval_p (val, osi->object_size_type)) + object_sizes_set (osi, varno, + make_ssa_name (sizetype), + make_ssa_name (sizetype)); } /* Initialize OFFSET_LIMIT variable. */ @@ -204,14 +341,15 @@ static tree size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE) { gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST); - gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST); gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype)); /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net offset from the whole object. */ - if (wholesize && tree_int_cst_compare (sz, wholesize)) + if (wholesize && wholesize != sz + && (TREE_CODE (sz) != INTEGER_CST + || TREE_CODE (wholesize) != INTEGER_CST + || tree_int_cst_compare (sz, wholesize))) { - gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST); gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize), sizetype)); @@ -228,13 +366,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE) if (!types_compatible_p (TREE_TYPE (offset), sizetype)) fold_convert (sizetype, offset); - if (integer_zerop (offset)) - return sz; + if (TREE_CODE (offset) == INTEGER_CST) + { + if (integer_zerop (offset)) + return sz; - /* Negative or too large offset even after adjustment, cannot be within - bounds of an object. */ - if (compare_tree_int (offset, offset_limit) > 0) - return size_zero_node; + /* Negative or too large offset even after adjustment, cannot be within + bounds of an object. */ + if (compare_tree_int (offset, offset_limit) > 0) + return size_zero_node; + } return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset); } @@ -671,6 +812,201 @@ pass_through_call (const gcall *call) return NULL_TREE; } +/* Emit PHI nodes for size expressions fo. */ + +static void +emit_phi_nodes (gimple *stmt, tree size, tree wholesize) +{ + tree phires; + gphi *wholephi = NULL; + + if (wholesize != size) + { + phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1); + wholephi = create_phi_node (phires, gimple_bb (stmt)); + } + + phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1); + gphi *phi = create_phi_node (phires, gimple_bb (stmt)); + gphi *obj_phi = as_a (stmt); + + gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC); + gcc_checking_assert (TREE_CODE (size) == TREE_VEC); + + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + { + gimple_seq seq = NULL; + tree wsz = TREE_VEC_ELT (wholesize, i); + tree sz = TREE_VEC_ELT (size, i); + + /* If we built an expression, we will need to build statements + and insert them on the edge right away. */ + if (TREE_CODE (wsz) != SSA_NAME) + wsz = force_gimple_operand (wsz, &seq, true, NULL); + if (TREE_CODE (sz) != SSA_NAME) + { + gimple_seq s; + sz = force_gimple_operand (sz, &s, true, NULL); + gimple_seq_add_seq (&seq, s); + } + + if (seq) + gsi_insert_seq_on_edge (gimple_phi_arg_edge (obj_phi, i), seq); + + if (wholephi) + add_phi_arg (wholephi, wsz, + gimple_phi_arg_edge (obj_phi, i), + gimple_phi_arg_location (obj_phi, i)); + + add_phi_arg (phi, sz, + gimple_phi_arg_edge (obj_phi, i), + gimple_phi_arg_location (obj_phi, i)); + } +} + +/* Descend through EXPR and return size_unknown if it uses any SSA variable + object_size_set or object_size_set_temp generated, which turned out to be + size_unknown, as noted in UNKNOWNS. */ + +static tree +propagate_unknowns (object_size_info *osi, tree expr) +{ + int object_size_type = osi->object_size_type; + + switch (TREE_CODE (expr)) + { + case SSA_NAME: + if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr))) + return size_unknown (object_size_type); + return expr; + + case MIN_EXPR: + case MAX_EXPR: + { + tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0)); + if (size_unknown_p (res, object_size_type)) + return res; + + res = propagate_unknowns (osi, TREE_OPERAND (expr, 1)); + if (size_unknown_p (res, object_size_type)) + return res; + + return expr; + } + case MODIFY_EXPR: + { + tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1)); + if (size_unknown_p (res, object_size_type)) + return res; + return expr; + } + case TREE_VEC: + for (int i = 0; i < TREE_VEC_LENGTH (expr); i++) + { + tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i)); + if (size_unknown_p (res, object_size_type)) + return res; + } + return expr; + case PLUS_EXPR: + case MINUS_EXPR: + { + tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0)); + if (size_unknown_p (res, object_size_type)) + return res; + + return expr; + } + default: + return expr; + } +} + +/* Walk through size expressions that need reexamination and generate + statements for them. */ + +static void +gimplify_size_expressions (object_size_info *osi) +{ + int object_size_type = osi->object_size_type; + bitmap_iterator bi; + unsigned int i; + bool changed; + + /* Step 1: Propagate unknowns into expressions. */ + bitmap reexamine = BITMAP_ALLOC (NULL); + bitmap_copy (reexamine, osi->reexamine); + do + { + changed = false; + EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) + { + object_size cur = object_sizes_get_raw (osi, i); + + if (size_unknown_p (propagate_unknowns (osi, cur.size), + object_size_type) + || size_unknown_p (propagate_unknowns (osi, cur.wholesize), + object_size_type)) + { + object_sizes_set (osi, i, + size_unknown (object_size_type), + size_unknown (object_size_type)); + changed = true; + } + } + bitmap_copy (reexamine, osi->reexamine); + } + while (changed); + + /* Release all unknowns. */ + EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi) + release_ssa_name (ssa_name (i)); + + /* Expand all size expressions to put their definitions close to the objects + for whom size is being computed. */ + EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi) + { + gimple_seq seq = NULL; + object_size osize = object_sizes_get_raw (osi, i); + + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i)); + enum gimple_code code = gimple_code (stmt); + + /* PHI nodes need special attention. */ + if (code == GIMPLE_PHI) + emit_phi_nodes (stmt, osize.size, osize.wholesize); + else + { + tree size_expr = NULL_TREE; + + /* Bundle wholesize in with the size to gimplify if needed. */ + if (osize.wholesize != osize.size + && !size_usable_p (osize.wholesize)) + size_expr = size_binop (COMPOUND_EXPR, + osize.wholesize, + osize.size); + else if (!size_usable_p (osize.size)) + size_expr = osize.size; + + if (size_expr) + { + gimple_stmt_iterator gsi; + if (code == GIMPLE_NOP) + gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun))); + else + gsi = gsi_for_stmt (stmt); + + force_gimple_operand (size_expr, &seq, true, NULL); + gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING); + } + } + + /* We're done, so replace the MODIFY_EXPRs with the SSA names. */ + object_sizes_initialize (osi, i, + object_sizes_get (osi, i), + object_sizes_get (osi, i, true)); + } +} /* Compute __builtin_object_size value for PTR and set *PSIZE to the resulting value. If the declared object is known and PDECL @@ -749,9 +1085,15 @@ compute_builtin_object_size (tree ptr, int object_size_type, osi.visited = BITMAP_ALLOC (NULL); osi.reexamine = BITMAP_ALLOC (NULL); - osi.depths = NULL; - osi.stack = NULL; - osi.tos = NULL; + + if (object_size_type & OST_DYNAMIC) + osi.unknowns = BITMAP_ALLOC (NULL); + else + { + osi.depths = NULL; + osi.stack = NULL; + osi.tos = NULL; + } /* First pass: walk UD chains, compute object sizes that can be computed. osi.reexamine bitmap at the end will @@ -761,6 +1103,14 @@ compute_builtin_object_size (tree ptr, int object_size_type, osi.changed = false; collect_object_sizes_for (&osi, ptr); + if (object_size_type & OST_DYNAMIC) + { + osi.pass = 1; + gimplify_size_expressions (&osi); + BITMAP_FREE (osi.unknowns); + bitmap_clear (osi.reexamine); + } + /* Second pass: keep recomputing object sizes of variables that need reexamination, until no object sizes are increased or all object sizes are computed. */ @@ -1007,6 +1357,25 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt) return reexamine; } +static void +dynamic_object_size (struct object_size_info *osi, tree var, + tree *size, tree *wholesize) +{ + int object_size_type = osi->object_size_type; + + if (TREE_CODE (var) == SSA_NAME) + { + unsigned varno = SSA_NAME_VERSION (var); + + collect_object_sizes_for (osi, var); + *size = object_sizes_get (osi, varno); + *wholesize = object_sizes_get (osi, varno, true); + } + else if (TREE_CODE (var) == ADDR_EXPR) + addr_object_size (osi, var, object_size_type, size, wholesize); + else + *size = *wholesize = size_unknown (object_size_type); +} /* Compute object_sizes for VAR, defined at STMT, which is a COND_EXPR. Return true if the object size might need reexamination @@ -1028,6 +1397,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt) then_ = gimple_assign_rhs2 (stmt); else_ = gimple_assign_rhs3 (stmt); + if (object_size_type & OST_DYNAMIC) + { + tree then_size, then_wholesize, else_size, else_wholesize; + + dynamic_object_size (osi, then_, &then_size, &then_wholesize); + if (!size_unknown_p (then_size, object_size_type)) + dynamic_object_size (osi, else_, &else_size, &else_wholesize); + + tree cond_size, cond_wholesize; + if (size_unknown_p (then_size, object_size_type) + || size_unknown_p (else_size, object_size_type)) + cond_size = cond_wholesize = size_unknown (object_size_type); + else + { + cond_size = fold_build3 (COND_EXPR, sizetype, + gimple_assign_rhs1 (stmt), + then_size, else_size); + cond_wholesize = fold_build3 (COND_EXPR, sizetype, + gimple_assign_rhs1 (stmt), + then_wholesize, else_wholesize); + } + + object_sizes_set (osi, varno, cond_size, cond_wholesize); + + return false; + } + if (TREE_CODE (then_) == SSA_NAME) reexamine |= merge_object_sizes (osi, var, then_); else @@ -1044,6 +1440,57 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt) return reexamine; } +/* Compute an object size expression for VAR, which is the result of a PHI + node. */ + +static void +phi_dynamic_object_size (struct object_size_info *osi, tree var) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (var); + gimple *stmt = SSA_NAME_DEF_STMT (var); + unsigned i, num_args = gimple_phi_num_args (stmt); + bool wholesize_needed = false; + + /* The extra space is for the PHI result at the end, which object_sizes_set + sets for us. */ + tree sizes = make_tree_vec (num_args + 1); + tree wholesizes = make_tree_vec (num_args + 1); + + /* Bail out if the size of any of the PHI arguments cannot be + determined. */ + for (i = 0; i < num_args; i++) + { + edge e = gimple_phi_arg_edge (as_a (stmt), i); + if (e->flags & EDGE_COMPLEX) + break; + + tree rhs = gimple_phi_arg_def (stmt, i); + tree size, wholesize; + + dynamic_object_size (osi, rhs, &size, &wholesize); + + if (size_unknown_p (size, object_size_type)) + break; + + if (size != wholesize) + wholesize_needed = true; + + TREE_VEC_ELT (sizes, i) = size; + TREE_VEC_ELT (wholesizes, i) = wholesize; + } + + if (i < num_args) + sizes = wholesizes = size_unknown (object_size_type); + + /* Point to the same TREE_VEC so that we can avoid emitting two PHI + nodes. */ + if (!wholesize_needed) + wholesizes = sizes; + + object_sizes_set (osi, varno, sizes, wholesizes); +} + /* Compute object sizes for VAR. For ADDR_EXPR an object size is the number of remaining bytes to the end of the object (where what is considered an object depends on @@ -1089,6 +1536,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) { /* Found a dependency loop. Mark the variable for later re-examination. */ + if (object_size_type & OST_DYNAMIC) + object_sizes_set_temp (osi, varno); + bitmap_set_bit (osi->reexamine, varno); if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -1170,6 +1620,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) { unsigned i; + if (object_size_type & OST_DYNAMIC) + { + phi_dynamic_object_size (osi, var); + break; + } + for (i = 0; i < gimple_phi_num_args (stmt); i++) { tree rhs = gimple_phi_arg (stmt, i)->def; @@ -1192,7 +1648,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) if (! reexamine || object_sizes_unknown_p (object_size_type, varno)) { bitmap_set_bit (computed[object_size_type], varno); - bitmap_clear_bit (osi->reexamine, varno); + if (!(object_size_type & OST_DYNAMIC)) + bitmap_clear_bit (osi->reexamine, varno); } else {