# include "config.h"
#endif
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif
+#include <string.h>
+#include "iroptimize.h"
#include "irnode_t.h"
#include "irgraph_t.h"
#include "irmode_t.h"
#include "opt_polymorphy.h"
#include "irmemory.h"
#include "xmalloc.h"
+#include "irphase_t.h"
+#include "irgopt.h"
+#include "debug.h"
+
+/** The debug handle. */
+DEBUG_ONLY(static firm_dbg_module_t *dbg;)
#ifdef DO_CACHEOPT
#include "cacheopt/cachesim.h"
/**
* get the Load/Store info of a node
*/
-static ldst_info_t *get_ldst_info(ir_node *node, walk_env_t *env) {
+static ldst_info_t *get_ldst_info(ir_node *node, struct obstack *obst) {
ldst_info_t *info = get_irn_link(node);
if (! info) {
- info = obstack_alloc(&env->obst, sizeof(*info));
+ info = obstack_alloc(obst, sizeof(*info));
memset(info, 0, sizeof(*info));
set_irn_link(node, info);
}
/**
* get the Block info of a node
*/
-static block_info_t *get_block_info(ir_node *node, walk_env_t *env) {
+static block_info_t *get_block_info(ir_node *node, struct obstack *obst) {
block_info_t *info = get_irn_link(node);
if (! info) {
- info = obstack_alloc(&env->obst, sizeof(*info));
+ info = obstack_alloc(obst, sizeof(*info));
memset(info, 0, sizeof(*info));
set_irn_link(node, info);
}
op = get_irn_op(pred);
if (op == op_Load) {
- ldst_info = get_ldst_info(pred, wenv);
+ ldst_info = get_ldst_info(pred, &wenv->obst);
wenv->changes |= update_projs(ldst_info, node);
}
/*
- * Place the Proj's to the same block as the
- * predecessor Load. This is always ok and prevents
- * "non-SSA" form after optimizations if the Proj
- * is in a wrong block.
- */
+ * Place the Proj's to the same block as the
+ * predecessor Load. This is always ok and prevents
+ * "non-SSA" form after optimizations if the Proj
+ * is in a wrong block.
+ */
blk = get_nodes_block(node);
pred_blk = get_nodes_block(pred);
if (blk != pred_blk) {
set_nodes_block(node, pred_blk);
}
} else if (op == op_Store) {
- ldst_info = get_ldst_info(pred, wenv);
+ ldst_info = get_ldst_info(pred, &wenv->obst);
wenv->changes |= update_projs(ldst_info, node);
int i;
for (i = get_Block_n_cfgpreds(node) - 1; i >= 0; --i) {
- ir_node *pred_block;
+ ir_node *pred_block, *proj;
block_info_t *bl_info;
+ int is_exc = 0;
+
+ pred = proj = get_Block_cfgpred(node, i);
- pred = skip_Proj(get_Block_cfgpred(node, i));
+ if (is_Proj(proj)) {
+ pred = get_Proj_pred(proj);
+ is_exc = get_Proj_proj(proj) == pn_Generic_X_except;
+ }
/* ignore Bad predecessors, they will be removed later */
if (is_Bad(pred))
continue;
pred_block = get_nodes_block(pred);
- bl_info = get_block_info(pred_block, wenv);
+ bl_info = get_block_info(pred_block, &wenv->obst);
- if (is_fragile_op(pred))
+ if (is_fragile_op(pred) && is_exc)
bl_info->flags |= BLOCK_HAS_EXC;
else if (is_irn_forking(pred))
bl_info->flags |= BLOCK_HAS_COND;
- if (get_irn_op(pred) == op_Load || get_irn_op(pred) == op_Store) {
- ldst_info = get_ldst_info(pred, wenv);
+ if (is_exc && (get_irn_op(pred) == op_Load || get_irn_op(pred) == op_Store)) {
+ ldst_info = get_ldst_info(pred, &wenv->obst);
wenv->changes |= update_exc(ldst_info, node, i);
}
/* a Load which value is neither used nor exception checked, remove it */
exchange(info->projs[pn_Load_M], mem);
+ if (info->projs[pn_Load_X_regular])
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
exchange(load, new_Bad());
reduce_adr_usage(ptr);
}
* This is only true in strong typed languages, not in C were the following
* is possible a = *(ir_type1 *)p; b = *(ir_type2 *)p ...
*/
- if (get_irn_op(pred) == op_Store && get_Store_ptr(pred) == ptr &&
+ if (is_Store(pred) && get_Store_ptr(pred) == ptr &&
can_use_stored_value(get_irn_mode(get_Store_value(pred)), load_mode)) {
/*
* a Load immediately after a Store -- a read after write.
* We may remove the Load, if both Load & Store does not have an exception handler
- * OR they are in the same block. In the latter case the Load cannot
+ * OR they are in the same MacroBlock. In the latter case the Load cannot
* throw an exception when the previous Store was quiet.
*
* Why we need to check for Store Exception? If the Store cannot
* be executed (ROM) the exception handler might simply jump into
- * the load block :-(
+ * the load MacroBlock :-(
* We could make it a little bit better if we would know that the exception
* handler of the Store jumps directly to the end...
*/
if ((pred_info->projs[pn_Store_X_except] == NULL && info->projs[pn_Load_X_except] == NULL) ||
- get_nodes_block(load) == get_nodes_block(pred)) {
+ get_nodes_MacroBlock(load) == get_nodes_MacroBlock(pred)) {
ir_node *value = get_Store_value(pred);
DBG_OPT_RAW(load, value);
exchange( info->projs[pn_Load_X_except], new_Bad());
res |= CF_CHANGED;
}
+ if (info->projs[pn_Load_X_regular]) {
+ exchange( info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ res |= CF_CHANGED;
+ }
if (info->projs[pn_Load_res])
exchange(info->projs[pn_Load_res], value);
reduce_adr_usage(ptr);
return res | DF_CHANGED;
}
- } else if (get_irn_op(pred) == op_Load && get_Load_ptr(pred) == ptr &&
+ } else if (is_Load(pred) && get_Load_ptr(pred) == ptr &&
can_use_stored_value(get_Load_mode(pred), load_mode)) {
/*
* a Load after a Load -- a read after read.
* We may remove the second Load, if it does not have an exception handler
- * OR they are in the same block. In the later case the Load cannot
+ * OR they are in the same MacroBlock. In the later case the Load cannot
* throw an exception when the previous Load was quiet.
*
* Here, there is no need to check if the previous Load has an exception
* hander because they would have exact the same exception...
*/
- if (info->projs[pn_Load_X_except] == NULL || get_nodes_block(load) == get_nodes_block(pred)) {
+ if (info->projs[pn_Load_X_except] == NULL || get_nodes_MacroBlock(load) == get_nodes_MacroBlock(pred)) {
ir_node *value;
DBG_OPT_RAR(load, pred);
exchange(info->projs[pn_Load_X_except], new_Bad());
res |= CF_CHANGED;
}
+ if (info->projs[pn_Load_X_regular]) {
+ exchange( info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ res |= CF_CHANGED;
+ }
exchange(load, new_Bad());
reduce_adr_usage(ptr);
}
}
- if (get_irn_op(pred) == op_Store) {
+ if (is_Store(pred)) {
/* check if we can pass through this store */
ir_alias_relation rel = get_alias_relation(
current_ir_graph,
MARK_NODE(pred_info);
}
- if (get_irn_op(pred) == op_Sync) {
+ if (is_Sync(pred)) {
int i;
/* handle all Sync predecessors */
ir_node *mem = get_Sel_mem(ptr);
/* FIXME: works with the current FE, but better use the base */
- if (get_irn_op(skip_Proj(mem)) == op_Alloc) {
+ if (is_Alloc(skip_Proj(mem))) {
/* ok, check the types */
ir_entity *ent = get_Sel_entity(ptr);
ir_type *s_type = get_entity_type(ent);
exchange(info->projs[pn_Load_X_except], new_Bad());
info->projs[pn_Load_X_except] = NULL;
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ info->projs[pn_Load_X_regular] = NULL;
res |= CF_CHANGED;
}
}
- } else if ((get_irn_op(skip_Proj(ptr)) == op_Alloc) ||
- ((get_irn_op(ptr) == op_Cast) && (get_irn_op(skip_Proj(get_Cast_op(ptr))) == op_Alloc))) {
+ } else if (is_Alloc(skip_Proj(skip_Cast(ptr)))) {
/* simple case: a direct load after an Alloc. Firm Alloc throw
* an exception in case of out-of-memory. So, there is no way for an
* exception in this load.
*/
exchange(info->projs[pn_Load_X_except], new_Bad());
info->projs[pn_Load_X_except] = NULL;
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ info->projs[pn_Load_X_regular] = NULL;
res |= CF_CHANGED;
}
}
/* a Load which value is neither used nor exception checked, remove it */
exchange(info->projs[pn_Load_M], mem);
+ if (info->projs[pn_Load_X_regular]) {
+ /* should not happen, but if it does, remove it */
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ res |= CF_CHANGED;
+ }
exchange(load, new_Bad());
reduce_adr_usage(ptr);
return res | DF_CHANGED;
if (info->projs[pn_Load_X_except]) {
exchange(info->projs[pn_Load_X_except], new_Bad());
info->projs[pn_Load_X_except] = NULL;
+ res |= CF_CHANGED;
+ }
+ if (info->projs[pn_Load_X_regular]) {
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ info->projs[pn_Load_X_regular] = NULL;
+ res |= CF_CHANGED;
}
if (info->projs[pn_Load_res])
exchange(info->projs[pn_Load_res], new_node);
info->projs[pn_Load_X_except] = NULL;
res |= CF_CHANGED;
}
+ if (info->projs[pn_Load_X_regular]) {
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ info->projs[pn_Load_X_regular] = NULL;
+ res |= CF_CHANGED;
+ }
if (variability_constant == get_entity_variability(ent)
&& is_atomic_entity(ent)) {
ir_node *value = get_Store_value(store);
ir_mode *mode = get_irn_mode(value);
ir_node *block = get_nodes_block(store);
+ ir_node *mblk = get_Block_MacroBlock(block);
for (pred = curr; pred != store;) {
ldst_info_t *pred_info = get_irn_link(pred);
* However, if the mode that is written have a bigger or equal size the the old
* one, the old value is completely overwritten and can be killed ...
*/
- if (get_irn_op(pred) == op_Store && get_Store_ptr(pred) == ptr &&
- get_nodes_block(pred) == block &&
+ if (is_Store(pred) && get_Store_ptr(pred) == ptr &&
+ get_nodes_MacroBlock(pred) == mblk &&
is_completely_overwritten(get_irn_mode(get_Store_value(pred)), mode)) {
/*
* a Store after a Store in the same block -- a write after write.
*/
if (get_Store_volatility(pred) != volatility_is_volatile && !pred_info->projs[pn_Store_X_except]) {
DBG_OPT_WAW(pred, store);
- exchange( pred_info->projs[pn_Store_M], get_Store_mem(pred) );
+ exchange(pred_info->projs[pn_Store_M], get_Store_mem(pred));
exchange(pred, new_Bad());
reduce_adr_usage(ptr);
return DF_CHANGED;
}
- } else if (get_irn_op(pred) == op_Load && get_Load_ptr(pred) == ptr &&
+ } else if (is_Load(pred) && get_Load_ptr(pred) == ptr &&
value == pred_info->projs[pn_Load_res]) {
/*
* a Store of a value after a Load -- a write after read.
*/
if (! info->projs[pn_Store_X_except]) {
DBG_OPT_WAR(store, pred);
- exchange( info->projs[pn_Store_M], mem );
+ exchange(info->projs[pn_Store_M], mem);
exchange(store, new_Bad());
reduce_adr_usage(ptr);
return DF_CHANGED;
}
}
- if (get_irn_op(pred) == op_Store) {
+ if (is_Store(pred)) {
/* check if we can pass thru this store */
ir_alias_relation rel = get_alias_relation(
current_ir_graph,
MARK_NODE(pred_info);
}
- if (get_irn_op(pred) == op_Sync) {
+ if (is_Sync(pred)) {
int i;
/* handle all Sync predecessors */
int i, n;
ir_node *store, *old_store, *ptr, *block, *phi_block, *phiM, *phiD, *exc, *projM;
ir_mode *mode;
- ir_node **inM, **inD, **stores;
+ ir_node **inM, **inD, **projMs;
int *idx;
dbg_info *db = NULL;
ldst_info_t *info;
if (n <= 0)
return 0;
- store = skip_Proj(get_Phi_pred(phi, 0));
+ /* must be only one user */
+ projM = get_Phi_pred(phi, 0);
+ if (get_irn_n_edges(projM) != 1)
+ return 0;
+
+ store = skip_Proj(projM);
old_store = store;
if (get_irn_op(store) != op_Store)
return 0;
return 0;
phi_block = get_nodes_block(phi);
- if (! block_postdominates(phi_block, block))
+ if (! block_strictly_postdominates(phi_block, block))
return 0;
/* this is the address of the store */
exc = info->exc_block;
for (i = 1; i < n; ++i) {
- ir_node *pred = skip_Proj(get_Phi_pred(phi, i));
+ ir_node *pred = get_Phi_pred(phi, i);
- if (get_irn_op(pred) != op_Store)
+ if (get_irn_n_edges(pred) != 1)
+ return 0;
+
+ pred = skip_Proj(pred);
+ if (!is_Store(pred))
return 0;
if (ptr != get_Store_ptr(pred) || mode != get_irn_mode(get_Store_value(pred)))
* Is only allowed if the predecessor blocks have only one successor.
*/
- NEW_ARR_A(ir_node *, stores, n);
+ NEW_ARR_A(ir_node *, projMs, n);
NEW_ARR_A(ir_node *, inM, n);
NEW_ARR_A(ir_node *, inD, n);
NEW_ARR_A(int, idx, n);
first because we otherwise may loose a store when exchanging its
memory Proj.
*/
- for (i = 0; i < n; ++i)
- stores[i] = skip_Proj(get_Phi_pred(phi, i));
-
- /* Prepare: Skip the memory Proj: we need this in the case some stores
- are cascaded.
- Beware: One Store might be included more than once in the stores[]
- list, so we must prevent to do the exchange more than once.
- */
- for (i = 0; i < n; ++i) {
- ir_node *store = stores[i];
- ir_node *proj_m;
-
- info = get_irn_link(store);
- proj_m = info->projs[pn_Store_M];
+ for (i = n - 1; i >= 0; --i) {
+ ir_node *store;
- if (is_Proj(proj_m) && get_Proj_pred(proj_m) == store)
- exchange(proj_m, get_Store_mem(store));
- }
+ projMs[i] = get_Phi_pred(phi, i);
+ assert(is_Proj(projMs[i]));
- /* first step: collect all inputs */
- for (i = 0; i < n; ++i) {
- ir_node *store = stores[i];
- info = get_irn_link(store);
+ store = get_Proj_pred(projMs[i]);
+ info = get_irn_link(store);
inM[i] = get_Store_mem(store);
inD[i] = get_Store_value(store);
/* third step: create a new data Phi */
phiD = new_rd_Phi(get_irn_dbg_info(phi), current_ir_graph, block, n, inD, mode);
+ /* rewire memory and kill the node */
+ for (i = n - 1; i >= 0; --i) {
+ ir_node *proj = projMs[i];
+
+ if(is_Proj(proj)) {
+ ir_node *store = get_Proj_pred(proj);
+ exchange(proj, inM[i]);
+ kill_node(store);
+ }
+ }
+
/* fourth step: create the Store */
store = new_rd_Store(db, current_ir_graph, block, phiM, ptr, phiD);
#ifdef DO_CACHEOPT
projM = new_rd_Proj(NULL, current_ir_graph, block, store, mode_M, pn_Store_M);
- info = get_ldst_info(store, wenv);
+ info = get_ldst_info(store, &wenv->obst);
info->projs[pn_Store_M] = projM;
/* fifths step: repair exception flow */
}
} /* do_load_store_optimize */
+/** A scc. */
+typedef struct scc {
+ ir_node *head; /**< the head of the list */
+} scc;
+
+/** A node entry. */
+typedef struct node_entry {
+ unsigned DFSnum; /**< the DFS number of this node */
+ unsigned low; /**< the low number of this node */
+ ir_node *header; /**< the header of this node */
+ int in_stack; /**< flag, set if the node is on the stack */
+ ir_node *next; /**< link to the next node the the same scc */
+ scc *pscc; /**< the scc of this node */
+ unsigned POnum; /**< the post order number for blocks */
+} node_entry;
+
+/** A loop entry. */
+typedef struct loop_env {
+ ir_phase ph; /**< the phase object */
+ ir_node **stack; /**< the node stack */
+ int tos; /**< tos index */
+ unsigned nextDFSnum; /**< the current DFS number */
+ unsigned POnum; /**< current post order number */
+
+ unsigned changes; /**< a bitmask of graph changes */
+} loop_env;
+
+/**
+* Gets the node_entry of a node
+*/
+static node_entry *get_irn_ne(ir_node *irn, loop_env *env) {
+ ir_phase *ph = &env->ph;
+ node_entry *e = phase_get_irn_data(&env->ph, irn);
+
+ if (! e) {
+ e = phase_alloc(ph, sizeof(*e));
+ memset(e, 0, sizeof(*e));
+ phase_set_irn_data(ph, irn, e);
+ }
+ return e;
+} /* get_irn_ne */
+
+/**
+ * Push a node onto the stack.
+ *
+ * @param env the loop environment
+ * @param n the node to push
+ */
+static void push(loop_env *env, ir_node *n) {
+ node_entry *e;
+
+ if (env->tos == ARR_LEN(env->stack)) {
+ int nlen = ARR_LEN(env->stack) * 2;
+ ARR_RESIZE(ir_node *, env->stack, nlen);
+ }
+ env->stack[env->tos++] = n;
+ e = get_irn_ne(n, env);
+ e->in_stack = 1;
+} /* push */
+
+/**
+ * pop a node from the stack
+ *
+ * @param env the loop environment
+ *
+ * @return The topmost node
+ */
+static ir_node *pop(loop_env *env) {
+ ir_node *n = env->stack[--env->tos];
+ node_entry *e = get_irn_ne(n, env);
+
+ e->in_stack = 0;
+ return n;
+} /* pop */
+
+/**
+ * Check if irn is a region constant.
+ * The block or irn must strictly dominate the header block.
+ *
+ * @param irn the node to check
+ * @param header_block the header block of the induction variable
+ */
+static int is_rc(ir_node *irn, ir_node *header_block) {
+ ir_node *block = get_nodes_block(irn);
+
+ return (block != header_block) && block_dominates(block, header_block);
+} /* is_rc */
+
+typedef struct phi_entry phi_entry;
+struct phi_entry {
+ ir_node *phi; /**< A phi with a region const memory. */
+ int pos; /**< The position of the region const memory */
+ ir_node *load; /**< the newly created load for this phi */
+ phi_entry *next;
+};
+
+/**
+ * Move loops out of loops if possible
+ */
+static void move_loads_in_loops(scc *pscc, loop_env *env) {
+ ir_node *phi, *load, *next, *other, *next_other;
+ ir_entity *ent;
+ int j;
+ phi_entry *phi_list = NULL;
+
+ /* collect all outer memories */
+ for (phi = pscc->head; phi != NULL; phi = next) {
+ node_entry *ne = get_irn_ne(phi, env);
+ next = ne->next;
+
+ /* check all memory Phi's */
+ if (! is_Phi(phi))
+ continue;
+
+ assert(get_irn_mode(phi) == mode_M && "DFS geturn non-memory Phi");
+
+ for (j = get_irn_arity(phi) - 1; j >= 0; --j) {
+ ir_node *pred = get_irn_n(phi, j);
+ node_entry *pe = get_irn_ne(pred, env);
+
+ if (pe->pscc != ne->pscc) {
+ /* not in the same SCC, is region const */
+ phi_entry *pe = phase_alloc(&env->ph, sizeof(*pe));
+
+ pe->phi = phi;
+ pe->pos = j;
+ pe->next = phi_list;
+ phi_list = pe;
+ }
+ }
+ }
+ /* no Phis no fun */
+ assert(phi_list != NULL && "DFS found a loop without Phi");
+
+ for (load = pscc->head; load; load = next) {
+ ir_mode *load_mode;
+ node_entry *ne = get_irn_ne(load, env);
+ next = ne->next;
+
+ if (is_Load(load)) {
+ ldst_info_t *info = get_irn_link(load);
+ ir_node *ptr = get_Load_ptr(load);
+
+ /* for now, we cannot handle Loads with exceptions */
+ if (info->projs[pn_Load_res] == NULL || info->projs[pn_Load_X_regular] != NULL || info->projs[pn_Load_X_except] != NULL)
+ continue;
+
+ /* for now, we can only handle Load(SymConst) */
+ if (! is_SymConst(ptr) || get_SymConst_kind(ptr) != symconst_addr_ent)
+ continue;
+ ent = get_SymConst_entity(ptr);
+ load_mode = get_Load_mode(load);
+ for (other = pscc->head; other != NULL; other = next_other) {
+ node_entry *ne = get_irn_ne(other, env);
+ next_other = ne->next;
+
+ if (is_Store(other)) {
+ ir_alias_relation rel = get_alias_relation(
+ current_ir_graph,
+ get_Store_ptr(other),
+ get_irn_mode(get_Store_value(other)),
+ ptr, load_mode);
+ /* if the might be an alias, we cannot pass this Store */
+ if (rel != no_alias)
+ break;
+ }
+ }
+ if (other == NULL) {
+ ldst_info_t *ninfo;
+ phi_entry *pe;
+ dbg_info *db;
+
+ /* for now, we cannot handle more than one input */
+ if (phi_list->next != NULL)
+ return;
+
+ /* yep, no aliasing Store found, Load can be moved */
+ DB((dbg, LEVEL_1, " Found a Load that could be moved: %+F\n", load));
+
+ db = get_irn_dbg_info(load);
+ for (pe = phi_list; pe != NULL; pe = pe->next) {
+ int pos = pe->pos;
+ ir_node *phi = pe->phi;
+ ir_node *blk = get_nodes_block(phi);
+ ir_node *pred = get_Block_cfgpred_block(blk, pos);
+ ir_node *irn, *mem;
+
+ pe->load = irn = new_rd_Load(db, current_ir_graph, pred, get_Phi_pred(phi, pos), ptr, load_mode);
+ ninfo = get_ldst_info(irn, phase_obst(&env->ph));
+
+ ninfo->projs[pn_Load_M] = mem = new_r_Proj(current_ir_graph, pred, irn, mode_M, pn_Load_M);
+ set_Phi_pred(phi, pos, mem);
+
+ ninfo->projs[pn_Load_res] = new_r_Proj(current_ir_graph, pred, irn, load_mode, pn_Load_res);
+
+ DB((dbg, LEVEL_1, " Created %+F in %+F\n", irn, pred));
+ }
+
+ /* now kill the old Load */
+ exchange(info->projs[pn_Load_M], get_Load_mem(load));
+ exchange(info->projs[pn_Load_res], ninfo->projs[pn_Load_res]);
+
+ env->changes |= DF_CHANGED;
+ }
+ }
+ }
+} /* move_loads_in_loops */
+
+/**
+ * Process a loop SCC.
+ *
+ * @param pscc the SCC
+ * @param env the loop environment
+ */
+static void process_loop(scc *pscc, loop_env *env) {
+ ir_node *irn, *next, *header = NULL;
+ node_entry *b, *h = NULL;
+ int j, only_phi, num_outside, process = 0;
+ ir_node *out_rc;
+
+ /* find the header block for this scc */
+ for (irn = pscc->head; irn; irn = next) {
+ node_entry *e = get_irn_ne(irn, env);
+ ir_node *block = get_nodes_block(irn);
+
+ next = e->next;
+ b = get_irn_ne(block, env);
+
+ if (header) {
+ if (h->POnum < b->POnum) {
+ header = block;
+ h = b;
+ }
+ }
+ else {
+ header = block;
+ h = b;
+ }
+ }
+
+ /* check if this scc contains only Phi, Loads or Stores nodes */
+ only_phi = 1;
+ num_outside = 0;
+ out_rc = NULL;
+ for (irn = pscc->head; irn; irn = next) {
+ node_entry *e = get_irn_ne(irn, env);
+
+ next = e->next;
+ switch (get_irn_opcode(irn)) {
+ case iro_Call:
+ case iro_CopyB:
+ /* cannot handle Calls or CopyB yet */
+ goto fail;
+ case iro_Load:
+ process = 1;
+ if (get_Load_volatility(irn) == volatility_is_volatile) {
+ /* cannot handle loops with volatile Loads */
+ goto fail;
+ }
+ only_phi = 0;
+ break;
+ case iro_Store:
+ if (get_Store_volatility(irn) == volatility_is_volatile) {
+ /* cannot handle loops with volatile Stores */
+ goto fail;
+ }
+ only_phi = 0;
+ break;
+ default:
+ only_phi = 0;
+ break;
+ case iro_Phi:
+ for (j = get_irn_arity(irn) - 1; j >= 0; --j) {
+ ir_node *pred = get_irn_n(irn, j);
+ node_entry *pe = get_irn_ne(pred, env);
+
+ if (pe->pscc != e->pscc) {
+ /* not in the same SCC, must be a region const */
+ if (! is_rc(pred, header)) {
+ /* not a memory loop */
+ goto fail;
+ }
+ if (! out_rc) {
+ out_rc = pred;
+ ++num_outside;
+ } else if (out_rc != pred) {
+ ++num_outside;
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (! process)
+ goto fail;
+
+ /* found a memory loop */
+ DB((dbg, LEVEL_2, " Found a memory loop:\n "));
+ if (only_phi && num_outside == 1) {
+ /* a phi cycle with only one real predecessor can be collapsed */
+ DB((dbg, LEVEL_2, " Found an USELESS Phi cycle:\n "));
+
+ for (irn = pscc->head; irn; irn = next) {
+ node_entry *e = get_irn_ne(irn, env);
+ next = e->next;
+ e->header = NULL;
+ exchange(irn, out_rc);
+ }
+ env->changes |= DF_CHANGED;
+ return;
+ }
+
+ /* set the header for every node in this scc */
+ for (irn = pscc->head; irn; irn = next) {
+ node_entry *e = get_irn_ne(irn, env);
+ e->header = header;
+ next = e->next;
+ DB((dbg, LEVEL_2, " %+F,", irn));
+ }
+ DB((dbg, LEVEL_2, "\n"));
+
+ move_loads_in_loops(pscc, env);
+
+fail:
+ ;
+} /* process_loop */
+
+/**
+ * Process a SCC.
+ *
+ * @param pscc the SCC
+ * @param env the loop environment
+ */
+static void process_scc(scc *pscc, loop_env *env) {
+ ir_node *head = pscc->head;
+ node_entry *e = get_irn_ne(head, env);
+
+#ifdef DEBUG_libfirm
+ {
+ ir_node *irn, *next;
+
+ DB((dbg, LEVEL_4, " SCC at %p:\n ", pscc));
+ for (irn = pscc->head; irn; irn = next) {
+ node_entry *e = get_irn_ne(irn, env);
+
+ next = e->next;
+
+ DB((dbg, LEVEL_4, " %+F,", irn));
+ }
+ DB((dbg, LEVEL_4, "\n"));
+ }
+#endif
+
+ if (e->next != NULL) {
+ /* this SCC has more than one member */
+ process_loop(pscc, env);
+ }
+} /* process_scc */
+
+/**
+ * Do Tarjan's SCC algorithm and drive load/store optimization.
+ *
+ * @param irn start at this node
+ * @param env the loop environment
+ */
+static void dfs(ir_node *irn, loop_env *env)
+{
+ int i, n;
+ node_entry *node = get_irn_ne(irn, env);
+
+ mark_irn_visited(irn);
+
+ node->DFSnum = env->nextDFSnum++;
+ node->low = node->DFSnum;
+ push(env, irn);
+
+ /* handle preds */
+ if (is_Phi(irn) || is_Sync(irn)) {
+ n = get_irn_arity(irn);
+ for (i = 0; i < n; ++i) {
+ ir_node *pred = get_irn_n(irn, i);
+ node_entry *o = get_irn_ne(pred, env);
+
+ if (irn_not_visited(pred)) {
+ dfs(pred, env);
+ node->low = MIN(node->low, o->low);
+ }
+ if (o->DFSnum < node->DFSnum && o->in_stack)
+ node->low = MIN(o->DFSnum, node->low);
+ }
+ } else if (is_fragile_op(irn)) {
+ ir_node *pred = get_fragile_op_mem(irn);
+ node_entry *o = get_irn_ne(pred, env);
+
+ if (irn_not_visited(pred)) {
+ dfs(pred, env);
+ node->low = MIN(node->low, o->low);
+ }
+ if (o->DFSnum < node->DFSnum && o->in_stack)
+ node->low = MIN(o->DFSnum, node->low);
+ } else if (is_Proj(irn)) {
+ ir_node *pred = get_Proj_pred(irn);
+ node_entry *o = get_irn_ne(pred, env);
+
+ if (irn_not_visited(pred)) {
+ dfs(pred, env);
+ node->low = MIN(node->low, o->low);
+ }
+ if (o->DFSnum < node->DFSnum && o->in_stack)
+ node->low = MIN(o->DFSnum, node->low);
+ }
+ else {
+ /* IGNORE predecessors */
+ }
+
+ if (node->low == node->DFSnum) {
+ scc *pscc = phase_alloc(&env->ph, sizeof(*pscc));
+ ir_node *x;
+
+ pscc->head = NULL;
+ do {
+ node_entry *e;
+
+ x = pop(env);
+ e = get_irn_ne(x, env);
+ e->pscc = pscc;
+ e->next = pscc->head;
+ pscc->head = x;
+ } while (x != irn);
+
+ process_scc(pscc, env);
+ }
+} /* dfs */
+
+/**
+ * Do the DFS on the memory edges a graph.
+ *
+ * @param irg the graph to process
+ * @param env the loop environment
+ */
+static void do_dfs(ir_graph *irg, loop_env *env) {
+ ir_graph *rem = current_ir_graph;
+ ir_node *endblk, *end;
+ int i;
+
+ current_ir_graph = irg;
+ inc_irg_visited(irg);
+
+ /* visit all memory nodes */
+ endblk = get_irg_end_block(irg);
+ for (i = get_Block_n_cfgpreds(endblk) - 1; i >= 0; --i) {
+ ir_node *pred = get_Block_cfgpred(endblk, i);
+
+ if (is_Return(pred))
+ dfs(get_Return_mem(pred), env);
+ else if (is_Raise(pred))
+ dfs(get_Raise_mem(pred), env);
+ else if (is_fragile_op(pred))
+ dfs(get_fragile_op_mem(pred), env);
+ else {
+ assert(0 && "Unknown EndBlock predecessor");
+ }
+ }
+
+ /* visit the keep-alives */
+ end = get_irg_end(irg);
+ for (i = get_End_n_keepalives(end) - 1; i >= 0; --i) {
+ ir_node *ka = get_End_keepalive(end, i);
+
+ if (is_Phi(ka) && irn_not_visited(ka))
+ dfs(ka, env);
+ }
+ current_ir_graph = rem;
+} /* do_dfs */
+
+/**
+ * Initialize new phase data. We do this always explicit, so return NULL here
+ */
+static void *init_loop_data(ir_phase *ph, ir_node *irn, void *data) {
+ (void)ph;
+ (void)irn;
+ (void)data;
+ return NULL;
+} /* init_loop_data */
+
+/**
+ * Optimize Loads/Stores in loops.
+ *
+ * @param irg the graph
+ */
+static int optimize_loops(ir_graph *irg) {
+ loop_env env;
+
+ env.stack = NEW_ARR_F(ir_node *, 128);
+ env.tos = 0;
+ env.nextDFSnum = 0;
+ env.POnum = 0;
+ env.changes = 0;
+ phase_init(&env.ph, "ldstopt", irg, PHASE_DEFAULT_GROWTH, init_loop_data, NULL);
+
+ /* calculate the SCC's and drive loop optimization. */
+ do_dfs(irg, &env);
+
+ DEL_ARR_F(env.stack);
+ phase_free(&env.ph);
+
+ return env.changes;
+} /* optimize_loops */
+
/*
* do the load store optimization
*/
void optimize_load_store(ir_graph *irg) {
walk_env_t env;
+ FIRM_DBG_REGISTER(dbg, "firm.opt.ldstopt");
+ firm_dbg_set_mask(dbg, SET_LEVEL_4);
+
assert(get_irg_phase_state(irg) != phase_building);
assert(get_irg_pinned(irg) != op_pin_state_floats &&
"LoadStore optimization needs pinned graph");
if (! get_opt_redundant_loadstore())
return;
+ /* we need landing pads */
+ remove_critical_cf_edges(irg);
+
edges_assure(irg);
+ /* loop optimizations need dominators ... */
+ assure_doms(irg);
+
/* for Phi optimization post-dominators are needed ... */
assure_postdoms(irg);
/* now we have collected enough information, optimize */
irg_walk_graph(irg, NULL, do_load_store_optimize, &env);
+ env.changes |= optimize_loops(irg);
+
obstack_free(&env.obst, NULL);
/* Handle graph state */
if (env.changes) {
- if (get_irg_outs_state(irg) == outs_consistent)
- set_irg_outs_inconsistent(irg);
+ set_irg_outs_inconsistent(irg);
}
if (env.changes & CF_CHANGED) {