From patchwork Tue Nov 9 19:01:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 47329 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 67D82385AC1B for ; Tue, 9 Nov 2021 19:06:44 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from hamster.birch.relay.mailchannels.net (hamster.birch.relay.mailchannels.net [23.83.209.80]) by sourceware.org (Postfix) with ESMTPS id 80A703857C7B for ; Tue, 9 Nov 2021 19:02:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 80A703857C7B 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 15F17121BDE; Tue, 9 Nov 2021 19:02:14 +0000 (UTC) Received: from pdx1-sub0-mail-a306.dreamhost.com (100-96-18-144.trex.outbound.svc.cluster.local [100.96.18.144]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 4BA7E12169C; Tue, 9 Nov 2021 19:02:13 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a306.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.96.18.144 (trex/6.4.3); Tue, 09 Nov 2021 19:02:14 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Spicy-Abiding: 75eca1533b440910_1636484533694_3173258182 X-MC-Loop-Signature: 1636484533694:2809167630 X-MC-Ingress-Time: 1636484533694 Received: from rhbox.redhat.com (unknown [1.186.121.127]) (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-a306.dreamhost.com (Postfix) with ESMTPSA id 4HpcmM3Xn4z2n; Tue, 9 Nov 2021 11:02:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gotplt.org; s=gotplt.org; t=1636484533; bh=GGoQvcxJsqK7S51WhBy/iK1Gbss=; h=From:To:Cc:Subject:Date:Content-Transfer-Encoding; b=HwDGgpokoNl5eNltB39kT9e24MWzSYkaH0xLQKYedgMHFBRkPUArzIDJKk8nNq2Ca WrFccX5/11s1YTDKX6sznjavNzF6qSUbeWGXOEJnnM3wbcFavj3QGJsbjapXkkkMnW 1BqT405+zzd/+H5E//9HSyI8bXZ5YQyjIc+lxtnA= From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH 10/10] tree-object-size: Handle dynamic offsets Date: Wed, 10 Nov 2021 00:31:36 +0530 Message-Id: <20211109190137.1107736-11-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211109190137.1107736-1-siddhesh@gotplt.org> References: <20211109190137.1107736-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_MSPIKE_H2, RCVD_IN_SBL, 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" Compute whole sizes for objects to allow offsets to be negative for dynamic object sizes. This way, the returned object size would be precise even for negative offsets as long as the offset is within bounds of the object. gcc/ChangeLog: * tree-object-size.c (addr_object_size, expr_object_size, plus_stmt_object_size, cond_expr_object_size, phi_dynamic_object_size, parm_object_size): Add new wholesize argument and handle whole sizes. (object_whole_sizes): New vector array. (object_sizes_grow, object_sizes_release, object_sizes_get, object_sizes_set, object_sizes_initialize): Add new wholesize argument to manage object_whole_sizes arrays. (size_for_offset): Get offset from wholesize if available. (estimate_size): Handle COMPOUND_EXPR. (gimplify_size_expressions): Also remove the whole size SSA. (bundle_whole_size): New function. (ssa_object_size): Remove function. (collect_object_sizes_for): Return a tree and accept new wholesize argument. (init_object_sizes): Allocate object_whole_sizes. (fini_object_sizes): Free object_whole_sizes. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c: Add new tests. Signed-off-by: Siddhesh Poyarekar --- .../gcc.dg/builtin-dynamic-object-size-0.c | 87 ++++ gcc/tree-object-size.c | 423 ++++++++++++------ 2 files changed, 383 insertions(+), 127 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 a1db63b2d45..3b38b915593 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -273,6 +273,25 @@ test_substring (size_t sz, size_t off) 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); +} + +size_t +__attribute__ ((noinline)) +test_substring_ptrplus2 (size_t sz, size_t off, size_t off2) +{ + int str[sz]; + int *ptr = &str[off]; + + return __builtin_dynamic_object_size (ptr + off2, 0); +} + size_t __attribute__ ((access (__read_write__, 1, 2))) test_parmsz_simple (void *obj, size_t sz) @@ -280,6 +299,40 @@ test_parmsz_simple (void *obj, size_t sz) return __builtin_dynamic_object_size (obj, 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); +} + +size_t +__attribute__ ((noinline)) +__attribute__ ((access (__read_write__, 1, 2))) +test_parmsz_scale (int *obj, size_t sz, size_t off) +{ + return __builtin_dynamic_object_size (obj + off, 0); +} + +size_t +__attribute__ ((noinline)) +__attribute__ ((access (__read_write__, 1, 2))) +test_loop (int *obj, size_t sz, size_t start, size_t end, int incr) +{ + int *ptr = obj + start; + + for (int i = start; i != end; i = i + incr) + { + ptr = ptr + incr; + if (__builtin_dynamic_object_size (ptr, 0) == 0) + return 0; + } + + return __builtin_dynamic_object_size (ptr, 0); +} + + unsigned nfails = 0; #define FAIL() ({ \ @@ -357,6 +410,14 @@ main (int argc, char **argv) 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) + FAIL (); + if (test_substring_ptrplus2 (128, 4, 4) != (128 - 8) * sizeof (int)) + FAIL (); + if (test_substring_ptrplus2 (128, 4, -3) != (128 - 1) * sizeof (int)) + FAIL (); if (test_dynarray_cond (0) != 16) FAIL (); if (test_dynarray_cond (1) != 8) @@ -368,6 +429,32 @@ main (int argc, char **argv) if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1) != __builtin_strlen (argv[0]) + 1) 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 (); + int in[42]; + if (test_parmsz_scale (in, 42, 2) != 40 * sizeof (int)) + FAIL (); + if (test_loop (in, 42, 0, 32, 1) != 10 * sizeof (int)) + FAIL (); + if (test_loop (in, 42, 32, -1, -1) != 0) + FAIL (); + if (test_loop (in, 42, 32, 10, -1) != 32 * sizeof (int)) + FAIL (); + if (test_loop (in, 42, 42, 0, -1) != 42 * sizeof (int)) + FAIL (); + if (test_loop (in, 42, 44, 0, -1) != 0) + FAIL (); + if (test_loop (in, 42, 20, 52, 1) != 0) + FAIL (); if (nfails > 0) __builtin_abort (); diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c index 865fc3feea5..d82937de6ba 100644 --- a/gcc/tree-object-size.c +++ b/gcc/tree-object-size.c @@ -57,14 +57,14 @@ enum static tree compute_object_offset (const_tree, const_tree); static bool addr_object_size (struct object_size_info *, - const_tree, int, tree *); + const_tree, int, tree *, tree *t = NULL); static tree alloc_object_size (const gcall *, int); static tree pass_through_call (const gcall *); -static void collect_object_sizes_for (struct object_size_info *, tree); -static tree expr_object_size (struct object_size_info *, tree); -static tree ssa_object_size (struct object_size_info *, tree, tree); -static tree plus_stmt_object_size (struct object_size_info *, gimple *); -static tree cond_expr_object_size (struct object_size_info *, gimple *); +static tree collect_object_sizes_for (struct object_size_info *, tree, + tree *t = NULL); +static tree expr_object_size (struct object_size_info *, tree, tree *); +static tree plus_stmt_object_size (struct object_size_info *, tree, tree *); +static tree cond_expr_object_size (struct object_size_info *, tree, tree *); static void init_offset_limit (void); /* object_sizes[0] is upper bound for number of bytes till the end of @@ -90,6 +90,9 @@ static void init_offset_limit (void); - Its value in object_sizes is an expression that evaluates to the size. */ static vec object_sizes[OST_END]; +/* Whole size expressions are only needed for dynamic object sizes. */ +static vec object_whole_sizes[OST_DYNAMIC]; + /* Bitmaps what object sizes have been computed already. */ static bitmap computed[OST_END]; @@ -149,21 +152,35 @@ size_unknown (int object_size_type) return size_int (unknown (object_size_type)); } -/* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names. */ +/* Grow object_sizes[OBJECT_SIZE_TYPE] and if WHOLE is true, + object_whole_sizes[OBJECT_SIZE_TYPE] to num_ssa_names. */ static inline void -object_sizes_grow (int object_size_type) +object_sizes_grow (int object_size_type, bool whole = false) { if (num_ssa_names > object_sizes[object_size_type].length ()) object_sizes[object_size_type].safe_grow (num_ssa_names, true); + if (whole) + { + gcc_assert (object_size_type & OST_DYNAMIC); + object_size_type &= ~OST_DYNAMIC; + if (num_ssa_names > object_whole_sizes[object_size_type].length ()) + object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true); + } } /* Release object_sizes[OBJECT_SIZE_TYPE]. */ static inline void -object_sizes_release (int object_size_type) +object_sizes_release (int object_size_type, bool whole = false) { object_sizes[object_size_type].release (); + if (whole) + { + gcc_assert (object_size_type & OST_DYNAMIC); + object_size_type &= ~OST_DYNAMIC; + object_whole_sizes[object_size_type].release (); + } } /* Return true if object_sizes[OBJECT_SIZE_TYPE][VARNO] is unknown. */ @@ -178,8 +195,16 @@ object_sizes_unknown_p (int object_size_type, unsigned varno) /* Return size for VARNO corresponding to OSI. */ static inline tree -object_sizes_get (struct object_size_info *osi, unsigned varno) +object_sizes_get (struct object_size_info *osi, unsigned varno, + bool whole = false) { + if (whole) + { + int object_size_type = osi->object_size_type; + gcc_assert (object_size_type & OST_DYNAMIC); + object_size_type &= ~OST_DYNAMIC; + return object_whole_sizes[object_size_type][varno]; + } return object_sizes[osi->object_size_type][varno]; } @@ -187,22 +212,38 @@ object_sizes_get (struct object_size_info *osi, unsigned varno) static inline void object_sizes_initialize (struct object_size_info *osi, unsigned varno, - tree val = NULL_TREE) + tree val, bool whole = false) { int object_size_type = osi->object_size_type; - if (!val) - val = size_initval (object_size_type); object_sizes[object_size_type][varno] = val; + if (whole) + { + object_size_type = osi->object_size_type; + gcc_assert (object_size_type & OST_DYNAMIC); + object_size_type &= ~OST_DYNAMIC; + object_whole_sizes[object_size_type][varno] = val; + } } /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or maximum. */ static inline void -object_sizes_set (struct object_size_info *osi, unsigned varno, tree val) +object_sizes_set (struct object_size_info *osi, unsigned varno, tree val, + bool whole = false) { int object_size_type = osi->object_size_type; + + if (whole) + { + int object_size_type = osi->object_size_type; + gcc_assert (object_size_type & OST_DYNAMIC); + object_size_type &= ~OST_DYNAMIC; + object_whole_sizes[object_size_type][varno] = val; + return; + } + tree curval = object_sizes[object_size_type][varno]; /* Object size is set at most twice, once to put in an SSA name to resolve @@ -239,11 +280,21 @@ init_offset_limit (void) offset_limit /= 2; } -/* Bytes at end of the object with SZ from offset OFFSET. */ +/* Bytes at end of the object with SZ from offset OFFSET. If WHOLESIZE is + present, use it to allow negative OFFSET to the extent that it does not + underflow WHOLESIZE. */ static tree -size_for_offset (tree sz, tree offset) +size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE) { + if (wholesize) + { + offset = size_binop (PLUS_EXPR, size_binop (MAX_EXPR, wholesize, sz), + offset); + offset = size_binop (MINUS_EXPR, offset, sz); + sz = wholesize; + } + return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset); } @@ -360,7 +411,7 @@ decl_init_size (tree decl, bool min) static bool addr_object_size (struct object_size_info *osi, const_tree ptr, - int object_size_type, tree *psize) + int object_size_type, tree *psize, tree *wholesize) { tree pt_var, pt_var_size = NULL_TREE, var_size, bytes; @@ -369,6 +420,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, /* Set to unknown and overwrite just before returning if the size could be determined. */ *psize = size_unknown (object_size_type); + if (wholesize) + *wholesize = size_unknown (object_size_type); pt_var = TREE_OPERAND (ptr, 0); while (handled_component_p (pt_var)) @@ -391,21 +444,13 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, else { tree var = TREE_OPERAND (pt_var, 0); - collect_object_sizes_for (osi, var); - if (bitmap_bit_p (computed[object_size_type], - SSA_NAME_VERSION (var))) - sz = object_sizes_get (osi, SSA_NAME_VERSION (var)); - else + sz = collect_object_sizes_for (osi, var); + if (!bitmap_bit_p (computed[object_size_type], + SSA_NAME_VERSION (var))) sz = size_unknown (object_size_type); } if (!size_unknown_p (sz, object_size_type)) - { - tree offset = TREE_OPERAND (pt_var, 1); - if (TREE_CODE (offset) != INTEGER_CST) - sz = size_unknown (object_size_type); - else - sz = size_for_offset (sz, offset); - } + sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1)); if (!size_unknown_p (sz, object_size_type) && (TREE_CODE (sz) != INTEGER_CST @@ -570,6 +615,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, if (size_known_p (bytes, object_size_type)) { + if ((object_size_type & OST_DYNAMIC) && wholesize) + *wholesize = object_size_type & OST_SUBOBJECT ? bytes : pt_var_size; *psize = bytes; return true; } @@ -766,6 +813,8 @@ estimate_size (object_size_info *osi, tree size, bitmap *visitlog = NULL) } return size_binop (code, ret, off); } + case COMPOUND_EXPR: + return estimate_size (osi, TREE_OPERAND (size, 1), visitlog); case INTEGER_CST: default: return size; @@ -943,6 +992,13 @@ gimplify_size_expressions (object_size_info *osi) if (stmt) { gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + remove_phi_node (&gsi, false); + + /* Also remove the whole size SSA. */ + stmt = SSA_NAME_DEF_STMT (object_sizes_get (osi, varno, + true)); + gcc_checking_assert (stmt); + gsi = gsi_for_stmt (stmt); remove_phi_node (&gsi, true); } bitmap_set_bit (tempsize_free, i); @@ -1043,7 +1099,7 @@ compute_builtin_object_size (tree ptr, int object_size_type, bitmap_iterator bi; unsigned int i; - object_sizes_grow (object_size_type); + object_sizes_grow (object_size_type, object_size_type & OST_DYNAMIC); if (dump_file) { fprintf (dump_file, "Computing %s %s%sobject size for ", @@ -1142,10 +1198,52 @@ make_or_get_tempsize (struct object_size_info *osi, unsigned varno) return ssa; } +static tree +bundle_whole_size (struct object_size_info *osi, tree var, tree res, + tree *wholesize) +{ + int object_size_type = osi->object_size_type; + + if (!(object_size_type & OST_DYNAMIC) + || size_unknown_p (res, object_size_type)) + return res; + + tree cur = NULL_TREE; + + /* Get the temp SSA name if we created one for a dependency loop. */ + if (TREE_CODE (var) == SSA_NAME) + cur = object_sizes_get (osi, SSA_NAME_VERSION (var), true); + + /* If we need to gimplify WHOLERES, fold it into a compound expression + along with RES so that their statements are emitted together. */ + if (cur && TREE_CODE (cur) == SSA_NAME) + { + unsigned wno = SSA_NAME_VERSION (cur); + if (bitmap_bit_p (osi->reexamine, wno)) + bitmap_clear_bit (osi->reexamine, wno); + else + cur = NULL_TREE; + } + else if (!is_gimple_variable (*wholesize) + && TREE_CODE (*wholesize) != INTEGER_CST) + cur = make_ssa_name (sizetype); + else + cur = NULL_TREE; + + if (cur) + { + res = size_binop (COMPOUND_EXPR, + size_binop (MODIFY_EXPR, cur, *wholesize), + res); + *wholesize = cur; + } + return res; +} + /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME. */ static tree -expr_object_size (struct object_size_info *osi, tree value) +expr_object_size (struct object_size_info *osi, tree value, tree *wholesize) { int object_size_type = osi->object_size_type; tree bytes; @@ -1153,14 +1251,17 @@ expr_object_size (struct object_size_info *osi, tree value) if (TREE_CODE (value) == WITH_SIZE_EXPR) value = TREE_OPERAND (value, 0); - /* Pointer variables should have been handled by ssa_object_size. */ + /* Pointer variables should have been handled by + collect_object_sizes_for. */ gcc_assert (TREE_CODE (value) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (value))); if (TREE_CODE (value) == ADDR_EXPR - && addr_object_size (osi, value, object_size_type, &bytes)) - return bytes; + && addr_object_size (osi, value, object_size_type, &bytes, wholesize)) + return bundle_whole_size (osi, value, bytes, wholesize); + if (wholesize) + *wholesize = size_unknown (object_size_type); return size_unknown (object_size_type); } @@ -1168,16 +1269,20 @@ expr_object_size (struct object_size_info *osi, tree value) /* Compute object_sizes for PTR, defined to the result of a call. */ static tree -call_object_size (struct object_size_info *osi, gcall *call) +call_object_size (struct object_size_info *osi, tree var, tree *wholesize) { int object_size_type = osi->object_size_type; + gcall *call = as_a (SSA_NAME_DEF_STMT (var)); gcc_assert (is_gimple_call (call)); tree bytes = alloc_object_size (call, object_size_type); + if (wholesize) + *wholesize = bytes; + if (size_known_p (bytes, object_size_type)) - return bytes; + return bundle_whole_size (osi, var, bytes, wholesize); return size_unknown (object_size_type); } @@ -1186,44 +1291,25 @@ call_object_size (struct object_size_info *osi, gcall *call) /* Compute object_sizes for PTR, defined to an unknown value. */ static tree -unknown_object_size (struct object_size_info *osi) +unknown_object_size (struct object_size_info *osi, tree *wholesize) { int object_size_type = osi->object_size_type; + if (wholesize) + *wholesize = size_unknown (object_size_type); return size_unknown (object_size_type); } -/* Return object size of ORIG + OFFSET. */ - -static tree -ssa_object_size (struct object_size_info *osi, tree orig, tree offset) -{ - int object_size_type = osi->object_size_type; - tree orig_bytes; - - if (compare_tree_int (offset, offset_limit) >= 0) - return size_unknown (object_size_type); - - collect_object_sizes_for (osi, orig); - - orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig)); - if (!size_unknown_p (orig_bytes, object_size_type) - && !integer_zerop (offset)) - orig_bytes = size_for_offset (orig_bytes, offset); - - return orig_bytes; -} - - /* Compute object_sizes for VAR, defined to the result of an assignment with operator POINTER_PLUS_EXPR. */ static tree -plus_stmt_object_size (struct object_size_info *osi, gimple *stmt) +plus_stmt_object_size (struct object_size_info *osi, tree var, tree *wholesize) { int object_size_type = osi->object_size_type; tree bytes; tree op0, op1; + gimple *stmt = SSA_NAME_DEF_STMT (var); if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) { @@ -1240,29 +1326,24 @@ plus_stmt_object_size (struct object_size_info *osi, gimple *stmt) else gcc_unreachable (); - /* Handle PTR + OFFSET here. */ - if (TREE_CODE (op1) == INTEGER_CST - && (TREE_CODE (op0) == SSA_NAME - || TREE_CODE (op0) == ADDR_EXPR)) - { - if (TREE_CODE (op0) == SSA_NAME) - bytes = ssa_object_size (osi, op0, op1); - else - { - /* op0 will be ADDR_EXPR here. */ - addr_object_size (osi, op0, object_size_type, &bytes); - if (size_unknown_p (bytes, object_size_type)) - ; - else if (compare_tree_int (op1, offset_limit) > 0) - bytes = size_unknown (object_size_type); - else if (!integer_zerop (op1)) - bytes = size_for_offset (bytes, op1); - } - } + if (!(object_size_type & OST_DYNAMIC) + && (TREE_CODE (op1) != INTEGER_CST + || compare_tree_int (op1, offset_limit) > 0)) + return size_unknown (object_size_type); + + if (TREE_CODE (op0) == SSA_NAME) + bytes = collect_object_sizes_for (osi, op0, wholesize); + else if (TREE_CODE (op0) == ADDR_EXPR) + addr_object_size (osi, op0, object_size_type, &bytes, wholesize); else bytes = size_unknown (object_size_type); - return bytes; + if (!size_unknown_p (bytes, object_size_type)) + bytes = size_for_offset (bytes, op1, wholesize ? *wholesize : NULL_TREE); + else if (wholesize) + *wholesize = size_unknown (object_size_type); + + return bundle_whole_size (osi, var, bytes, wholesize); } @@ -1270,34 +1351,48 @@ plus_stmt_object_size (struct object_size_info *osi, gimple *stmt) a COND_EXPR. */ static tree -cond_expr_object_size (struct object_size_info *osi, gimple *stmt) +cond_expr_object_size (struct object_size_info *osi, tree var, tree *wholesize) { tree then_, else_; int object_size_type = osi->object_size_type; - tree thenbytes, elsebytes; + tree thenbytes, elsebytes, thenwhole, elsewhole; + gimple *stmt = SSA_NAME_DEF_STMT (var); gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR); + if (!(object_size_type & OST_DYNAMIC)) + wholesize = NULL; + then_ = gimple_assign_rhs2 (stmt); else_ = gimple_assign_rhs3 (stmt); if (TREE_CODE (then_) == SSA_NAME) - thenbytes = ssa_object_size (osi, then_, size_int (0)); + thenbytes = collect_object_sizes_for (osi, then_, wholesize); else - thenbytes = expr_object_size (osi, then_); + thenbytes = expr_object_size (osi, then_, wholesize); + + thenwhole = wholesize ? *wholesize : NULL_TREE; if (TREE_CODE (else_) == SSA_NAME) - elsebytes = ssa_object_size (osi, else_, size_int (0)); + elsebytes = collect_object_sizes_for (osi, else_, wholesize); else - elsebytes = expr_object_size (osi, else_); + elsebytes = expr_object_size (osi, else_, wholesize); + + elsewhole = wholesize ? *wholesize : NULL_TREE; if (size_unknown_p (thenbytes, object_size_type) || size_unknown_p (elsebytes, object_size_type)) return size_unknown (object_size_type); if (object_size_type & OST_DYNAMIC) - return fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt), - thenbytes, elsebytes); + { + gcc_checking_assert (elsewhole && thenwhole); + *wholesize = fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt), + thenwhole, elsewhole); + tree res = fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt), + thenbytes, elsebytes); + return bundle_whole_size (osi, var, res, wholesize); + } return size_binop (OST_TREE_CODE (object_size_type), thenbytes, elsebytes); } @@ -1316,9 +1411,9 @@ phi_object_size (struct object_size_info *osi, gimple *stmt) tree phires; if (TREE_CODE (rhs) == SSA_NAME) - phires = ssa_object_size (osi, rhs, size_int (0)); + phires = collect_object_sizes_for (osi, rhs); else - phires = expr_object_size (osi, rhs); + phires = expr_object_size (osi, rhs, NULL); res = size_binop (OST_TREE_CODE (object_size_type), res, phires); @@ -1329,42 +1424,69 @@ phi_object_size (struct object_size_info *osi, gimple *stmt) } static tree -phi_dynamic_object_size (struct object_size_info *osi, tree var) +phi_dynamic_object_size (struct object_size_info *osi, tree var, + tree *wholesize) { 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); - tree res; + tree res, wholeres; - vec sizes; + vec sizes, wholesizes; sizes.create (0); + wholesizes.create (0); sizes.safe_grow (num_args); + wholesizes.safe_grow (num_args); /* Bail out if the size of any of the PHI arguments cannot be determined. */ for (i = 0; i < num_args; i++) { tree rhs = gimple_phi_arg_def (stmt, i); - tree sz; + tree sz, cur_whole; if (TREE_CODE (rhs) != SSA_NAME) - sz = expr_object_size (osi, rhs); + sz = expr_object_size (osi, rhs, &cur_whole); else - sz = ssa_object_size (osi, rhs, size_int (0)); + sz = collect_object_sizes_for (osi, rhs, &cur_whole); - if (size_unknown_p (sz, object_size_type)) + if (size_unknown_p (sz, object_size_type) + || size_unknown_p (cur_whole, object_size_type)) break; + if (!is_gimple_variable (cur_whole) + && TREE_CODE (cur_whole) != INTEGER_CST) + { + tree tmp = make_ssa_name (sizetype); + tmp = size_binop (MODIFY_EXPR, tmp, cur_whole); + sz = size_binop (COMPOUND_EXPR, tmp, sz); + cur_whole = tmp; + } sizes[i] = sz; + wholesizes[i] = cur_whole; } if (i == num_args) { res = make_or_get_tempsize (osi, varno); + wholeres = object_sizes_get (osi, varno, true); + if (TREE_CODE (wholeres) != SSA_NAME) + { + gcc_checking_assert (TREE_CODE (wholeres) == INTEGER_CST); + gcc_checking_assert (compare_tree_int (wholeres, + initval (object_size_type)) + == 0); + wholeres = make_ssa_name (sizetype); + } + else + /* We don't reexamine whole sizes on their own. */ + bitmap_clear_bit (osi->reexamine, SSA_NAME_VERSION (wholeres)); + bitmap_set_bit (osi->phiresults, SSA_NAME_VERSION (res)); object_sizes_initialize (osi, SSA_NAME_VERSION (res), res); + gphi *wholephi = create_phi_node (wholeres, gimple_bb (stmt)); gphi *phi = create_phi_node (res, gimple_bb (stmt)); gphi *obj_phi = as_a (stmt); @@ -1377,6 +1499,11 @@ phi_dynamic_object_size (struct object_size_info *osi, tree var) sizes[i] = ssa; } + /* We're guaranteed to get gimple variables for whole sizes through + expr_object_size and collect_object_sizes_for. */ + 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)); @@ -1387,20 +1514,26 @@ phi_dynamic_object_size (struct object_size_info *osi, tree var) print_generic_expr (dump_file, var, dump_flags); fprintf (dump_file, ": PHI Node with result: "); print_gimple_stmt (dump_file, phi, dump_flags); + fprintf (dump_file, " and wholephi: "); + print_gimple_stmt (dump_file, wholephi, dump_flags); } + *wholesize = wholeres; } else - res = size_unknown (object_size_type); + { + res = size_unknown (object_size_type); + *wholesize = size_unknown (object_size_type); + } sizes.release (); - + wholesizes.release (); return res; } /* Find size of an object passed as a parameter to the function. */ static tree -parm_object_size (struct object_size_info *osi, tree var) +parm_object_size (struct object_size_info *osi, tree var, tree *wholesize) { int object_size_type = osi->object_size_type; tree parm = SSA_NAME_VAR (var); @@ -1414,7 +1547,7 @@ parm_object_size (struct object_size_info *osi, tree var) } if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm))) - return expr_object_size (osi, parm); + return expr_object_size (osi, parm, wholesize); /* Look for access attribute. */ rdwr_map rdwr_idx; @@ -1422,6 +1555,7 @@ parm_object_size (struct object_size_info *osi, tree var) tree fndecl = cfun->decl; const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl); tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm))); + tree sz = NULL_TREE; if (access && access->sizarg != UINT_MAX) { @@ -1436,17 +1570,21 @@ parm_object_size (struct object_size_info *osi, tree var) if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg))) { - tree sz = get_or_create_ssa_default_def (cfun, arg); + sz = get_or_create_ssa_default_def (cfun, arg); if (sz != NULL_TREE) { sz = fold_convert (sizetype, sz); if (typesize) sz = size_binop (MULT_EXPR, sz, typesize); - return sz; } } } - return size_unknown (object_size_type); + if (sz == NULL_TREE) + sz = size_unknown (object_size_type); + + if (wholesize) + *wholesize = sz; + return bundle_whole_size (osi, var, sz, wholesize); } /* Compute object sizes for VAR. @@ -1469,23 +1607,32 @@ parm_object_size (struct object_size_info *osi, tree var) Otherwise, object size is the maximum of object sizes of variables that it might be set to. */ -static void -collect_object_sizes_for (struct object_size_info *osi, tree var) +static tree +collect_object_sizes_for (struct object_size_info *osi, tree var, + tree *wholesize) { int object_size_type = osi->object_size_type; unsigned int varno = SSA_NAME_VERSION (var); + tree res, wholeres = NULL_TREE; gimple *stmt; - tree res; + + if ((object_size_type & OST_DYNAMIC) && !wholesize) + wholesize = &wholeres; if (bitmap_bit_p (computed[object_size_type], varno)) - return; + { + if (wholesize) + *wholesize = object_sizes_get (osi, varno, true); + return object_sizes_get (osi, varno); + } /* We want to evaluate the self-referencing object only once. */ if (bitmap_set_bit (osi->visited, varno)) { /* Initialize to 0 for maximum size and M1U for minimum size so that it gets immediately overridden. */ - object_sizes_initialize (osi, varno); + object_sizes_initialize (osi, varno, size_initval (object_size_type), + object_size_type & OST_DYNAMIC); } else { @@ -1497,6 +1644,19 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) fprintf (dump_file, "\n"); } res = make_or_get_tempsize (osi, varno); + if (wholesize) + { + tree tmp = object_sizes_get (osi, varno, true); + if (TREE_CODE (tmp) == INTEGER_CST) + *wholesize = make_tempsize (osi, varno); + else + { + gcc_assert (TREE_CODE (res) == SSA_NAME + && bitmap_bit_p (osi->reexamine, + SSA_NAME_VERSION (tmp))); + *wholesize = tmp; + } + } goto out; } @@ -1517,58 +1677,57 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR || (gimple_assign_rhs_code (stmt) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF)) - res = plus_stmt_object_size (osi, stmt); + res = plus_stmt_object_size (osi, var, wholesize); else if (gimple_assign_rhs_code (stmt) == COND_EXPR) - res = cond_expr_object_size (osi, stmt); + res = cond_expr_object_size (osi, var, wholesize); 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))) - res = ssa_object_size (osi, rhs, size_int (0)); + res = collect_object_sizes_for (osi, rhs, wholesize); else - res = expr_object_size (osi, rhs); + res = expr_object_size (osi, rhs, wholesize); } else - res = unknown_object_size (osi); + res = unknown_object_size (osi, wholesize); break; } case GIMPLE_CALL: { - gcall *call_stmt = as_a (stmt); - tree arg = pass_through_call (call_stmt); + tree arg = pass_through_call (as_a (stmt)); if (arg) { if (TREE_CODE (arg) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (arg))) - res = ssa_object_size (osi, arg, size_int (0)); + res = collect_object_sizes_for (osi, arg, wholesize); else - res = expr_object_size (osi, arg); + res = expr_object_size (osi, arg, wholesize); } else - res = call_object_size (osi, call_stmt); + res = call_object_size (osi, var, wholesize); break; } case GIMPLE_ASM: /* Pointers defined by __asm__ statements can point anywhere. */ - res = size_unknown (object_size_type); + res = unknown_object_size (osi, wholesize); break; case GIMPLE_NOP: if (SSA_NAME_VAR (var) && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL) - res = parm_object_size (osi, var); + res = parm_object_size (osi, var, wholesize); else /* Uninitialized SSA names point nowhere. */ - res = size_unknown (object_size_type); + res = unknown_object_size (osi, wholesize); break; case GIMPLE_PHI: { if (object_size_type & OST_DYNAMIC) - res = phi_dynamic_object_size (osi, var); + res = phi_dynamic_object_size (osi, var, wholesize); else res = phi_object_size (osi, stmt); break; @@ -1578,16 +1737,26 @@ collect_object_sizes_for (struct object_size_info *osi, tree var) gcc_unreachable (); } - if ((object_size_type & OST_DYNAMIC) - && TREE_CODE (res) != INTEGER_CST && !is_gimple_variable (res)) + res = bundle_whole_size (osi, var, res, wholesize); + + if (wholesize) + gcc_checking_assert (TREE_CODE (*wholesize) == INTEGER_CST + || is_gimple_variable (*wholesize)); + + if ((object_size_type & OST_DYNAMIC) && TREE_CODE (res) != INTEGER_CST + && !is_gimple_variable (res)) { tree ssa = make_or_get_tempsize (osi, varno); object_sizes_initialize (osi, SSA_NAME_VERSION (ssa), res); res = ssa; } + bitmap_set_bit (computed[object_size_type], varno); out: object_sizes_set (osi, varno, res); + if (object_size_type & OST_DYNAMIC) + object_sizes_set (osi, varno, *wholesize, true); + return res; } @@ -1603,7 +1772,7 @@ init_object_sizes (void) for (object_size_type = 0; object_size_type < OST_END; object_size_type++) { - object_sizes_grow (object_size_type); + object_sizes_grow (object_size_type, object_size_type >= OST_DYNAMIC); computed[object_size_type] = BITMAP_ALLOC (NULL); } @@ -1620,7 +1789,7 @@ fini_object_sizes (void) for (object_size_type = 0; object_size_type < OST_END; object_size_type++) { - object_sizes_release (object_size_type); + object_sizes_release (object_size_type, object_size_type >= OST_DYNAMIC); BITMAP_FREE (computed[object_size_type]); } }