@@ -1380,6 +1380,7 @@ OBJS = \
trans-mem.o \
tree-affine.o \
asan.o \
+ ipa-nonfree.o \
tsan.o \
ubsan.o \
sanopt.o \
@@ -2322,6 +2323,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
$(srcdir)/ipa-inline.h \
$(srcdir)/vtable-verify.c \
$(srcdir)/asan.c \
+ $(srcdir)/ipa-nonfree.c \
$(srcdir)/ubsan.c \
$(srcdir)/tsan.c \
$(srcdir)/sanopt.c \
@@ -2539,7 +2539,8 @@ nonfreeing_call_p (gimple call)
return true;
}
- return false;
+ tree decl = gimple_call_fndecl (call);
+ return decl && lookup_attribute ("nonfreeing", DECL_ATTRIBUTES (decl));
}
/* Callback for walk_stmt_load_store_ops.
new file mode 100644
@@ -0,0 +1,206 @@
+/* Nonfreeing functions discovering decision heuristics.
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* For each defined function in compiling file, nonfree pass tries to
+ understand if this function is nonfreeing (does not free any allocated
+ memory). Each such function is marked with "nonfreeing" attribute, which
+ existence will be checked in nonfreeing_call_p function later. All external
+ routines and local, calling some external ones, are assumed to not be
+ nofreeing. Functions having indirect calls are also nofreeing. Function
+ is nonfreeing if and only if it calls only nonfreeing functions. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "is-a.h"
+#include "hash-map.h"
+#include "plugin-api.h"
+#include "ipa-ref.h"
+#include "cgraph.h"
+#include "stringpool.h"
+#include "tree-pass.h"
+
+/* This enum describes three possible statuses of functions in terms of its to
+ be nonfreeing. Value STATUS_UNKNOWN means that it is not determined for
+ given function whether it is nonfreeing or not. Value STATUS_NONFREEING
+ means that it is nonfreeing and STATUS_FREEING - that it is definitely
+ freeing. */
+
+enum freeing_status {
+ STATUS_UNKNOWN,
+ STATUS_NONFREEING,
+ STATUS_FREEING
+};
+
+/* This vector contains cached information about each node in cgraph related to
+ freeing property. */
+
+static vec<enum freeing_status> freeing_status_cached;
+
+/* Returns true if function can be interposed by linker (static or dynamic). */
+
+static inline bool
+is_interposable (struct symtab_node *node)
+{
+ return node->get_availability () <= AVAIL_INTERPOSABLE
+ || node->can_be_discarded_p ();
+}
+
+/* Returns current freeing status of function NODE. */
+
+static enum freeing_status
+get_freeing_status (struct cgraph_node *node)
+{
+ static bool inited;
+
+ static const char *freeing_fn_names[] = {
+ "free",
+ "realloc",
+ "_ITM_free",
+ "__builtin_free",
+ "__builtin_realloc",
+ "__builtin_stack_restore",
+ "operator delete",
+ "operator delete[]"
+ };
+
+ static tree freeing_fn_idents[ARRAY_SIZE (freeing_fn_names)];
+ if (!inited)
+ {
+ for (size_t i = 0; i < ARRAY_SIZE (freeing_fn_idents); ++i)
+ freeing_fn_idents[i] = get_identifier (freeing_fn_names[i]);
+
+ inited = true;
+ }
+
+ if (freeing_status_cached[node->uid] != STATUS_UNKNOWN)
+ return freeing_status_cached[node->uid];
+
+ if ((!node->has_gimple_body_p () && !node->alias)
+ || node->cpp_implicit_alias
+ || node->indirect_calls
+ || is_interposable (node))
+ return freeing_status_cached[node->uid] = STATUS_FREEING;
+
+ const tree decl = node->decl;
+ for (size_t i = 0; i < ARRAY_SIZE (freeing_fn_idents); ++i)
+ if (DECL_NAME (decl) == freeing_fn_idents[i])
+ return freeing_status_cached[node->uid] = STATUS_FREEING;
+
+ if (DECL_IS_BUILTIN (decl))
+ return freeing_status_cached[node->uid] = STATUS_NONFREEING;
+
+ return STATUS_UNKNOWN;
+}
+
+/* This function marks defined functions as
+ nonfreeing if this can be proven. */
+
+static int
+find_nonfreeing (void)
+{
+ if (!symtab->cgraph_max_uid)
+ return 0;
+
+ freeing_status_cached.safe_grow_cleared (symtab->cgraph_max_uid);
+
+ bool changed;
+
+ do
+ {
+ changed = false;
+ struct cgraph_node *node;
+
+ FOR_EACH_DEFINED_FUNCTION (node)
+ {
+ if (get_freeing_status (node) != STATUS_UNKNOWN)
+ continue;
+
+ struct cgraph_edge *edge;
+ freeing_status status = STATUS_NONFREEING;
+ for (edge = node->ultimate_alias_target ()->callees;
+ edge && (node == edge->callee || status == STATUS_NONFREEING);
+ edge = edge->next_callee)
+ if (node != edge->callee)
+ status = get_freeing_status (edge->callee);
+
+ if (status != STATUS_UNKNOWN)
+ {
+ if (status == STATUS_NONFREEING)
+ DECL_ATTRIBUTES (node->decl)
+ = tree_cons (get_identifier ("nonfreeing"), NULL,
+ DECL_ATTRIBUTES (node->decl));
+ freeing_status_cached[node->uid] = status;
+ changed = true;
+ }
+ }
+ }
+ while (changed);
+
+ if (dump_file)
+ {
+ struct cgraph_node *node;
+ FOR_EACH_DEFINED_FUNCTION (node)
+ fprintf (dump_file, "Function %s is %sfreeing\n", fndecl_name (node->decl),
+ freeing_status_cached[node->uid] == STATUS_NONFREEING ? "non" : "");
+ }
+
+ freeing_status_cached.release ();
+
+ return 0;
+}
+
+namespace {
+
+const pass_data pass_data_nonfree =
+{
+ SIMPLE_IPA_PASS, /* type */
+ "nonfree", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_nonfree : public simple_ipa_opt_pass
+{
+public:
+ pass_nonfree (gcc::context *ctxt)
+ : simple_ipa_opt_pass (pass_data_nonfree, ctxt)
+ {}
+
+ /* nonfree_pass methods: */
+ virtual unsigned int execute (function *) { return find_nonfreeing (); }
+
+}; // class pass_nonfree
+
+} // anon namespace
+
+simple_ipa_opt_pass *
+make_pass_nonfree (gcc::context *ctxt)
+{
+ return new pass_nonfree (ctxt);
+}
@@ -136,6 +136,7 @@ along with GCC; see the file COPYING3. If not see
passes are executed after partitioning and thus see just parts of the
compiled unit. */
INSERT_PASSES_AFTER (all_late_ipa_passes)
+ NEXT_PASS (pass_nonfree);
NEXT_PASS (pass_ipa_pta);
NEXT_PASS (pass_omp_simd_clone);
TERMINATE_PASS_LIST ()
@@ -459,6 +459,7 @@ extern simple_ipa_opt_pass *make_pass_build_ssa_passes (gcc::context *ctxt);
extern simple_ipa_opt_pass *make_pass_chkp_instrumentation_passes (gcc::context *ctxt);
extern simple_ipa_opt_pass *make_pass_local_optimization_passes (gcc::context *ctxt);
+extern simple_ipa_opt_pass *make_pass_nonfree (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_whole_program_visibility (gcc::context
*ctxt);
extern simple_ipa_opt_pass *make_pass_ipa_increase_alignment (gcc::context