diff options
Diffstat (limited to 'gcc/ipa-type-escape-analysis.c')
-rw-r--r-- | gcc/ipa-type-escape-analysis.c | 1029 |
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); +} |