From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from nikam.ms.mff.cuni.cz (nikam.ms.mff.cuni.cz [195.113.20.16]) by sourceware.org (Postfix) with ESMTPS id A767B3858D20 for ; Sun, 19 Nov 2023 01:21:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A767B3858D20 Authentication-Results: sourceware.org; dmarc=fail (p=none dis=none) header.from=ucw.cz Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kam.mff.cuni.cz ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A767B3858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=195.113.20.16 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700356890; cv=none; b=mxvFzPzroLi8Z1Us7be81l0EgWznk2JSFD5mI4JdwY3O4DGCoQ8fJEAq+qXsbsbVTc7gl4dEp68BRpXMFy59tLyyaPlS5cQVlFfDBNTvpOXu2Dv/42AVdUd2kWiMtKaSRSa0rYYcJgg2ZB0aDKWf0jrIyHztGIut1FhRbdg9nxA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700356890; c=relaxed/simple; bh=pO4daEae/zlR7VlaQqhctBl9JNGuU6p1G722ZObX28U=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=NJOaicAD0SINy9dvxnESRaK4KOOm1sc64ejKOCbC5dn42+d5uhXDOmH5kzJ439d1I9E3wL0tRd0U8dq0XDg9sGLbpEQJjbfyf23/KTRSG82icZUisA0diyXxKrgM2+Q1cyl1pJW9FJsUJT8a/62+8YxbdwuAJztoVZP4C1m+rCo= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by nikam.ms.mff.cuni.cz (Postfix, from userid 16202) id DB6AD28B7EB; Sun, 19 Nov 2023 02:21:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ucw.cz; s=gen1; t=1700356881; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=Y76lp86bEF659CyKALrVGqCzGBCaPLdt3bl3wp72pYQ=; b=hBhYOVvAVeZd9wWYhhPfl5e4hwncpLudugfGIYXBZ+4DTdOURj84wv5at6BjUu9pdrVLYA UxGOXCzR3IQdwEavtYYu4+e0Q0aPztCSLidfGmwLyFEFjQhqRHFkUKwy8TbYauL2v0LnTM iVHrphdRB+MBH5TWjlTkft6pT6nZIN4= Date: Sun, 19 Nov 2023 02:21:21 +0100 From: Jan Hubicka To: gcc-patches@gcc.gnu.org Subject: Propagate value ranges of return values Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,GIT_PATCH_0,HEADER_FROM_DIFFERENT_DOMAINS,JMQ_SPF_NEUTRAL,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi, this patch implements very basic propaation of return value ranges from VRP pass. This helps std::vector's push_back since we work out value range of allocated block. This propagates only within single translation unit. I hoped we will also do the propagation at WPA stage, but that needs more work on ipa-cp side. I also added code auto-detecting return_nonnull and corresponding -Wsuggest-attribute Variant of this patch bootstrapped/regtested x86_64-linux, testing with this version is running. I plan to commit the patch at Monday provided there are no issues. gcc/ChangeLog: * cgraph.cc (add_detected_attribute_1): New function. (cgraph_node::add_detected_attribute): New member function. * cgraph.h (struct cgraph_node): Declare it. * common.opt: Add Wsuggest-attribute=returns_nonnull. * doc/invoke.texi: Document +Wsuggest-attribute=returns_nonnull. * gimple-range-fold.cc: Include ipa-prop and dependencies. (fold_using_range::range_of_call): Look for return value range. * ipa-prop.cc (struct ipa_return_value_summary): New structure. (class ipa_return_value_sum_t): New summary. (ipa_record_return_value_range): New function. (ipa_return_value_range): New function. * ipa-prop.h (ipa_return_value_range): Declare. (ipa_record_return_value_range): Declare. * ipa-pure-const.cc (warn_function_returns_nonnull): New function. * ipa-utils.h (warn_function_returns_nonnull): Declare. * symbol-summary.h: Fix comment typo. * tree-vrp.cc (execute_ranger_vrp): Record return values. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/return-value-range-1.c: New test. diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index e41e5ad3ae7..71dacf23ce1 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -2629,6 +2629,54 @@ cgraph_node::set_malloc_flag (bool malloc_p) return changed; } +/* Worker to set malloc flag. */ +static void +add_detected_attribute_1 (cgraph_node *node, const char *attr, bool *changed) +{ + if (!lookup_attribute (attr, DECL_ATTRIBUTES (node->decl))) + { + DECL_ATTRIBUTES (node->decl) = tree_cons (get_identifier (attr), + NULL_TREE, DECL_ATTRIBUTES (node->decl)); + *changed = true; + } + + ipa_ref *ref; + FOR_EACH_ALIAS (node, ref) + { + cgraph_node *alias = dyn_cast (ref->referring); + if (alias->get_availability () > AVAIL_INTERPOSABLE) + add_detected_attribute_1 (alias, attr, changed); + } + + for (cgraph_edge *e = node->callers; e; e = e->next_caller) + if (e->caller->thunk + && (e->caller->get_availability () > AVAIL_INTERPOSABLE)) + add_detected_attribute_1 (e->caller, attr, changed); +} + +/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any. */ + +bool +cgraph_node::add_detected_attribute (const char *attr) +{ + bool changed = false; + + if (get_availability () > AVAIL_INTERPOSABLE) + add_detected_attribute_1 (this, attr, &changed); + else + { + ipa_ref *ref; + + FOR_EACH_ALIAS (this, ref) + { + cgraph_node *alias = dyn_cast (ref->referring); + if (alias->get_availability () > AVAIL_INTERPOSABLE) + add_detected_attribute_1 (alias, attr, &changed); + } + } + return changed; +} + /* Worker to set noreturng flag. */ static void set_noreturn_flag_1 (cgraph_node *node, bool noreturn_p, bool *changed) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index cedaaac3a45..cfdd9f693a8 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1190,6 +1190,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node bool set_pure_flag (bool pure, bool looping); + /* Add attribute ATTR to cgraph_node's decl and on aliases of the node + if any. */ + bool add_detected_attribute (const char *attr); + /* Call callback on function and aliases associated to the function. When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are skipped. */ diff --git a/gcc/common.opt b/gcc/common.opt index d21db5d4a20..0be4f02677c 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -781,6 +781,10 @@ Wsuggest-attribute=malloc Common Var(warn_suggest_attribute_malloc) Warning Warn about functions which might be candidates for __attribute__((malloc)). +Wsuggest-attribute=returns_nonnull +Common Var(warn_suggest_attribute_malloc) Warning +Warn about functions which might be candidates for __attribute__((malloc)). + Wsuggest-final-types Common Var(warn_suggest_final_types) Warning Warn about C++ polymorphic types where adding final keyword would improve code quality. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 557d613a1e6..b9e98843613 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -8092,7 +8092,7 @@ if the array is referenced as a flexible array member. @opindex Wsuggest-attribute= @opindex Wno-suggest-attribute= -@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]} +@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}returns_nonnull@r{|} Warn for cases where adding an attribute may be beneficial. The attributes currently supported are listed below. @@ -8112,9 +8112,11 @@ attributes currently supported are listed below. @itemx -Wsuggest-attribute=noreturn @itemx -Wmissing-noreturn @itemx -Wsuggest-attribute=malloc +@itemx -Wsuggest-attribute=returns_nonnull +@itemx -Wno-suggest-attribute=returns_nonnull Warn about functions that might be candidates for attributes -@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler +@code{pure}, @code{const}, @code{noreturn}, @code{malloc} or @code{returns_nonnull}. The compiler only warns for functions visible in other compilation units or (in the case of @code{pure} and @code{const}) if it cannot prove that the function returns normally. A function returns normally if it doesn't contain an infinite loop or diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 6e9530c3d7f..0af3fa1b008 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -44,6 +44,11 @@ along with GCC; see the file COPYING3. If not see #include "value-query.h" #include "gimple-range-op.h" #include "gimple-range.h" +#include "cgraph.h" +#include "alloc-pool.h" +#include "symbol-summary.h" +#include "ipa-utils.h" +#include "ipa-prop.h" // Construct a fur_source, and set the m_query field. fur_source::fur_source (range_query *q) @@ -1013,6 +1018,24 @@ fold_using_range::range_of_call (vrange &r, gcall *call, fur_source &) else r.set_varying (type); + tree callee = gimple_call_fndecl (call); + if (callee) + { + Value_Range val; + if (ipa_return_value_range (val, callee)) + { + r.intersect (val); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Using return value range of "); + print_generic_expr (dump_file, callee, TDF_SLIM); + fprintf (dump_file, ": "); + val.dump (dump_file); + fprintf (dump_file, "\n"); + } + } + } + // If there is an LHS, intersect that with what is known. if (lhs) { diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index 7de2b788185..e77bc9c340b 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -237,6 +237,35 @@ gt_ggc_mx (ipa_vr *&x) return gt_ggc_mx ((ipa_vr *) x); } +/* Analysis summery of function call return value. */ +struct GTY(()) ipa_return_value_summary +{ + /* Known value range. + This needs to be wrapped in struccture due to specific way + we allocate ipa_vr. */ + ipa_vr *vr; +}; + +/* Function summary for return values. */ +class ipa_return_value_sum_t : public function_summary +{ +public: + ipa_return_value_sum_t (symbol_table *table, bool ggc): + function_summary (table, ggc) { } + + /* Hook that is called by summary when a node is duplicated. */ + void duplicate (cgraph_node *, + cgraph_node *, + ipa_return_value_summary *data, + ipa_return_value_summary *data2) final override + { + *data2=*data; + } +}; + +/* Variable hoding the return value summary. */ +static GTY(()) function_summary *ipa_return_value_sum; + /* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated with NODE should prevent us from analyzing it for the purposes of IPA-CP. */ @@ -5915,5 +5944,49 @@ ipcp_transform_function (struct cgraph_node *node) return modified_mem_access ? TODO_update_ssa_only_virtuals : 0; } +/* Record that current function return value range is VAL. */ + +void +ipa_record_return_value_range (Value_Range val) +{ + cgraph_node *n = cgraph_node::get (current_function_decl); + if (!ipa_return_value_sum) + { + if (!ipa_vr_hash_table) + ipa_vr_hash_table = hash_table::create_ggc (37); + ipa_return_value_sum = new (ggc_alloc_no_dtor ()) + ipa_return_value_sum_t (symtab, true); + ipa_return_value_sum->disable_insertion_hook (); + } + ipa_return_value_sum->get_create (n)->vr = ipa_get_value_range (val); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Recording return range "); + val.dump (dump_file); + fprintf (dump_file, "\n"); + } +} + +/* Return true if value range of DECL is known and if so initialize RANGE. */ + +bool +ipa_return_value_range (Value_Range &range, tree decl) +{ + cgraph_node *n = cgraph_node::get (decl); + if (!n || !ipa_return_value_sum) + return false; + enum availability avail; + n = n->ultimate_alias_target (&avail); + if (avail < AVAIL_AVAILABLE) + return false; + if (n->decl != decl && !useless_type_conversion_p (TREE_TYPE (decl), TREE_TYPE (n->decl))) + return false; + ipa_return_value_summary *v = ipa_return_value_sum->get (n); + if (!v) + return false; + v->vr->get_vrange (range); + return true; +} + #include "gt-ipa-prop.h" diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index fcd0e5c638f..5901c805c40 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -309,7 +309,7 @@ public: void get_vrange (Value_Range &) const; bool equal_p (const vrange &) const; const vrange_storage *storage () const { return m_storage; } - void streamer_read (lto_input_block *, data_in *); + void streamer_read (lto_input_block *, class data_in *); void streamer_write (output_block *) const; void dump (FILE *) const; @@ -1274,4 +1274,7 @@ ipa_range_set_and_normalize (vrange &r, tree val) r.set (val, val); } +bool ipa_return_value_range (Value_Range &range, tree decl); +void ipa_record_return_value_range (Value_Range val); + #endif /* IPA_PROP_H */ diff --git a/gcc/ipa-pure-const.cc b/gcc/ipa-pure-const.cc index 058a7dd3019..3060ffeefcd 100644 --- a/gcc/ipa-pure-const.cc +++ b/gcc/ipa-pure-const.cc @@ -292,6 +292,15 @@ warn_function_cold (tree decl) true, warned_about, "cold"); } +void +warn_function_returns_nonnull (tree decl) +{ + static hash_set *warned_about; + warned_about + = suggest_attribute (OPT_Wsuggest_attribute_returns_nonnull, decl, + true, warned_about, "returns_nonnull"); +} + /* Check to see if the use (or definition when CHECKING_WRITE is true) variable T is legal in a function that is either pure or const. */ diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index 0eefcf40d44..84728c589ea 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -105,6 +105,7 @@ tree prevailing_odr_type (tree type); void enable_odr_based_tbaa (tree type); bool odr_based_tbaa_p (const_tree type); void set_type_canonical_for_odr_type (tree type, tree canonical); +void warn_function_returns_nonnull (tree); void register_odr_enum (tree type); diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h index 3ed61627382..5fd49a2552e 100644 --- a/gcc/symbol-summary.h +++ b/gcc/symbol-summary.h @@ -71,7 +71,7 @@ public: = m_symtab->add_cgraph_insertion_hook (m_symtab_insertion, this); } - /* Enable insertion hook invocation. */ + /* Disable insertion hook invocation. */ void disable_insertion_hook () { if (m_symtab_insertion_hook != NULL) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c new file mode 100644 index 00000000000..4db52233c5d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/return-value-range-1.c @@ -0,0 +1,22 @@ +/* { dg-do ling } */ +/* { dg-options "-O1 -dump-tree-evrp-details" } */ +__attribute__ ((__noinline__)) +int a(char c) +{ + return c; +} +void link_error (); + +void +test(int d) +{ + if (a(d) > 200) + link_error (); +} +int +main(int argc, char **argv) +{ + test(argc); + return 0; +} +/* { dg-final { scan-tree-dump-times "Recording return range" 2 "evrp"} } */ diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc index 917fa873714..11a382170d8 100644 --- a/gcc/tree-vrp.cc +++ b/gcc/tree-vrp.cc @@ -52,6 +52,11 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "tree-dfa.h" #include "tree-ssa-dce.h" +#include "alloc-pool.h" +#include "cgraph.h" +#include "symbol-summary.h" +#include "ipa-utils.h" +#include "ipa-prop.h" // This class is utilized by VRP and ranger to remove __builtin_unreachable // calls, and reflect any resulting global ranges. @@ -1081,6 +1086,47 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p, array_checker.check (); } + + if (Value_Range::supports_type_p (TREE_TYPE (TREE_TYPE (current_function_decl)))) + { + edge e; + edge_iterator ei; + bool found = false; + Value_Range return_range (TREE_TYPE (TREE_TYPE (current_function_decl))); + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + if (greturn *ret = dyn_cast (*gsi_last_bb (e->src))) + { + tree retval = gimple_return_retval (ret); + if (!retval) + { + return_range.set_varying (TREE_TYPE (TREE_TYPE (current_function_decl))); + found = true; + continue; + } + Value_Range r (TREE_TYPE (retval)); + if (ranger->range_of_expr (r, retval, ret) + && !r.undefined_p () + && !r.varying_p ()) + { + if (!found) + return_range = r; + else + return_range.union_ (r); + } + else + return_range.set_varying (TREE_TYPE (retval)); + found = true; + } + if (found && !return_range.varying_p ()) + { + ipa_record_return_value_range (return_range); + if (POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))) + && return_range.nonzero_p () + && cgraph_node::get (current_function_decl)->add_detected_attribute ("returns_nonnull")) + warn_function_returns_nonnull (current_function_decl); + } + } + phi_analysis_finalize (); disable_ranger (fun); scev_finalize ();