X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fopt%2Fopt_inline.c;h=45e55c82a71e406534c12b7ce784e92048904164;hb=5022905bace816183da531dfe0efbee3196ccd9e;hp=ac7581ab9ffff54b797fbb9f9785b8cb31275d2d;hpb=cdebd065a81afa949d70bbb2737f686acc30c946;p=libfirm diff --git a/ir/opt/opt_inline.c b/ir/opt/opt_inline.c index ac7581ab9..45e55c82a 100644 --- a/ir/opt/opt_inline.c +++ b/ir/opt/opt_inline.c @@ -60,6 +60,7 @@ #include "irflag_t.h" #include "irhooks.h" #include "irtools.h" +#include "iropt_dbg.h" DEBUG_ONLY(static firm_dbg_module_t *dbg;) @@ -439,7 +440,8 @@ void dead_node_elimination(ir_graph *irg) { #endif struct obstack *graveyard_obst = NULL; struct obstack *rebirth_obst = NULL; - assert(! edges_activated(irg) && "dead node elimination requires disabled edges"); + + edges_deactivate(irg); /* inform statistics that we started a dead-node elimination run */ hook_dead_node_elim(irg, 1); @@ -726,8 +728,7 @@ void survive_dce_register_irn(survive_dce_t *sd, ir_node **place) { * inlined procedure. The new entities must be in the link field of * the entities. */ -static INLINE void -copy_node_inline(ir_node *n, void *env) { +static void copy_node_inline(ir_node *n, void *env) { ir_node *nn; ir_type *frame_tp = (ir_type *)env; @@ -744,6 +745,27 @@ copy_node_inline(ir_node *n, void *env) { } } +/** + * Copies new predecessors of old node and move constants to + * the Start Block. + */ +static void copy_preds_inline(ir_node *n, void *env) { + ir_node *nn; + + copy_preds(n, env); + nn = skip_Id(get_new_node(n)); + if (is_irn_constlike(nn)) { + /* move Constants into the start block */ + set_nodes_block(nn, get_irg_start_block(current_ir_graph)); + + n = identify_remember(current_ir_graph->value_table, nn); + if (nn != n) { + DBG_OPT_CSE(nn, n); + exchange(nn, n); + } + } +} + /** * Walker: checks if P_value_arg_base is used. */ @@ -821,51 +843,57 @@ int inline_method(ir_node *call, ir_graph *called_graph) { ir_node *pre_call; ir_node *post_call, *post_bl; ir_node *in[pn_Start_max]; - ir_node *end, *end_bl; + ir_node *end, *end_bl, *block; ir_node **res_pred; ir_node **cf_pred; + ir_node **args_in; ir_node *ret, *phi; - int arity, n_ret, n_exc, n_res, i, n, j, rem_opt, irn_arity; + int arity, n_ret, n_exc, n_res, i, n, j, rem_opt, irn_arity, n_params; enum exc_mode exc_handling; - ir_type *called_frame, *curr_frame; + ir_type *called_frame, *curr_frame, *mtp, *ctp; ir_entity *ent; ir_graph *rem, *irg; irg_inline_property prop = get_irg_inline_property(called_graph); + unsigned long visited; if (prop == irg_inline_forbidden) return 0; ent = get_irg_entity(called_graph); - /* Do not inline variadic functions. */ - if (get_method_variadicity(get_entity_type(ent)) == variadicity_variadic) { - /* Arg, KR functions are marked as variadic one's, so check further */ - ir_type *mtp = get_entity_type(ent); - ir_type *ctp = get_Call_type(call); - int n_params = get_method_n_params(mtp); - int i; - - /* This is too strong, but probably ok. Function calls with a wrong number of - parameters should not be inlined. */ - if (n_params != get_method_n_params(ctp)) - return 0; - - /* check types: for K&R calls, this was not done by the compiler. Again, this is - too strong, but ok for now. */ - for (i = n_params - 1; i >= 0; --i) { - ir_type *param_tp = get_method_param_type(mtp, i); - ir_type *arg_tp = get_method_param_type(ctp, i); + mtp = get_entity_type(ent); + ctp = get_Call_type(call); + if (get_method_n_params(mtp) > get_method_n_params(ctp)) { + /* this is a bad feature of C: without a prototype, we can can call a function with less + parameters than needed. Currently we don't support this, although it would be + to use Unknown than. */ + return 0; + } - if (param_tp != arg_tp) + /* Argh, compiling C has some bad consequences: + the call type AND the method type might be different. + It is implementation defendant what happens in that case. + We support inlining, if the bitsize of the types matches AND + the same arithmetic is used. */ + n_params = get_method_n_params(mtp); + for (i = n_params - 1; i >= 0; --i) { + ir_type *param_tp = get_method_param_type(mtp, i); + ir_type *arg_tp = get_method_param_type(ctp, i); + + if (param_tp != arg_tp) { + ir_mode *pmode = get_type_mode(param_tp); + ir_mode *amode = get_type_mode(arg_tp); + + if (pmode == NULL || amode == NULL) return 0; + if (get_mode_size_bits(pmode) != get_mode_size_bits(amode)) + return 0; + if (get_mode_arithmetic(pmode) != get_mode_arithmetic(amode)) + return 0; + /* otherwise we can simply "reinterpret" the bits */ } - DB((dbg, LEVEL_1, "Inlining allowed for variadic function %+F\n", called_graph)); - /* types match, fine: when the frame is access, the inliner stops at can_inline() */ } - assert(get_method_n_params(get_entity_type(ent)) == - get_method_n_params(get_Call_type(call))); - irg = get_irn_irg(call); /* @@ -926,6 +954,21 @@ int inline_method(ir_node *call, ir_graph *called_graph) { else { exc_handling = exc_no_handler; } /* !Mproj && !Xproj */ } + /* create the argument tuple */ + NEW_ARR_A(ir_type *, args_in, n_params); + + block = get_nodes_block(call); + for (i = n_params - 1; i >= 0; --i) { + ir_node *arg = get_Call_param(call, i); + ir_type *param_tp = get_method_param_type(mtp, i); + ir_mode *mode = get_type_mode(param_tp); + + if (mode != get_irn_mode(arg)) { + arg = new_r_Conv(irg, block, arg, mode); + } + args_in[i] = arg; + } + /* -- the procedure and later replaces the Start node of the called graph. Post_call is the old Call node and collects the results of the called @@ -937,7 +980,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) { in[pn_Start_M] = get_Call_mem(call); in[pn_Start_P_frame_base] = get_irg_frame(irg); in[pn_Start_P_tls] = get_irg_tls(irg); - in[pn_Start_T_args] = new_Tuple(get_Call_n_params(call), get_Call_param_arr(call)); + in[pn_Start_T_args] = new_Tuple(n_params, args_in); /* in[pn_Start_P_value_arg_base] = ??? */ assert(pn_Start_P_value_arg_base == pn_Start_max - 1 && "pn_Start_P_value_arg_base not supported, fix"); pre_call = new_Tuple(pn_Start_max - 1, in); @@ -952,20 +995,26 @@ int inline_method(ir_node *call, ir_graph *called_graph) { /* Visited flags in calling irg must be >= flag in called irg. Else walker and arity computation will not work. */ if (get_irg_visited(irg) <= get_irg_visited(called_graph)) - set_irg_visited(irg, get_irg_visited(called_graph)+1); + set_irg_visited(irg, get_irg_visited(called_graph) + 1); if (get_irg_block_visited(irg) < get_irg_block_visited(called_graph)) set_irg_block_visited(irg, get_irg_block_visited(called_graph)); + visited = get_irg_visited(irg); + /* Set pre_call as new Start node in link field of the start node of calling graph and pre_calls block as new block for the start block of calling graph. Further mark these nodes so that they are not visited by the copying. */ set_irn_link(get_irg_start(called_graph), pre_call); - set_irn_visited(get_irg_start(called_graph), get_irg_visited(irg)); + set_irn_visited(get_irg_start(called_graph), visited); set_irn_link(get_irg_start_block(called_graph), get_nodes_block(pre_call)); - set_irn_visited(get_irg_start_block(called_graph), get_irg_visited(irg)); - set_irn_link(get_irg_bad(called_graph), get_irg_bad(irg)); - set_irn_visited(get_irg_bad(called_graph), get_irg_visited(irg)); + set_irn_visited(get_irg_start_block(called_graph), visited); + + set_irn_link(get_irg_bad(called_graph), get_irg_bad(current_ir_graph)); + set_irn_visited(get_irg_bad(called_graph), visited); + + set_irn_link(get_irg_no_mem(called_graph), get_irg_no_mem(current_ir_graph)); + set_irn_visited(get_irg_no_mem(called_graph), visited); /* Initialize for compaction of in arrays */ inc_irg_block_visited(irg); @@ -989,7 +1038,7 @@ int inline_method(ir_node *call, ir_graph *called_graph) { /* -- Performing dead node elimination inlines the graph -- */ /* Copies the nodes to the obstack of current_ir_graph. Updates links to new entities. */ - irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds, + irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds_inline, get_irg_frame_type(called_graph)); /* Repair called_graph */ @@ -1303,6 +1352,7 @@ typedef struct { int n_callers_orig; /**< for statistics */ unsigned got_inline:1; /**< Set, if at least one call inside this graph was inlined. */ unsigned local_vars:1; /**< Set, if a inlined function gets the address of an inlined variable. */ + unsigned recursive:1; /**< Set, if this function is self recursive. */ call_entry *call_head; /**< The head of the list of all call nodes in this graph. */ call_entry *call_tail; /**< The tail of the list of all call nodes in this graph .*/ unsigned *local_weights; /**< Once allocated, the beneficial weight for transmitting local addresses. */ @@ -1324,6 +1374,7 @@ static inline_irg_env *alloc_inline_irg_env(void) { env->n_callers_orig = 0; env->got_inline = 0; env->local_vars = 0; + env->recursive = 0; env->local_weights = NULL; return env; } @@ -1382,6 +1433,8 @@ static void collect_calls2(ir_node *call, void *ctx) { ++callee_env->n_callers; ++callee_env->n_callers_orig; } + if (callee == current_ir_graph) + x->recursive = 1; /* link it in the list of possible inlinable entries */ entry = obstack_alloc(&temp_obst, sizeof(*entry)); @@ -1643,7 +1696,7 @@ void inline_leave_functions(int maxsize, int leavesize, int size, int ignore_run callee_env = alloc_inline_irg_env(); set_irg_link(copy, callee_env); - assure_cf_loop(irg); + assure_cf_loop(copy); wenv.x = callee_env; wenv.ignore_callers = 1; irg_walk_graph(copy, NULL, collect_calls2, &wenv); @@ -1845,14 +1898,19 @@ static unsigned get_method_local_adress_weight(ir_graph *callee, int pos) { } /** - * calculate a benefice value for inlining the given call. + * Calculate a benefice value for inlining the given call. + * + * @param call the call node we have to inspect + * @param callee the called graph + * @param local_adr set after return if an address of a local variable is + * transmitted as a parameter */ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local_adr) { ir_entity *ent = get_irg_entity(callee); ir_node *frame_ptr; ir_type *mtp; int weight = 0; - int i, n_params; + int i, n_params, all_const; unsigned cc, v; inline_irg_env *curr_env, *callee_env; @@ -1881,21 +1939,27 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local /* constant parameters improve the benefice */ frame_ptr = get_irg_frame(current_ir_graph); + all_const = 1; for (i = 0; i < n_params; ++i) { ir_node *param = get_Call_param(call, i); - if (is_Const(param) || is_SymConst(param)) + if (is_Const(param)) { weight += get_method_param_weight(ent, i); - else if (is_Sel(param) && get_Sel_ptr(param) == frame_ptr) { - /* - * An address of a local variable is transmitted. After inlining, - * scalar_replacement might be able to remove the local variable, - * so honor this. - */ - v = get_method_local_adress_weight(callee, i); - weight += v; - if (v > 0) - *local_adr = 1; + } else { + all_const = 0; + if (is_SymConst(param)) + weight += get_method_param_weight(ent, i); + else if (is_Sel(param) && get_Sel_ptr(param) == frame_ptr) { + /* + * An address of a local variable is transmitted. After inlining, + * scalar_replacement might be able to remove the local variable, + * so honor this. + */ + v = get_method_local_adress_weight(callee, i); + weight += v; + if (v > 0) + *local_adr = 1; + } } } @@ -1912,14 +1976,14 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local /* reduce the benefice if the current function is already big */ curr_env = get_irg_link(current_ir_graph); - weight -= curr_env->n_nodes / 100; + weight -= curr_env->n_nodes / 50; /* give a bonus for functions with one block */ if (callee_env->n_blocks == 1) weight = weight * 3 / 2; - /* and one for small functions: we want them to be inlined in mostly every case */ - else if (callee_env->n_nodes < 20) + /* and one for small non-recursive functions: we want them to be inlined in mostly every case */ + else if (callee_env->n_nodes < 20 && !callee_env->recursive) weight += 5000; /* and finally for leaves: they do not increase the register pressure @@ -1927,6 +1991,19 @@ static int calc_inline_benefice(ir_node *call, ir_graph *callee, unsigned *local else if (callee_env->n_call_nodes == 0) weight += 25; + /* + * Reduce the weight for recursive function IFF not all arguments are const. + * inlining recursive functions is rarely good. + */ + if (callee_env->recursive && !all_const) + weight -= 500; + + /* + * All arguments constant is probably a good sign, give an extra bonus + */ + if (all_const) + weight += 100; + return weight; } @@ -2089,6 +2166,12 @@ void inline_functions(int maxsize, int inline_threshold) { curr_call = curr_call->next; } + } + + for (i = 0; i < n_irgs; ++i) { + ir_graph *irg = get_irp_irg(i); + + env = get_irg_link(irg); if (env->got_inline) { /* this irg got calls inlined: optimize it */