From: Götz Lindenmaier Date: Mon, 8 Mar 2004 10:17:31 +0000 (+0000) Subject: new inlineing heuristic X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=8d2c83137a6273f1480ea9f633837e84db8a1939;p=libfirm new inlineing heuristic [r2502] --- diff --git a/ir/ir/irgopt.c b/ir/ir/irgopt.c index dd3538c61..15cb664d9 100644 --- a/ir/ir/irgopt.c +++ b/ir/ir/irgopt.c @@ -28,6 +28,7 @@ # include "irgmod.h" # include "array.h" # include "pset.h" +# include "eset.h" # include "pdeq.h" /* Fuer code placement */ # include "irouts.h" # include "irloop.h" @@ -851,14 +852,34 @@ static int pos; I didn't get a version with NEW_ARR_F to run. */ #define MAX_INLINE 1024 +/* given an Call node, returns the irg called. NULL if not + * known. */ +static ir_graph *get_call_called_irg(ir_node *call) { + ir_node *addr; + tarval *tv; + ir_graph *called_irg = NULL; + + assert(get_irn_op(call) == op_Call); + + addr = get_Call_ptr(call); + if (get_irn_op(addr) == op_Const) { + /* Check whether the constant is the pointer to a compiled entity. */ + tv = get_Const_tarval(addr); + if (tarval_to_entity(tv)) + called_irg = get_entity_irg(tarval_to_entity(tv)); + } + return called_irg; +} + static void collect_calls(ir_node *call, void *env) { + + if (get_irn_op(call) != op_Call) return; + ir_node **calls = (ir_node **)env; ir_node *addr; tarval *tv; ir_graph *called_irg; - if (get_irn_op(call) != op_Call) return; - addr = get_Call_ptr(call); if (get_irn_op(addr) == op_Const) { /* Check whether the constant is the pointer to a compiled entity. */ @@ -916,6 +937,194 @@ void inline_small_irgs(ir_graph *irg, int size) { current_ir_graph = rem; } +typedef struct { + int n_nodes; /* Nodes in graph except Id, Tuple, Proj, Start, End */ + int n_nodes_orig; /* for statistics */ + eset *call_nodes; /* All call nodes in this graph */ + int n_call_nodes; + int n_call_nodes_orig; /* for statistics */ + int n_callers; /* Number of known graphs that call this graphs. */ + int n_callers_orig; /* for statistics */ +} inline_irg_env; + +static inline_irg_env *new_inline_irg_env(void) { + inline_irg_env *env = malloc(sizeof(inline_irg_env)); + env->n_nodes = -2; /* uncount Start, End */ + env->n_nodes_orig = -2; /* uncount Start, End */ + env->call_nodes = eset_create(); + env->n_call_nodes = 0; + env->n_call_nodes_orig = 0; + env->n_callers = 0; + env->n_callers_orig = 0; + return env; +} + +static void free_inline_irg_env(inline_irg_env *env) { + eset_destroy(env->call_nodes); + free(env); +} + +static void collect_calls2(ir_node *call, void *env) { + inline_irg_env *x = (inline_irg_env *)env; + ir_op *op = get_irn_op(call); + + /* count nodes in irg */ + if (op != op_Proj && op != op_Tuple && op != op_Sync) { + x->n_nodes++; + x->n_nodes_orig++; + } + + if (op != op_Call) return; + + /* collect all call nodes */ + eset_insert(x->call_nodes, (void *)call); + x->n_call_nodes++; + x->n_call_nodes_orig++; + + /* count all static callers */ + ir_graph *callee = get_call_called_irg(call); + if (callee) { + ((inline_irg_env *)get_irg_link(callee))->n_callers++; + ((inline_irg_env *)get_irg_link(callee))->n_callers_orig++; + } +} + +INLINE static int is_leave(ir_graph *irg) { + return (((inline_irg_env *)get_irg_link(irg))->n_call_nodes == 0); +} + +INLINE static int is_smaller(ir_graph *callee, int size) { + return (((inline_irg_env *)get_irg_link(callee))->n_nodes < size); +} + + +/* Inlines small leave methods at call sites where the called address comes + from a Const node that references the entity representing the called + method. + The size argument is a rough measure for the code size of the method: + Methods where the obstack containing the firm graph is smaller than + size are inlined. */ +void inline_leave_functions(int maxsize, int leavesize, int size) { + inline_irg_env *env; + int i, n_irgs = get_irp_n_irgs(); + ir_graph *rem = current_ir_graph; + int did_inline = 1; + + if (!(get_optimize() && get_opt_inline())) return; + + /* extend all irgs by a temporary datastructure for inlineing. */ + for (i = 0; i < n_irgs; ++i) + set_irg_link(get_irp_irg(i), new_inline_irg_env()); + + /* Precompute information in temporary datastructure. */ + for (i = 0; i < n_irgs; ++i) { + current_ir_graph = get_irp_irg(i); + assert(get_irg_phase_state(current_ir_graph) != phase_building); + + irg_walk(get_irg_end(current_ir_graph), NULL, collect_calls2, + get_irg_link(current_ir_graph)); + env = (inline_irg_env *)get_irg_link(current_ir_graph); + } + + /* and now inline. + Inline leaves recursively -- we might construct new leaves. */ + //int itercnt = 1; + while (did_inline) { + //printf("iteration %d\n", itercnt++); + did_inline = 0; + for (i = 0; i < n_irgs; ++i) { + ir_node *call; + eset *walkset; + int phiproj_computed = 0; + + current_ir_graph = get_irp_irg(i); + env = (inline_irg_env *)get_irg_link(current_ir_graph); + + /* we can not walk and change a set, nor remove from it. + So recompute.*/ + walkset = env->call_nodes; + env->call_nodes = eset_create(); + for (call = eset_first(walkset); call; call = eset_next(walkset)) { + ir_graph *callee = get_call_called_irg(call); + if (env->n_nodes > maxsize) break; + if (callee && is_leave(callee) && is_smaller(callee, leavesize)) { + if (!phiproj_computed) { + phiproj_computed = 1; + collect_phiprojs(current_ir_graph); + } + inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee); + // printf(" %s: Inlineing %s.\n", get_entity_name(get_irg_entity(current_ir_graph)), + // get_entity_name(get_irg_entity(callee))); + inline_method(call, callee); + did_inline = 1; + env->n_call_nodes--; + eset_insert_all(env->call_nodes, callee_env->call_nodes); + env->n_call_nodes += callee_env->n_call_nodes; + env->n_nodes += callee_env->n_nodes; + callee_env->n_callers--; + } else { + eset_insert(env->call_nodes, call); + } + } + eset_destroy(walkset); + } + } + + //printf("Non leaves\n"); + /* inline other small functions. */ + for (i = 0; i < n_irgs; ++i) { + ir_node *call; + eset *walkset; + int phiproj_computed = 0; + + current_ir_graph = get_irp_irg(i); + env = (inline_irg_env *)get_irg_link(current_ir_graph); + + /* we can not walk and change a set, nor remove from it. + So recompute.*/ + walkset = env->call_nodes; + env->call_nodes = eset_create(); + for (call = eset_first(walkset); call; call = eset_next(walkset)) { + ir_graph *callee = get_call_called_irg(call); + if (env->n_nodes > maxsize) break; + if (callee && is_smaller(callee, size)) { + if (!phiproj_computed) { + phiproj_computed = 1; + collect_phiprojs(current_ir_graph); + } + inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee); + //printf(" %s: Inlineing %s.\n", get_entity_name(get_irg_entity(current_ir_graph)), + // get_entity_name(get_irg_entity(callee))); + inline_method(call, callee); + did_inline = 1; + env->n_call_nodes--; + eset_insert_all(env->call_nodes, callee_env->call_nodes); + env->n_call_nodes += callee_env->n_call_nodes; + env->n_nodes += callee_env->n_nodes; + callee_env->n_callers--; + } else { + eset_insert(env->call_nodes, call); + } + } + eset_destroy(walkset); + } + + for (i = 0; i < n_irgs; ++i) { + current_ir_graph = get_irp_irg(i); +#if 0 + env = (inline_irg_env *)get_irg_link(current_ir_graph); + if ((env->n_call_nodes_orig != env->n_call_nodes) || + (env->n_callers_orig != env->n_callers)) + printf("Nodes:%3d ->%3d, calls:%3d ->%3d, callers:%3d ->%3d, -- %s\n", + env->n_nodes_orig, env->n_nodes, env->n_call_nodes_orig, env->n_call_nodes, + env->n_callers_orig, env->n_callers, + get_entity_name(get_irg_entity(current_ir_graph))); +#endif + free_inline_irg_env((inline_irg_env *)get_irg_link(current_ir_graph)); + } + + current_ir_graph = rem; +} /********************************************************************/ /* Code Placement. Pinns all floating nodes to a block where they */ diff --git a/ir/ir/irgopt.h b/ir/ir/irgopt.h index fc102687c..0c249a206 100644 --- a/ir/ir/irgopt.h +++ b/ir/ir/irgopt.h @@ -89,6 +89,27 @@ void inline_method(ir_node *call, ir_graph *called_graph); combination as control flow operation. */ void inline_small_irgs(ir_graph *irg, int size); + +/** Inlineing with a different heuristic than inline_small_irgs. + * + * Inlines leave functions. If inlinening creates new leave + * function inlines these, too. (If g calls f, and f calls leave h, + * h is first inlined in f and then f in g.) + * + * Then inlines all small functions (this is not recursive). + * + * For a heuristic this inlineing uses firm node counts. It does + * not count auxiliary nodes as Proj, Tuple, End, Start, Id, Sync. + * + * maxsize Do not inline any calls if a method has more than + * maxsize firm nodes. It may reach this limit by + * inlineing. + * leavesize Inline leave functions if they have less than leavesize + * nodes. + * size Inline all function smaller than size. + */ +void inline_leave_functions(int maxsize, int leavesize, int size); + /** Code Placement. Pinns all floating nodes to a block where they will be executed only if needed. Depends on the flag opt_global_cse. Graph may not be in phase_building. Does not schedule control dead