From: Michael Beck Date: Thu, 3 Nov 2005 16:37:30 +0000 (+0000) Subject: initial escape analysis added X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=73b6c3b0c8d275b3dd8b56656c5088a2ca8a58c6;p=libfirm initial escape analysis added [r6865] --- diff --git a/ir/opt/Makefile.in b/ir/opt/Makefile.in index c3e5260c5..eaa6ead58 100644 --- a/ir/opt/Makefile.in +++ b/ir/opt/Makefile.in @@ -17,14 +17,15 @@ subdir := ir/ir INSTALL_HEADERS = cfopt.h tailrec.h ldstopt.h strength_red.h reassoc.h \ loop_unrolling.h funccall.h opt_polymorphy.h ifconv.h \ - return.h tropt.h scalar_replace.h + return.h tropt.h scalar_replace.h escape_ana.h SOURCES = $(INSTALL_HEADERS) SOURCES += Makefile.in \ cfopt.c tailrec.c strength_red.c ldstopt.c reassoc.c \ loop_unrolling.c ifconv.c scalar_replace.c funccall.c \ - opt_polymorphy.c return.c tropt.c opt_confirms.c opt_confirms.h + opt_polymorphy.c return.c tropt.c opt_confirms.c opt_confirms.h \ + escape_ana.c include $(topdir)/MakeRules diff --git a/ir/opt/escape_ana.c b/ir/opt/escape_ana.c new file mode 100644 index 000000000..c08951124 --- /dev/null +++ b/ir/opt/escape_ana.c @@ -0,0 +1,292 @@ +/* + * Project: libFIRM + * File name: ir/opt/escape_ana.c + * Purpose: escape analysis and optimization + * Author: Michael Beck + * Modified by: + * Created: 03.11.2005 + * CVS-ID: $Id$ + * Copyright: (c) 1999-2005 Universität Karlsruhe + * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE. + */ + +/** @file escape_ana.c + * + * escape analysis. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "irgraph_t.h" +#include "irnode_t.h" +#include "irouts.h" +#include "analyze_irg_args.h" +#include "irgmod.h" +#include "ircons.h" +#include "escape_ana.h" + +/** + * walker environment + */ +typedef struct _walk_env { + ir_node *found_allocs; /**< list of all found non-escaped allocs */ + ir_node *dead_allocs; /**< list of all found dead alloc */ + unsigned nr_changed; /**< number of changed allocs */ + unsigned nr_deads; /**< number of dead allocs */ + + /* these fields are only used in the global escape analysis */ + ir_graph *irg; /**< the irg for this environment */ + struct _walk_env *next; /**< for linking environments */ + +} walk_env_t; + +/** + * determine if a value calculated by n "escape", ie + * is stored somewhere we could not track + */ +static int do_escape(ir_node *n) { + int i, j, k; + + /* should always be pointer mode or we made some mistake */ + assert(mode_is_reference(get_irn_mode(n))); + + for (i = get_irn_n_outs(n) - 1; i >= 0; --i) { + ir_node *succ = get_irn_out(n, i); + ir_op *op = get_irn_op(succ); + + if (op == op_Store) { + if (get_Store_value(succ) == n) { + /* + * We are storing n. As long as we do not further + * evaluate things, the pointer 'escape' here + */ + return 1; + } + } + else if (op == op_Conv) { + /* + * Should not happen, but if it does we leave the pointer + * path and do not track further + */ + return 1; + } + else if (op == op_Call) { /* most complicated case */ + ir_node *ptr = get_Call_ptr(succ); + entity *ent; + + if (get_irn_op(ptr) == op_SymConst && + get_SymConst_kind(ptr) == symconst_addr_ent) { + ent = get_SymConst_entity(ptr); + + /* we know the called entity */ + for (j = get_Call_n_params(succ); j >= 0; --j) { + if (get_Call_param(succ, j) == n) { + /* n is the j'th param of the call */ + if (get_method_param_access(ent, j) & ptr_access_store) + /* n is store in ent */ + return 1; + } + } + } + else { + /* go through all possible callees */ + for (k = get_Call_n_callees(succ) - 1; k >= 0; --k) { + ent = get_Call_callee(succ, k); + + for (j = get_Call_n_params(succ); j >= 0; --j) { + if (get_Call_param(succ, j) == n) { + /* n is the j'th param of the call */ + if (get_method_param_access(ent, j) & ptr_access_store) + /* n is store in ent */ + return 1; + } + } + } + } + } + + if (! mode_is_reference(get_irn_mode(succ))) + continue; + + if (do_escape(succ)) + return 1; + } + return 0; +} + +/** + * walker: search for Alloc nodes and follow the usages + */ +static void find_allocations(ir_node *alloc, void *ctx) +{ + int i; + ir_node *adr; + walk_env_t *env = ctx; + + if (get_irn_op(alloc) != op_Alloc) + return; + + /* we searching only for heap allocations */ + if (get_Alloc_where(alloc) != heap_alloc) + return; + + adr = NULL; + for (i = get_irn_n_outs(alloc); i >= 0; --i) { + ir_node *proj = get_irn_out(alloc, i); + + if (get_Proj_proj(proj) == pn_Alloc_res) { + adr = proj; + break; + } + } + + if (! adr) { + /* + * bad: no-one wants the result, should NOT happen but + * if it does we could delete it. + */ + set_irn_link(alloc, env->dead_allocs); + env->dead_allocs = alloc; + + return; + } + + if (! do_escape(adr)) { + set_irn_link(alloc, env->found_allocs); + env->found_allocs = alloc; + } +} + +/** + * do the necessary graph transformations + */ +static void transform_allocs(ir_graph *irg, walk_env_t *env) +{ + ir_node *alloc, *next, *mem, *sel; + type *ftp; + entity *ent; + char name[32]; + unsigned nr = 0; + dbg_info *dbg; + + /* kill all dead allocs */ + for (alloc = env->dead_allocs; alloc; alloc = next) { + next = get_irn_link(alloc); + + mem = get_Alloc_mem(alloc); + turn_into_tuple(alloc, pn_Alloc_max); + set_Tuple_pred(alloc, pn_Alloc_M, mem); + set_Tuple_pred(alloc, pn_Alloc_X_except, new_r_Bad(irg)); + + ++env->nr_deads; + } + + /* convert all non-escaped heap allocs into frame variables */ + ftp = get_irg_frame_type(irg); + for (alloc = env->dead_allocs; alloc; alloc = next) { + next = get_irn_link(alloc); + dbg = get_irn_dbg_info(alloc); + + snprintf(name, sizeof(name), "_not_escaped_%u", nr++); + ent = new_d_entity(ftp, new_id_from_str(name), get_Alloc_type(alloc), dbg); + + sel = new_rd_simpleSel(dbg, irg, get_nodes_block(alloc), + get_irg_no_mem(irg), get_irg_frame(irg), ent); + mem = get_Alloc_mem(alloc); + + turn_into_tuple(alloc, pn_Alloc_max); + set_Tuple_pred(alloc, pn_Alloc_M, mem); + set_Tuple_pred(alloc, pn_Alloc_X_except, new_r_Bad(irg)); + set_Tuple_pred(alloc, pn_Alloc_res, sel); + + ++env->nr_changed; + } + + if (env->nr_changed | env->nr_deads) { + set_irg_outs_inconsistent(irg); + + if (env->nr_deads) + set_irg_dom_inconsistent(irg); + } +} + +/* Do simple and fast escape analysis for one graph. */ +void escape_enalysis_irg(ir_graph *irg) +{ + walk_env_t env; + + if (get_irg_callee_info_state(irg) != irg_callee_info_consistent) { + /* no way yet to calculate this for one irg */ + assert(! "need callee info"); + return; + } + + if (get_irg_outs_state(irg) != outs_consistent) + compute_irg_outs(irg); + + env.found_allocs = NULL; + env.dead_allocs = NULL; + env.nr_changed = 0; + env.nr_deads = 0; + + irg_walk_graph(irg, NULL, find_allocations, &env); + + transform_allocs(irg, &env); +} + +/* Do simple and fast escape analysis for all graphs. */ +void escape_analysis(int run_scalar_replace) +{ + ir_graph *irg; + int i; + struct obstack obst; + walk_env_t *env, *elist; + + if (get_irp_callee_info_state() != irg_callee_info_consistent) { + assert(! "need callee info"); + return; + } + + /* + * We treat memory for speed: we first collect all info in a + * list of environments, than do the transformation. + * Doing it this way, no analysis info gets invalid while we run + * over graphs + */ + obstack_init(&obst); + elist = NULL; + + env = obstack_alloc(&obst, sizeof(*env)); + env->found_allocs = NULL; + env->dead_allocs = NULL; + + for (i = get_irp_n_irgs() - 1; i >= 0; --i) { + irg = get_irp_irg(i); + + if (get_irg_outs_state(irg) != outs_consistent) + compute_irg_outs(irg); + + irg_walk_graph(irg, NULL, find_allocations, &env); + + if (env->found_allocs || env->dead_allocs) { + env->nr_changed = 0; + env->nr_deads = 0; + env->irg = irg; + env->next = elist; + + elist = env; + + env = obstack_alloc(&obst, sizeof(*env)); + env->found_allocs = NULL; + env->dead_allocs = NULL; + } + } + + for (env = elist; env; env = env->next) { + transform_allocs(env->irg, env); + } + + obstack_free(&obst, NULL); +} diff --git a/ir/opt/escape_ana.h b/ir/opt/escape_ana.h new file mode 100644 index 000000000..51bc4c5a1 --- /dev/null +++ b/ir/opt/escape_ana.h @@ -0,0 +1,46 @@ +/* + * Project: libFIRM + * File name: ir/opt/escape_ana.h + * Purpose: escape analysis and optimization + * Author: Michael Beck + * Modified by: + * Created: 03.11.2005 + * CVS-ID: $Id$ + * Copyright: (c) 1999-2005 Universität Karlsruhe + * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE. + */ +#ifndef _IR_OPT_ESCAPE_ANA_H_ +#define _IR_OPT_ESCAPE_ANA_H_ + +#include "firm_types.h" + +/** + * Do simple and fast escape analysis for one graph. + * + * @param irg the graph + */ +void escape_enalysis_irg(ir_graph *irg); + +/** + * Do simple and fast escape analysis for all graphs. + * + * This optimization implements a simple and fast but inexact + * escape analysis. Some addresses might be marked as 'escaped' even + * if they are not. + * The advantage is a low memory footprint and fast speed. + * + * @param run_scalar_replace if this flag in non-zero, scalar + * replacement optimization is run on graphs with removed + * allocation + * + * This optimization removes allocation which are not used (rare) and replace + * allocation that can be proved dead at the end of the graph which stack variables. + * + * The creation of stack variable allows scalar replacement to be run only + * on those graphs that have been changed. + * + * This is most effective on Java where no other stack variables exists. + */ +void escape_analysis(int run_scalar_replace); + +#endif /* _IR_OPT_ESCAPE_ANA_H_ */