summaryrefslogtreecommitdiff
path: root/gcc/ipa-type-escape-analysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ipa-type-escape-analysis.c')
-rw-r--r--gcc/ipa-type-escape-analysis.c1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c
new file mode 100644
index 00000000000..f4db0b6183f
--- /dev/null
+++ b/gcc/ipa-type-escape-analysis.c
@@ -0,0 +1,1029 @@
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "options.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "predict.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "symbol-summary.h"
+#include "tree-vrp.h"
+#include "ipa-prop.h"
+#include "tree-pretty-print.h"
+#include "tree-cfg.h"
+#include "gimple-pretty-print.h"
+#include "tree-inline.h"
+#include "ipa-fnsummary.h"
+#include "ipa-utils.h"
+#include "tree-ssa-ccp.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "gimple.h"
+#include "cfg.h" // needed for gimple-iterator.h
+#include "gimple-iterator.h"
+#include <stdbool.h>
+
+#include "ipa-str-reorg-utils.h"
+#include "ipa-type-escape-analysis.h"
+
+
+// First we need to collect all types
+
+static bool filter_type (type_map &escape_map, const_tree type);
+
+void update_escape_info (const_tree type, type_map &escape_map, bool is_escaping, escaping_reason reason);
+
+static const escaping_reason null_reason = { 0, 0, 0, 0 };
+inline escaping_reason
+new_escaping_reason()
+{
+ return null_reason;
+}
+
+//TODO:
+// Should we be using C++?
+escaping_reason
+union_op(const escaping_reason &left, const escaping_reason &right)
+{
+ escaping_reason retval;
+ retval.global_is_visible = left.global_is_visible | right.global_is_visible;
+ retval.parameter_is_visible = left.parameter_is_visible | right.parameter_is_visible;
+ retval.return_is_visible = left.return_is_visible | right.return_is_visible;;
+ retval.type_is_casted = left.type_is_casted | right.type_is_casted;;
+ return retval;
+}
+
+void
+explain_print(escaping_reason reason)
+{
+ if (reason.global_is_visible) log("global is visible");
+ if (reason.parameter_is_visible) log(", parameter is visible");
+ if (reason.return_is_visible) log(", return is visible");
+ if (reason.type_is_casted) log(", type is casted");
+ log("\n");
+}
+
+void
+update_escape_info_pointer (const_tree pointer_type, type_map &escape_map, bool is_escaping, escaping_reason reason)
+{
+ gcc_assert(pointer_type);
+ enum tree_code tree_code_pointer_type = TREE_CODE(pointer_type);
+ bool is_pointer_type = POINTER_TYPE == tree_code_pointer_type;
+ gcc_assert(is_pointer_type);
+
+ escaping_info *info = escape_map.get(pointer_type);
+ if (!info) { log("no info for %s\n", get_type_name(pointer_type)); return;}
+
+ info->is_escaping |= is_escaping;
+ info->reason = union_op(info->reason, reason);
+ tree tree_type_pointer_type = TREE_TYPE(pointer_type);
+ gcc_assert(tree_type_pointer_type);
+ update_escape_info (tree_type_pointer_type, escape_map, info->is_escaping, info->reason);
+}
+
+void
+update_escape_info_array (const_tree array_type, type_map &escape_map, bool is_escaping, escaping_reason reason)
+{
+ gcc_assert(array_type);
+ enum tree_code tree_code_array_type = TREE_CODE(array_type);
+ bool is_array_type = ARRAY_TYPE == tree_code_array_type;
+ gcc_assert(is_array_type);
+
+ escaping_info *info = escape_map.get(array_type);
+ if (!info) { log("no info for %s\n", get_type_name(array_type)); return;}
+
+ info->is_escaping |= is_escaping;
+ info->reason = union_op(info->reason, reason);
+ tree tree_type_array_type = TREE_TYPE(array_type);
+ gcc_assert(tree_type_array_type);
+ update_escape_info (tree_type_array_type, escape_map, info->is_escaping, info->reason);
+}
+
+void
+update_escape_info_record (const_tree record_type, type_map &escape_map, bool is_escaping, escaping_reason reason)
+{
+ gcc_assert(record_type);
+ enum tree_code tree_code_record_type = TREE_CODE(record_type);
+ bool is_record_type = RECORD_TYPE == tree_code_record_type;
+ gcc_assert(is_record_type);
+
+ escaping_info *info = escape_map.get(record_type);
+ // we are collecting records, therefore, we **must** have
+ // it in the escaping info
+ //FIXME: Is this true? According to creduce bug, no.
+ gcc_assert(info);
+ info->is_escaping |= is_escaping;
+ info->reason = union_op(info->reason, reason);
+
+
+ for (tree field = TYPE_FIELDS (record_type); field; field = DECL_CHAIN (field))
+ {
+ gcc_assert(field);
+ tree tree_type_field = TREE_TYPE(field);
+ gcc_assert(tree_type_field);
+ update_escape_info (tree_type_field, escape_map, info->is_escaping, info->reason);
+ }
+}
+
+void
+update_escape_info (const_tree type, type_map &escape_map, bool is_escaping, escaping_reason reason)
+{
+ // INFO: This is an optimization.
+ if (!is_escaping) { log("is not escaping\n"); return; }
+
+ gcc_assert(type);
+ enum tree_code tree_code_type = TREE_CODE(type);
+ // We need to make sure that we are only dealing with
+ // the main type and not typedefs...
+ switch (tree_code_type)
+ {
+ case ARRAY_TYPE:
+ update_escape_info_array(type, escape_map, is_escaping, reason);
+ break;
+ case POINTER_TYPE:
+ update_escape_info_pointer(type, escape_map, is_escaping, reason);
+ break;
+ case RECORD_TYPE:
+ update_escape_info_record(type, escape_map, is_escaping, reason);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool
+filter_pointer (type_map &escape_map, const_tree pointer)
+{
+ enum tree_code code = TREE_CODE (pointer);
+ gcc_assert(POINTER_TYPE == code);
+ const_tree base_type = TREE_TYPE(pointer);
+ return filter_type (escape_map, base_type);
+}
+
+static bool
+filter_array (type_map &escape_map, const_tree array)
+{
+ enum tree_code code = TREE_CODE (array);
+ gcc_assert(ARRAY_TYPE == code);
+ const_tree base_type = TREE_TYPE(array);
+ return filter_type (escape_map, base_type);
+}
+
+static bool
+filter_record (type_map &escape_map, const_tree record)
+{
+ log("filtering record %s\n", get_type_name(record));
+ gcc_assert(record);
+ enum tree_code code = TREE_CODE(record);
+ gcc_assert(RECORD_TYPE == code);
+
+ for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field))
+ {
+ tree field_type = TREE_TYPE(field);
+ gcc_assert(field_type);
+ filter_type(escape_map, field_type);
+ }
+
+ return false;
+}
+
+static bool
+filter_type (type_map &escape_map, const_tree type)
+{
+ log("inside filter_type\n");
+
+ if (!type) return true;
+
+ bool retval = true;
+ enum tree_code code = TREE_CODE(type);
+ switch (code)
+ {
+ case ARRAY_TYPE: retval = filter_array(escape_map, type); break;
+ case POINTER_TYPE: retval = filter_pointer(escape_map, type); break;
+ case RECORD_TYPE: retval = filter_record(escape_map, type); break;
+ default: break;
+ }
+
+
+ log("filter_type %s boring ? %s\n", get_type_name(type), retval ? "true" : "false");
+ if (retval) return retval;
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { type , false , reason};
+ escape_map.put(type, info);
+ return retval;
+}
+
+static bool
+filter_var_decl (type_map &escape_map, const_tree var_decl)
+{
+ log("filtering var_decl\n");
+ gcc_assert(var_decl);
+ enum tree_code code = TREE_CODE(var_decl);
+ gcc_assert(code == VAR_DECL);
+ tree type = TREE_TYPE (var_decl);
+ gcc_assert(type);
+ return filter_type(escape_map, type);
+}
+
+static bool
+filter_parm_declarations (const_tree parm_decl, type_map &escape_map)
+{
+ gcc_assert(parm_decl);
+ enum tree_code code = TREE_CODE(parm_decl);
+ gcc_assert(PARM_DECL == code);
+ tree type = TREE_TYPE (parm_decl);
+ gcc_assert(type);
+ return filter_type(escape_map, type);
+}
+
+static void
+collect_global(type_map &escape_map, varpool_node *vnode)
+{
+ log("collect_global\n");
+ gcc_assert(vnode);
+
+ struct ipa_ref *ref = NULL;
+ for (int i = 0; vnode->iterate_referring (i, ref); i++)
+ {
+ log("inside vnode loop\n");
+ tree decl = vnode->decl;
+ gcc_assert(decl);
+ enum tree_code code = TREE_CODE(decl);
+ gcc_assert(VAR_DECL == code);
+ bool filter_out = filter_var_decl(escape_map, decl);
+ if (filter_out) continue;
+
+ tree type = TREE_TYPE(decl);
+ gcc_assert(type);
+ log("collecting global type escape analysis %s\n", get_type_name(type));
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { type, false, reason };
+ escape_map.put (type, info);
+ //escape_map.put (TYPE_MAIN_VARIANT(type), info);
+ }
+}
+
+static void
+collect_globals(type_map &escape_map)
+{
+ varpool_node *vnode;
+ FOR_EACH_VARIABLE (vnode)
+ {
+ collect_global(escape_map, vnode);
+ }
+}
+
+static void
+collect_parm_declarations (cgraph_node *cnode, type_map &escape_map)
+{
+ gcc_assert(cnode);
+ log("does function %s have parameters?\n", cnode->name());
+ for (tree parm = DECL_ARGUMENTS (cnode->decl); parm; parm = DECL_CHAIN (parm))
+ {
+ tree type = TREE_TYPE(parm);
+ log("about to enter parameter type %s\n", get_type_name(type));
+ bool filter_out = filter_parm_declarations (parm, escape_map);
+ if (filter_out) continue;
+
+ log("putting parameter type %s\n", get_type_name(type));
+ gcc_assert(type);
+
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { type, false, reason };
+ escape_map.put(type, info);
+ }
+}
+
+static void
+collect_local_declarations (cgraph_node *cnode, type_map &escape_map)
+{
+ gcc_assert(cnode);
+ int i = 0;
+ function *func = DECL_STRUCT_FUNCTION (cnode->decl);
+ cnode->get_untransformed_body ();
+ tree var_decl = NULL;
+ FOR_EACH_LOCAL_DECL (func, i, var_decl)
+ {
+ gcc_assert(var_decl);
+ bool filter_out = filter_var_decl(escape_map, var_decl);
+ if (filter_out) continue;
+
+ tree tree_type = TREE_TYPE(var_decl);
+ gcc_assert(tree_type);
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { tree_type, false, reason};
+ escape_map.put(tree_type, info);
+ }
+}
+
+static void collect_expr (const_tree expr, type_map &escape_map);
+
+static void
+collect_expr_in_component_ref(const_tree cref, type_map &escape_map)
+{
+ gcc_assert(cref);
+ const enum tree_code code = TREE_CODE(cref);
+ const bool is_cref = COMPONENT_REF == code;
+ gcc_assert(is_cref);
+
+ const_tree _struct = TREE_OPERAND(cref, 0);
+ gcc_assert(_struct);
+
+ const_tree _struct_type = TREE_TYPE(_struct);
+ log("we are in collect_expr_in_component_ref %s\n", get_type_name(_struct_type));
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { _struct_type, false , reason};
+ escape_map.put(_struct_type, info);
+
+ collect_expr(_struct, escape_map);
+
+}
+
+static void
+collect_expr (const_tree expr, type_map &escape_map)
+{
+ gcc_assert(expr);
+ enum tree_code tree_code_expr = TREE_CODE(expr);
+ switch (tree_code_expr)
+ {
+ case COMPONENT_REF:
+ collect_expr_in_component_ref(expr, escape_map);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+collect_pointer_plus (tree lhs, tree rhs1, tree rhs2, type_map &escape_map)
+{
+ log("collect_pointer_plus\n");
+ const_tree lhs_type = lhs ? TREE_TYPE(lhs) : NULL;
+ bool is_lhs_boring = lhs_type ? filter_type(escape_map, lhs_type) : true;
+ const_tree rhs1_type = rhs1 ? TREE_TYPE(rhs1) : NULL;
+ bool is_rhs1_boring = rhs1_type ? filter_type(escape_map, rhs1_type) : true;
+ const_tree rhs2_type = rhs2 ? TREE_TYPE(rhs2) : NULL;
+ bool is_rhs2_boring = rhs2_type ? filter_type(escape_map, rhs2_type) : true;
+
+ if (!is_lhs_boring)
+ {
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = { lhs_type , false, reason };
+ escape_map.put(lhs_type, info);
+ }
+ if (!is_rhs1_boring)
+ {
+ collect_expr(rhs1, escape_map);
+ }
+ if (!is_rhs2_boring)
+ {
+ collect_expr(rhs2, escape_map);
+ }
+}
+
+static void
+collect_assign_rhs (gimple *stmt, type_map &escape_map)
+{
+ enum tree_code code = gimple_expr_code (stmt);
+ switch (code)
+ {
+ case POINTER_PLUS_EXPR:
+ case POINTER_DIFF_EXPR:
+ case COMPONENT_REF:
+ {
+ tree rhs2 = gimple_assign_rhs2(stmt);
+ tree rhs1 = gimple_assign_rhs1(stmt);
+ tree lhs = gimple_assign_lhs (stmt);
+ collect_pointer_plus (lhs, rhs1, rhs2, escape_map);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+collect_assign (gimple *stmt, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ collect_assign_rhs (stmt, escape_map);
+}
+
+static void
+collect_call_lhs (gimple *stmt, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs) return;
+
+ collect_expr (lhs, escape_map);
+}
+
+static void
+collect_call_rhs (gimple *stmt, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ tree fn = gimple_call_fn (stmt);
+
+ gcc_assert (fn);
+ gcall *call = dyn_cast <gcall *> (stmt);
+ //struct cgraph_node *n = cgraph_node::get (fn);
+ //if (n) log ("collecting arguments for %s \n", n->name());
+
+ tree return_type = gimple_call_return_type (call);
+ bool is_return_type_boring = filter_type(escape_map, return_type);
+
+ if (!is_return_type_boring) {
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = {return_type, false, reason};
+ escape_map.put(return_type, info);
+ }
+
+ unsigned args = gimple_call_num_args (stmt);
+ for (unsigned i = 0; i < args; i++)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ tree arg_type = TREE_TYPE(arg);
+ log("collecting argument of type %s\n", get_type_name(arg_type));
+ bool is_arg_boring = filter_type(escape_map, arg_type);
+ if (is_arg_boring) continue;
+
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = {arg_type, false, reason};
+ escape_map.put(arg_type, info);
+
+ }
+}
+
+static void
+collect_call(gimple *stmt, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ collect_call_lhs (stmt, escape_map);
+ collect_call_rhs (stmt, escape_map);
+}
+
+static void
+collect_stmt (gimple *stmt, type_map &escape_map)
+{
+ gcc_assert (stmt);
+ const enum gimple_code code = gimple_code (stmt);
+ switch (code)
+ {
+ case GIMPLE_ASSIGN:
+ collect_assign(stmt, escape_map);
+ break;
+ case GIMPLE_CALL:
+ collect_call(stmt, escape_map);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+static void
+collect_basic_block (basic_block bb, type_map &escape_map)
+{
+ gcc_assert(bb);
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next(&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ collect_stmt (stmt, escape_map);
+ }
+}
+
+static void
+collect_function_body (cgraph_node *cnode, type_map &escape_map)
+{
+ gcc_assert (cnode);
+ cnode->get_untransformed_body();
+ basic_block bb = NULL;
+ tree decl = cnode->decl;
+ gcc_assert(decl);
+ function *func = DECL_STRUCT_FUNCTION (decl);
+ gcc_assert(func);
+ push_cfun(func);
+ FOR_EACH_BB_FN (bb, func)
+ {
+ gcc_assert(bb);
+ collect_basic_block (bb, escape_map);
+ }
+ pop_cfun();
+}
+
+static void
+collect_return_type (cgraph_node *cnode, type_map &escape_map)
+{
+ gcc_assert(cnode);
+
+ const_tree decl = cnode->decl;
+ const enum tree_code code = TREE_CODE(decl);
+ const bool is_function_decl = FUNCTION_DECL == code;
+ gcc_assert (is_function_decl);
+
+ const_tree fn_type = TREE_TYPE (decl);
+ const enum tree_code fn_type_code = TREE_CODE(fn_type);
+ const bool is_fn_type = FUNCTION_TYPE == fn_type_code;
+ gcc_assert (is_fn_type);
+
+ const_tree ret_type = TREE_TYPE(fn_type);
+ gcc_assert (ret_type);
+
+ const bool is_boring = filter_type(escape_map, ret_type);
+ if (is_boring) return;
+
+ escaping_reason reason = new_escaping_reason();
+ escaping_info info = {ret_type, false, reason};
+ escape_map.put(ret_type, info);
+ return;
+}
+
+static void
+collect_types(type_map &escape_map)
+{
+ collect_globals(escape_map);
+
+ cgraph_node *cnode = NULL;
+ FOR_EACH_DEFINED_FUNCTION (cnode)
+ {
+ collect_function_body (cnode, escape_map);
+ collect_local_declarations (cnode, escape_map);
+ collect_parm_declarations (cnode, escape_map);
+ collect_return_type (cnode, escape_map);
+ }
+}
+
+bool
+_print_types( const_tree const &type, escaping_info *info, __attribute__((unused)) void*)
+{
+ log("collected,%s\n", get_type_name(type));
+ bool is_escaping = info->is_escaping;
+ log("type %s is escaping %s : ", get_type_name(type), is_escaping ? "true" : "false");
+ explain_print(info->reason);
+ return true;
+}
+
+static void
+print_types(type_map &escape_map)
+{
+ log("printing_types\n");
+ escape_map.traverse<void*, _print_types> (NULL);
+}
+
+static bool
+is_variable_escaping(varpool_node *vnode)
+{
+ gcc_assert(vnode);
+ return vnode->externally_visible;
+}
+
+static bool
+is_function_escaping(const cgraph_node *cnode)
+{
+ gcc_assert(cnode);
+ return cnode->externally_visible; /* TODO: How are FOR_EACH_FUNCTION and FOR_EACH_DEFINED_FUNCTION different */ // || !cnode->definition;
+}
+
+void calculate_escaping_parameters(const cgraph_node *cnode, type_map &escape_map, bool override = false);
+
+void
+calculate_escaping_parameters(const cgraph_node *cnode, type_map &escape_map, bool override)
+{
+ gcc_assert(cnode);
+ tree function = cnode->decl;
+ gcc_assert(function);
+ enum tree_code code = TREE_CODE (function);
+ bool is_function_decl = FUNCTION_DECL == code;
+ gcc_assert (is_function_decl);
+
+ bool is_escaping = is_function_escaping(cnode) || override;
+ function_args_iterator iter;
+ tree arg;
+ const_tree function_type = TREE_TYPE(function);
+ FOREACH_FUNCTION_ARGS (function_type, arg, iter)
+ {
+ //const_tree main_type = TYPE_MAIN_VARIANT(arg);
+ log("parameter type %s is escaping %s in function %s\n", get_type_name(arg), is_escaping ? "true" : "false", cnode->name());
+ escaping_reason reason = new_escaping_reason();
+ reason.parameter_is_visible = is_escaping;
+ update_escape_info(arg, escape_map, is_escaping, reason);
+ }
+}
+void is_return_type_escaping(const cgraph_node *cnode, type_map &escape_map, bool override = false);
+
+void
+is_return_type_escaping(const cgraph_node *cnode, type_map &escape_map, bool override)
+{
+ gcc_assert(cnode);
+ bool is_escaping = is_function_escaping(cnode) || override;
+ tree function = cnode->decl;
+ gcc_assert(function);
+ enum tree_code code = TREE_CODE(function);
+ bool is_function = FUNCTION_DECL == code;
+ gcc_assert(is_function);
+
+ tree tree_type = TREE_TYPE(function);
+ gcc_assert(tree_type);
+ tree return_type = TREE_TYPE(tree_type);
+ gcc_assert(return_type);
+ log("return type %s\n", get_type_name(return_type));
+
+ // void update_escaping_info (const_tree type, type_map &escape_map, bool is_escaping);
+/*struct escaping_reason_s {
+ unsigned global_is_visible : 1;
+ unsigned function_is_visible : 1;
+};
+*/
+ escaping_reason reason = new_escaping_reason();
+ reason.return_is_visible = is_escaping;
+ update_escape_info(return_type, escape_map, is_escaping, reason);
+ //escaping_info *info = escape_map.get(return_type);
+ // If there's no info it means it is not interesting...
+ //if (!info) return;
+ //info->is_escaping |= is_escaping;
+}
+
+
+bool
+calculate_escaping_types_from_function_signatures (
+ cgraph_node * const &cnode, type_map *escape_map)
+{
+
+ gcc_assert(escape_map);
+ gcc_assert(cnode);
+ bool is_escaping = is_function_escaping(cnode);
+ log("function %s is escaping %s\n", cnode->name(), is_escaping ? "true" : "false");
+ calculate_escaping_parameters(cnode, *escape_map, is_escaping);
+ is_return_type_escaping(cnode, *escape_map, is_escaping);
+ return true;
+}
+
+void
+find_calls_to_undefined_functions_from_call(gimple *stmt, hash_set<tree> &set, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ const enum gimple_code code = gimple_code(stmt);
+ const bool is_gimple_call = GIMPLE_CALL == code;
+ gcc_assert(is_gimple_call);
+
+ gcall *call = dyn_cast<gcall *>(stmt);
+ tree fn = gimple_call_fndecl(stmt);
+
+ // Function pointer?
+ if (!fn) return;
+
+ // Do we ever see a function with the name __xstat?
+ gcc_assert(fn);
+ cgraph_node *n = cgraph_node::get (fn);
+ //gcc_assert(n);
+ log("here2 %s\n", n ? n->name() : "there's no cnode!");
+ log("is contained in set ? %s\n", set.contains(fn) ? "true" : "false");
+
+ // if fn is in set... then it means that it is undefined.
+ if (!set.contains(fn)) return;
+
+ unsigned num_arguments = gimple_call_num_args(call);
+ for (unsigned i = 0; i < num_arguments; i++)
+ {
+ tree argument = gimple_call_arg(stmt, i);
+ /*
+ // TODO: Why is this the case?
+ gimple *def_for_arg = SSA_NAME_DEF_STMT(argument);
+ gcc_assert(def_for_arg);
+ const enum gimple_code code2 = gimple_code(def_for_arg);
+ const bool is_gimple_call2 = GIMPLE_CALL == code2;
+ const bool is_gimple_assign = GIMPLE_ASSIGN == code2;
+ const bool is_assignable = is_gimple_call2 ^ is_gimple_assign;
+ gcc_assert(is_assignable);
+
+ tree arg_var = is_gimple_assign ? gimple_assign_lhs(def_for_arg) : gimple_call_lhs(def_for_arg);
+ gcc_assert(arg_var);
+ */
+ tree arg_type = TREE_TYPE(argument);
+ gcc_assert(arg_type);
+
+ bool is_escaping = true;
+ escaping_reason reason = new_escaping_reason();
+ reason.parameter_is_visible = is_escaping;
+ update_escape_info(arg_type, escape_map, is_escaping, reason);
+ }
+}
+
+void
+find_calls_to_undefined_functions_from_stmt(gimple *stmt, hash_set<tree> &set, type_map &escape_map)
+{
+ gcc_assert(stmt);
+ const enum gimple_code code = gimple_code (stmt);
+ const bool is_gimple_call = GIMPLE_CALL == code;
+ if (!is_gimple_call) return;
+
+ find_calls_to_undefined_functions_from_call(stmt, set, escape_map);
+}
+
+
+void
+find_calls_to_undefined_functions_from_bb(basic_block bb, hash_set<tree> &set, type_map &escape_map)
+{
+ gcc_assert(bb);
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ find_calls_to_undefined_functions_from_stmt(stmt, set, escape_map);
+ }
+}
+
+void
+find_calls_to_undefined_functions_from_function(cgraph_node *cnode, hash_set<tree> &set, type_map &escape_map)
+{
+ gcc_assert(cnode);
+ cnode->get_untransformed_body ();
+ log("%s is an undefined function\n", cnode->name());
+ function *func = DECL_STRUCT_FUNCTION(cnode->decl);
+ basic_block bb = NULL;
+ push_cfun(func);
+ FOR_EACH_BB_FN(bb, func)
+ {
+ find_calls_to_undefined_functions_from_bb(bb, set, escape_map);
+ }
+ pop_cfun();
+}
+
+void
+is_any_function_escaping(type_map &escape_map)
+{
+ cgraph_node *cnode = NULL;
+ hash_set<tree> not_defined_functions;
+
+ FOR_EACH_FUNCTION (cnode)
+ {
+ gcc_assert(cnode);
+ const char* _free = "free";
+ //const char* _memset = "memset";
+ const char* _malloc = "malloc";
+ const char* _realloc = "realloc";
+ const char* _calloc= "calloc";
+ const char* ob_name = cnode->name();
+ //log("for each function %s\n", cnode->name()); // We do see __xstat...
+ if (strcmp(ob_name, _free) == 0) continue;
+ //if (strcmp(ob_name, _memset) == 0) continue;
+ if (strcmp(ob_name, _malloc) == 0) continue;
+ if (strcmp(ob_name, _realloc) == 0) continue;
+ if (strcmp(ob_name, _calloc) == 0) continue;
+
+ not_defined_functions.add(cnode->decl);
+ }
+
+
+ FOR_EACH_DEFINED_FUNCTION(cnode)
+ {
+ gcc_assert(cnode);
+ cnode->get_untransformed_body();
+ not_defined_functions.remove(cnode->decl);
+ //log("for each defined function %s\n", cnode->name()); // We don't see __xstat...
+ calculate_escaping_types_from_function_signatures (cnode, &escape_map);
+ }
+
+ // not_defined_functions.traverse<type_map *, calculate_escaping_types_from_function_signatures> (&escape_map);
+ //TODO: Walk gimple code and identify GIMPLE_CALL to see if function is not defined...
+ FOR_EACH_DEFINED_FUNCTION(cnode)
+ {
+ find_calls_to_undefined_functions_from_function(cnode, not_defined_functions, escape_map);
+ }
+}
+
+void
+is_any_variable_escaping(type_map &escape_map)
+{
+ varpool_node *vnode = NULL;
+ // Global variables
+ FOR_EACH_VARIABLE(vnode)
+ {
+ bool is_escaping = is_variable_escaping(vnode);
+ log("variable %s is escaping %s\n", vnode->name(), is_escaping ? "true" : "false");
+ tree decl = vnode->decl;
+ gcc_assert(decl);
+ tree type = TREE_TYPE(decl);
+ gcc_assert(type);
+ // void update_escaping_info (const_tree type, type_map &escape_map, bool is_escaping);
+ escaping_reason reason = new_escaping_reason();
+ reason.global_is_visible = is_escaping;
+ update_escape_info (type, escape_map, is_escaping, reason);
+ //escaping_info *info = escape_map.get(type);
+ //if (!info) return;
+ //info->is_escaping |= is_escaping;
+ }
+}
+
+//TODO: place in header file
+inline static void
+print_function (cgraph_node *cnode)
+{
+ if (!dump_file)
+ return;
+ gcc_assert (cnode);
+ cnode->get_untransformed_body ();
+ dump_function_to_file (cnode->decl, dump_file, TDF_NONE);
+}
+
+static bool
+cast_to_void_in_assign(const_tree lhs, const_tree rhs, type_map &escape_map)
+{
+ gcc_assert(lhs);
+ gcc_assert(rhs);
+ const_tree tree_type_lhs = TREE_TYPE(lhs);
+ gcc_assert(tree_type_lhs);
+ const_tree tree_type_rhs = TREE_TYPE(rhs);
+ gcc_assert(tree_type_rhs);
+ const char* const type_name_lhs = get_type_name(tree_type_lhs);
+ const char* const type_name_rhs = get_type_name(tree_type_rhs);
+ enum tree_code tree_code_rhs = TREE_CODE(rhs);
+ bool is_ssa_name = SSA_NAME == tree_code_rhs;
+ bool is_var_decl = VAR_DECL == tree_code_rhs;
+ bool is_var_decl_or_ssa_name = is_ssa_name || is_var_decl;
+ log("lhs = %s, rhs = %s\n", type_name_lhs, type_name_rhs);
+ if (!is_var_decl_or_ssa_name) return false;
+
+ // We need to find out the base...
+ const_tree base_type_lhs = get_base_type(tree_type_lhs);
+ gcc_assert(base_type_lhs);
+ const_tree base_type_rhs = get_base_type(tree_type_rhs);
+ gcc_assert(base_type_rhs);
+ // TODO: FIXME:
+ // Is this always correct? I think this can only happen
+ // if we are mallocing and friends...
+ enum tree_code tree_code_base_type_rhs = TREE_CODE(base_type_rhs);
+ if (tree_code_base_type_rhs == VOID_TYPE) return false;
+
+ bool is_casting_stmt = TYPE_MAIN_VARIANT(base_type_lhs) != TYPE_MAIN_VARIANT(base_type_rhs);
+ log("is casting stmt ? %s\n", is_casting_stmt ? "true" : "false");
+ if (!is_casting_stmt) return false;
+
+ log("escaping lhs %s\n", type_name_lhs);
+ escaping_reason reason = new_escaping_reason();
+ reason.type_is_casted = is_casting_stmt;
+ update_escape_info(tree_type_lhs, escape_map, is_casting_stmt, reason);
+
+ log("escaping rhs %s\n", type_name_rhs);
+ update_escape_info(tree_type_rhs, escape_map, is_casting_stmt, reason);
+ return true;
+}
+
+static void
+cast_to_void_in_assign (gimple *stmt, type_map &escape_map)
+{
+ enum gimple_code gcode = gimple_code (stmt);
+ const char* const gcode_str = gimple_code_name[gcode];
+ gcc_assert(gcode_str);
+
+ switch (gimple_assign_rhs_class (stmt))
+ {
+ case GIMPLE_SINGLE_RHS:
+ case GIMPLE_UNARY_RHS:
+ {
+ tree lhs = gimple_assign_lhs(stmt);
+ tree rhs = gimple_assign_rhs1(stmt);
+ bool retval = cast_to_void_in_assign(lhs, rhs, escape_map);
+ if (!retval) break;
+
+ tree tree_type_lhs = TYPE_MAIN_VARIANT(TREE_TYPE(lhs));
+ tree tree_type_rhs = TYPE_MAIN_VARIANT(TREE_TYPE(rhs));
+ const char* const type_name_lhs = get_type_name(tree_type_lhs);
+ const char* const type_name_rhs = get_type_name(tree_type_rhs);
+ log("type casting %s != %s ", type_name_lhs, type_name_rhs);
+ if (dump_file) print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
+ log("\n");
+ }
+ default:
+ break;
+ }
+}
+
+static void
+cast_to_void_in_stmt (gimple *stmt, type_map &escape_map)
+{
+ gcc_assert (stmt);
+ const enum gimple_code code = gimple_code (stmt);
+ switch (code)
+ {
+ case GIMPLE_ASSIGN:
+ cast_to_void_in_assign(stmt, escape_map);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+static void
+cast_to_void_in_bb(basic_block bb, type_map &escape_map)
+{
+ gcc_assert(bb);
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next(&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ cast_to_void_in_stmt (stmt, escape_map);
+ }
+}
+
+static void
+cast_to_void_in_function(cgraph_node *cnode, type_map &escape_map)
+{
+ gcc_assert (cnode);
+ print_function (cnode);
+ basic_block bb = NULL;
+ tree decl = cnode->decl;
+ gcc_assert(decl);
+ function *func = DECL_STRUCT_FUNCTION (decl);
+ gcc_assert(func);
+ push_cfun(func);
+ FOR_EACH_BB_FN (bb, func)
+ {
+ gcc_assert(bb);
+ cast_to_void_in_bb(bb, escape_map);
+ }
+ pop_cfun();
+}
+
+static void
+cast_to_void_in_program(type_map &escape_map)
+{
+ cgraph_node *cnode = NULL;
+ FOR_EACH_DEFINED_FUNCTION (cnode)
+ {
+ gcc_assert(cnode);
+ cnode->get_untransformed_body();
+ cast_to_void_in_function(cnode, escape_map);
+ }
+}
+
+/* INFO:
+ * Yes, I know we are returning a std::map.
+ * Bad pattern? Maybe, but this will only be called once
+ * and I rather pass by value because that allows
+ * me to have a pure function and not worry about
+ * garbage collection
+ *
+ * TODO: I'd like to change type_map for a std::map
+ * TODO: I'd like to make this a template that can work
+ * for std::map and std::set
+ */
+void
+calculate_escaping_types(type_map &escape_map)
+{
+ collect_types(escape_map);
+ is_any_variable_escaping(escape_map);
+ is_any_function_escaping(escape_map);
+ cast_to_void_in_program(escape_map);
+ print_types(escape_map);
+}
+
+static unsigned int
+iphw_execute()
+{
+ // We can ignore the return value here...
+ type_map escape_map;
+ calculate_escaping_types(escape_map);
+ return 0;
+}
+
+
+namespace {
+const pass_data pass_data_ipa_type_escape_analysis =
+{
+ SIMPLE_IPA_PASS,
+ "type-escape-analysis",
+ OPTGROUP_NONE,
+ TV_NONE,
+ (PROP_cfg | PROP_ssa),
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+class pass_ipa_type_escape_analysis : public simple_ipa_opt_pass
+{
+public:
+ pass_ipa_type_escape_analysis (gcc::context *ctx)
+ : simple_ipa_opt_pass(pass_data_ipa_type_escape_analysis, ctx)
+ {}
+
+ virtual bool gate(function*) { return in_lto_p && flag_ipa_type_escape_analysis; }
+ virtual unsigned execute (function*) { return iphw_execute(); }
+};
+} // anon namespace
+
+simple_ipa_opt_pass*
+make_pass_ipa_type_escape_analysis (gcc::context *ctx)
+{
+ return new pass_ipa_type_escape_analysis (ctx);
+}