2 * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
4 * This file is part of libFirm.
6 * This file may be distributed and/or modified under the terms of the
7 * GNU General Public License version 2 as published by the Free Software
8 * Foundation and appearing in the file LICENSE.GPL included in the
9 * packaging of this file.
11 * Licensees holding valid libFirm Professional Edition licenses may use
12 * this file in accordance with the libFirm Commercial License.
13 * Agreement provided with the Software.
15 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * @brief Dataflow driven Load/Store optimizations, uses some ideas from
24 * @author Michael Beck
39 #include "irnodemap.h"
40 #include "raw_bitset.h"
45 * Mapping an address to an densed ID.
47 typedef struct address_entry_t {
48 unsigned id; /**< The ID */
55 FLAG_KILL_LOADS = 1, /**< KILL all Loads */
56 FLAG_KILL_STORES = 2, /**< KILL all Stores */
57 FLAG_KILLED_NODE = 4, /**< this node was killed */
58 FLAG_EXCEPTION = 8, /**< this node has exception flow */
59 FLAG_IGNORE = 16, /**< ignore this node (volatile or other) */
60 /** this memop KILLS all addresses */
61 FLAG_KILL_ALL = FLAG_KILL_LOADS|FLAG_KILL_STORES
65 * A value: This represents a value stored at a given address in
66 * memory. Do not confuse with values from value numbering.
68 typedef struct value_t value_t;
70 ir_node *address; /**< the address of this value */
71 ir_node *value; /**< the value itself */
72 ir_mode *mode; /**< the mode of the value */
73 unsigned id; /**< address id */
77 * A memop describes an memory-related operation.
78 * These are Loads/Store and all other ops that might modify
79 * memory (Calls, CopyB) or causing exceptions.
81 typedef struct memop_t memop_t;
83 value_t value; /**< the value of this memop: only defined for Load/Store */
84 ir_node *node; /**< the memory op itself */
85 ir_node *mem; /**< the memory FROM this node */
86 ir_node *replace; /**< the replacement node if this memop is replaced */
87 memop_t *next; /**< links to the next memory op in the block in forward order. */
88 memop_t *prev; /**< links to the previous memory op in the block in forward order. */
89 unsigned flags; /**< memop flags */
93 * Additional data for every basic block.
95 typedef struct block_t block_t;
97 memop_t *memop_forward; /**< topologically sorted list of memory ops in this block */
98 memop_t *memop_backward; /**< last memop in the list */
99 unsigned *avail_out; /**< out-set of available addresses */
100 memop_t **id_2_memop_avail; /**< maps avail address ids to memops */
101 unsigned *anticL_in; /**< in-set of anticipated Load addresses */
102 memop_t **id_2_memop; /**< maps address ids to memops */
103 ir_node *block; /**< the associated block */
104 block_t *forward_next; /**< next block entry for forward iteration */
105 block_t *backward_next; /**< next block entry for backward iteration */
106 memop_t *avail; /**< used locally for the avail map */
109 #define get_block_entry(block) ((block_t *)get_irn_link(block))
112 * Metadata for this pass.
114 typedef struct ldst_env_t {
115 struct obstack obst; /**< obstack for temporary data */
116 ir_nodemap_t adr_map; /**< Map addresses to */
117 block_t *forward; /**< Inverse post-order list of all blocks Start->End */
118 block_t *backward; /**< Inverse post-order list of all blocks End->Start */
119 ir_node *start_bl; /**< start block of the current graph */
120 unsigned *curr_set; /**< current set of addresses */
121 unsigned curr_adr_id; /**< number for address mapping */
122 unsigned n_mem_ops; /**< number of memory operations (Loads/Stores) */
123 unsigned rbs_size; /**< size of all bitsets in bytes */
124 int changed; /**< Flags for changed graph state */
129 static firm_dbg_module_t *dbg;
131 /* the one and only environment */
135 * Dumps the block list.
137 * @param ldst environment
139 static dump_block_list(ldst_env *env) {
144 for (entry = env->forward; entry != NULL; entry = entry->forward_next) {
145 DB((dbg, LEVEL_2, "%+F {", entry->block));
148 for (op = entry->memop_forward; op != NULL; op = op->next) {
150 DB((dbg, LEVEL_2, "\n\t"));
151 } DB((dbg, LEVEL_2, "%+F", op->node));
152 if ((op->flags & FLAG_KILL_ALL) == FLAG_KILL_ALL)
153 DB((dbg, LEVEL_2, "X"));
154 else if (op->flags & FLAG_KILL_LOADS)
155 DB((dbg, LEVEL_2, "L"));
156 else if (op->flags & FLAG_KILL_STORES)
157 DB((dbg, LEVEL_2, "S"));
158 DB((dbg, LEVEL_2, ", "));
162 DB((dbg, LEVEL_2, "\n}\n\n"));
167 * Dumps the current set.
169 * @param bl current block
170 * @param s name of the set
172 static void dump_curr(block_t *bl, const char *s) {
174 unsigned end = env.n_mem_ops * 2 - 1;
177 DB((dbg, LEVEL_2, "%s[%+F] = {", s, bl->block));
179 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
180 memop_t *op = bl->id_2_memop[pos];
183 DB((dbg, LEVEL_2, "\n\t"));
185 DB((dbg, LEVEL_2, "<%+F, %+F>, ", op->value.address, op->value.value));
188 DB((dbg, LEVEL_2, "\n}\n"));
192 #define dump_block_list()
193 #define dump_curr(bl, s)
194 #endif /* DEBUG_libfirm */
197 * Walk over the memory edges from definition to users.
199 * @param irn start node
200 * @param pre pre walker function
201 * @param post post walker function
202 * @param ctx context parameter for the walker functions
204 static walk_memory(ir_node *irn, irg_walk_func *pre, irg_walk_func *post, void *ctx) {
208 mark_irn_visited(irn);
213 mode = get_irn_mode(irn);
214 if (mode == mode_M) {
215 /* every successor uses memory */
216 for (i = get_irn_n_outs(irn) - 1; i >= 0; --i) {
217 ir_node *succ = get_irn_out(irn, i);
219 if (! irn_visited(succ))
220 walk_memory(succ, pre, post, ctx);
222 } else if (mode == mode_T) {
223 /* only some Proj's uses memory */
224 for (i = get_irn_n_outs(irn) - 1; i >= 0; --i) {
225 ir_node *proj = get_irn_out(irn, i);
227 if (get_irn_mode(proj) == mode_M && ! irn_visited(proj))
228 walk_memory(proj, pre, post, ctx);
236 * Walks over all memory nodes of a graph.
239 * @param pre pre walker function
240 * @param post post walker function
241 * @param ctx context parameter for the walker functions
243 static void walk_memory_irg(ir_graph *irg, irg_walk_func pre, irg_walk_func post, void *ctx) {
244 inc_irg_visited(irg);
246 ir_reserve_resources(irg, IR_RESOURCE_IRN_VISITED);
249 * there are two possible sources for memory: initial_mem and nomem
250 * we ignore nomem as this should NOT change the memory
252 walk_memory(get_irg_initial_mem(irg), pre, post, ctx);
254 ir_free_resources(irg, IR_RESOURCE_IRN_VISITED);
258 * Block walker: allocate an block entry for every block.
260 static void prepare_blocks(ir_node *block, void *ctx) {
261 block_t *entry = obstack_alloc(&env.obst, sizeof(*entry));
265 entry->memop_forward = NULL;
266 entry->memop_backward = NULL;
267 entry->avail_out = NULL;
268 entry->id_2_memop_avail = NULL;
269 entry->anticL_in = NULL;
270 entry->id_2_memop = NULL;
271 entry->block = block;
272 entry->forward_next = env.forward;
273 entry->backward_next = NULL;
275 set_irn_link(block, entry);
277 /* create the list in inverse order */
282 * Block walker: create backward links for the memops of a block.
284 static void collect_backward(ir_node *block, void *ctx) {
285 block_t *entry = get_block_entry(block);
288 entry->backward_next = env.backward;
290 /* create the list in inverse order */
291 env.backward = entry;
293 /* create backward links for all memory ops */
295 for (op = entry->memop_forward; op != NULL; op = op->next) {
299 entry->memop_backward = last;
305 * @param irn the IR-node representing the memop
307 static memop_t *alloc_memop(ir_node *irn) {
308 memop_t *m = obstack_alloc(&env.obst, sizeof(*m));
310 m->value.address = NULL;
311 m->value.value = NULL;
312 m->value.mode = NULL;
323 * Register an address and allocate an ID for it.
325 * @param adr the IR-node representing the address
327 static unsigned register_address(ir_node *adr) {
328 address_entry *entry = ir_nodemap_get(&env.adr_map, adr);
332 entry = obstack_alloc(&env.obst, sizeof(*entry));
334 entry->id = env.curr_adr_id++;
335 ir_nodemap_insert(&env.adr_map, adr, entry);
341 * Return the memory properties of a call node.
343 * @param call the call node
345 * return a bitset of mtp_property_const and mtp_property_pure
347 static unsigned get_Call_memory_properties(ir_node *call) {
348 ir_type *call_tp = get_Call_type(call);
349 unsigned prop = get_method_additional_properties(call_tp);
351 /* check first the call type */
352 if ((prop & (mtp_property_const|mtp_property_pure)) == 0) {
353 /* try the called entity */
354 ir_node *ptr = get_Call_ptr(call);
356 if (is_Global(ptr)) {
357 ir_entity *ent = get_Global_entity(ptr);
359 prop = get_entity_additional_properties(ent);
362 return prop & (mtp_property_const|mtp_property_pure);
366 * Update a memop for a Load.
370 static void update_Load_memop(memop_t *m) {
372 ir_node *load = m->node;
373 ir_node *adr = get_Load_ptr(load);
375 if (get_Load_volatility(load) == volatility_is_volatile)
376 m->flags |= FLAG_IGNORE;
378 m->value.address = adr;
380 for (i = get_irn_n_outs(load) - 1; i >= 0; --i) {
381 ir_node *proj = get_irn_out(load, i);
383 switch (get_Proj_proj(proj)) {
385 m->value.value = proj;
386 m->value.mode = get_irn_mode(proj);
388 case pn_Load_X_except:
389 m->flags |= FLAG_EXCEPTION;
397 if (m->value.value != NULL && !(m->flags & FLAG_IGNORE)) {
398 /* only create an address if this node is NOT killed immediately or ignored */
399 m->value.id = register_address(adr);
402 /* no user, KILL it */
403 m->flags |= FLAG_KILLED_NODE;
408 * Update a memop for a Store.
412 static void update_Store_memop(memop_t *m) {
414 ir_node *store = m->node;
415 ir_node *adr = get_Store_ptr(store);
417 if (get_Store_volatility(store) == volatility_is_volatile) {
418 m->flags |= FLAG_IGNORE;
420 /* only create an address if this node is NOT ignored */
421 m->value.id = register_address(adr);
425 m->value.address = adr;
427 for (i = get_irn_n_outs(store) - 1; i >= 0; --i) {
428 ir_node *proj = get_irn_out(store, i);
430 switch (get_Proj_proj(proj)) {
431 case pn_Store_X_except:
432 m->flags |= FLAG_EXCEPTION;
439 m->value.value = get_Store_value(store);
440 m->value.mode = get_irn_mode(m->value.value);
444 * Update a memop for a Call.
448 static void update_Call_memop(memop_t *m) {
449 ir_node *call = m->node;
450 unsigned prop = get_Call_memory_properties(call);
453 if (prop & mtp_property_const) {
454 /* A constant call did NOT use memory at all, we
455 can kick it from the list. */
456 } else if (prop & mtp_property_pure) {
457 /* pure calls READ memory */
458 m->flags = FLAG_KILL_STORES;
460 m->flags = FLAG_KILL_ALL;
462 for (i = get_irn_n_outs(call) - 1; i >= 0; --i) {
463 ir_node *proj = get_irn_out(call, i);
465 switch (get_Proj_proj(proj)) {
466 case pn_Call_X_except:
467 m->flags |= FLAG_EXCEPTION;
469 case pn_Call_M_regular:
477 * Update a memop for a Div/Mod/Quot/DivMod.
481 static void update_DivOp_memop(memop_t *m) {
482 ir_node *div = m->node;
485 for (i = get_irn_n_outs(div) - 1; i >= 0; --i) {
486 ir_node *proj = get_irn_out(div, i);
488 switch (get_Proj_proj(proj)) {
489 case pn_Generic_X_except:
490 m->flags |= FLAG_EXCEPTION;
492 case pn_Generic_M_regular:
500 * Memory walker: collect all memory ops and build topological lists.
502 static void collect_memops(ir_node *irn, void *ctx) {
508 /* we can safely ignore ProjM's except the initial memory */
509 if (irn != get_irg_initial_mem(current_ir_graph))
513 op = alloc_memop(irn);
514 block = get_nodes_block(irn);
515 entry = get_block_entry(block);
518 /* Phis must be always placed first */
519 op->next = entry->memop_forward;
520 entry->memop_forward = op;
521 if (entry->memop_backward == NULL)
522 entry->memop_backward = op;
524 switch (get_irn_opcode(irn)) {
526 update_Load_memop(op);
529 update_Store_memop(op);
532 update_Call_memop(op);
544 /* we can those to find the memory edge */
550 update_DivOp_memop(op);
554 /* TODO: handle some builtins */
556 /* unsupported operation */
557 op->flags = FLAG_KILL_ALL;
561 /* all other should be placed last */
562 if (entry->memop_backward == NULL) {
563 entry->memop_forward = entry->memop_backward = op;
565 entry->memop_backward->next = op;
566 entry->memop_backward = op;
572 * Find an address in the current set.
574 * @param bl the block
575 * @param value the value to be searched for
577 static memop_t *find_address(const block_t *bl, const value_t *value) {
578 if (rbitset_is_set(env.curr_set, value->id)) {
579 memop_t *res = bl->id_2_memop[value->id];
581 if (res->value.mode == value->mode)
583 /* allow hidden casts */
584 if (get_mode_arithmetic(res->value.mode) == irma_twos_complement &&
585 get_mode_arithmetic(value->mode) == irma_twos_complement &&
586 get_mode_size_bits(res->value.mode) == get_mode_size_bits(value->mode))
593 * Find an address in the avail_out set.
595 * @param bl the block
596 * @param value the value to be searched for
598 static memop_t *find_address_avail(const block_t *bl, const value_t *value) {
599 if (rbitset_is_set(bl->avail_out, value->id)) {
600 memop_t *res = bl->id_2_memop_avail[value->id];
602 if (res->value.mode == value->mode)
604 /* allow hidden casts */
605 if (get_mode_arithmetic(res->value.mode) == irma_twos_complement &&
606 get_mode_arithmetic(value->mode) == irma_twos_complement &&
607 get_mode_size_bits(res->value.mode) == get_mode_size_bits(value->mode))
614 * Kill all Loads from the current set.
616 * @param bl the current block
618 static void kill_all_loads(block_t *bl) {
620 unsigned end = env.n_mem_ops * 2 - 1;
622 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
623 memop_t *op = bl->id_2_memop[pos];
625 if (! is_Store(op->node))
626 rbitset_clear(env.curr_set, pos);
631 * Kill all Stores from the current set.
633 * @param bl the current block
635 static void kill_all_stores(block_t *bl) {
637 unsigned end = env.n_mem_ops * 2 - 1;
639 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
640 memop_t *op = bl->id_2_memop[pos];
642 if (is_Store(op->node))
643 rbitset_clear(env.curr_set, pos);
648 * Kill all addresses from the current set.
650 static void kill_all(void) {
651 rbitset_clear_all(env.curr_set, env.rbs_size);
654 rbitset_set(env.curr_set, env.rbs_size - 1);
659 * Kill Stores that are not alias free due to a Load value from the current set.
661 * @param bl the block
662 * @param value the Load value
664 static void kill_stores(block_t *bl, const value_t *value) {
666 unsigned end = env.n_mem_ops * 2 - 1;
668 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
669 memop_t *op = bl->id_2_memop[pos];
671 if (is_Store(op->node)) {
672 if (ir_no_alias != get_alias_relation(current_ir_graph, value->address, value->mode,
673 op->value.address, op->value.mode)) {
674 rbitset_clear(env.curr_set, pos);
675 bl->id_2_memop[pos] = NULL;
682 * Kill memops that are not alias free due to a Store value from the current set.
684 * @param bl the block
685 * @param value the Store value
687 static void kill_memops(block_t *bl, const value_t *value) {
689 unsigned end = env.n_mem_ops * 2 - 1;
691 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
692 memop_t *op = bl->id_2_memop[pos];
694 if (ir_no_alias != get_alias_relation(current_ir_graph, value->address, value->mode,
695 op->value.address, op->value.mode)) {
696 rbitset_clear(env.curr_set, pos);
697 bl->id_2_memop[pos] = NULL;
703 * Add the value of a memop to the current set.
705 * @param bl the block
706 * @param op the memory op
708 static void add_memop(block_t *bl, memop_t *op) {
709 rbitset_set(env.curr_set, op->value.id);
710 bl->id_2_memop[op->value.id] = op;
714 * Add the value of a memop to the avail_out set.
716 * @param bl the block
717 * @param op the memory op
719 static void add_memop_avail(block_t *bl, memop_t *op) {
720 rbitset_set(bl->avail_out, op->value.id);
721 bl->id_2_memop_avail[op->value.id] = op;
725 * find a definition for a value in the given block.
727 * @param block the block
728 * @param value the value
730 * @return the definition
732 static ir_node *find_definition(ir_node *block, const value_t *value) {
733 ir_node *def_bl = get_nodes_block(value->value);
738 if (block_dominates(def_bl, block))
741 /* no: we need a new Phi */
742 n = get_Block_n_cfgpreds(block);
745 block = get_Block_cfgpred_block(block, 0);
746 n = get_Block_n_cfgpreds(block);
747 mop = find_address(get_block_entry(block), value);
753 NEW_ARR_A(ir_node *, in, n);
754 for (i = n - 1; i >= 0; --i) {
755 ir_node *pred_bl = get_Block_cfgpred_block(block, i);
756 mop = find_address(get_block_entry(pred_bl), value);
757 in[i] = find_definition(pred_bl, &mop->value);
760 phi = new_r_Phi(current_ir_graph, block, n, in, value->mode);
761 DB((dbg, LEVEL_2, "Created new Phi %+F for value %+F in block %+F\n", phi, value->value, block));
766 * Mark a Load memop to be replace by a definition
768 * @param op the Load memop
770 static void mark_replace_load(memop_t *op, ir_node *def) {
772 op->flags |= FLAG_KILLED_NODE;
777 * Mark a Store memop to be removed.
779 * @param op the Store memop
781 static void mark_remove_store(memop_t *op) {
782 op->flags |= FLAG_KILLED_NODE;
786 #define BYTE_SIZE(x) (((x) + 7) >> 3)
789 * Do forward dataflow analysis on a given block to calculate the avail_out set.
791 * @param bl the block
793 * @return non-zero if the set has changed since last iteration
795 static int forward_avail(block_t *bl) {
799 ir_node *pred = get_Block_cfgpred_block(bl->block, 0);
800 block_t *pred_bl = get_block_entry(pred);
802 memcpy(env.curr_set, pred_bl->avail_out, BYTE_SIZE(env.rbs_size));
804 n = get_Block_n_cfgpreds(bl->block);
808 /* more than one predecessors, calculate the join */
809 for (i = n - 1; i > 0; --i) {
810 ir_node *pred = get_Block_cfgpred_block(bl->block, i);
811 block_t *pred_bl = get_block_entry(pred);
813 rbitset_and(env.curr_set, pred_bl->avail_out, env.rbs_size);
816 /* sure that all values are in the map */
817 for (pos = env.rbs_size - 1; pos >= 0; --pos) {
818 if (! rbitset_is_set(env.curr_set, pos))
819 bl->id_2_memop[pos] = NULL;
821 for (i = n - 1; i >= 0; --i) {
822 ir_node *pred = get_Block_cfgpred_block(bl->block, i);
823 block_t *pred_bl = get_block_entry(pred);
825 if (pred_bl->id_2_memop[pos] != NULL) {
826 bl->id_2_memop[pos] = pred_bl->id_2_memop[pos];
833 /* only one predecessor, simply copy the map */
834 memcpy(bl->id_2_memop, pred_bl->id_2_memop, env.rbs_size * sizeof(bl->id_2_memop[0]));
837 dump_curr(bl, "Avail_in");
839 for (op = bl->memop_forward; op != NULL; op = op->next) {
840 switch (get_irn_opcode(op->node)) {
848 if (! (op->flags & (FLAG_KILLED_NODE|FLAG_IGNORE))) {
849 /* do we have this already? */
850 memop_t *other = find_address(bl, &op->value);
852 if (is_Store(other->node)) {
854 DB((dbg, LEVEL_1, "RAW %+F %+F\n", op->node, other->node));
857 DB((dbg, LEVEL_1, "RAR %+F %+F\n", op->node, other->node));
859 def = find_definition(bl->block, &other->value);
860 mark_replace_load(op, def);
863 kill_stores(bl, &op->value);
869 if (! (op->flags & (FLAG_KILLED_NODE|FLAG_IGNORE))) {
870 /* do we have this store already */
871 memop_t *other = find_address(bl, &op->value);
873 if (is_Store(other->node)) {
875 DB((dbg, LEVEL_1, "WAW %+F %+F\n", op->node, other->node));
876 mark_remove_store(other);
877 /* FIXME: a Load might be get freed due to this killed store */
878 } else if (other->value.value == op->value.value) {
880 DB((dbg, LEVEL_1, "WAR %+F %+F\n", op->node, other->node));
881 mark_remove_store(op);
883 /* we overwrite the value that was loaded */
888 kill_memops(bl, &op->value);
894 switch (op->flags & (FLAG_KILL_LOADS|FLAG_KILL_STORES)) {
895 case FLAG_KILL_LOADS|FLAG_KILL_STORES:
898 case FLAG_KILL_LOADS:
901 case FLAG_KILL_STORES:
909 dump_curr(bl, "Avail_out");
910 if (memcmp(bl->avail_out, env.curr_set, BYTE_SIZE(env.rbs_size)) != 0) {
912 memcpy(bl->avail_out, env.curr_set, env.rbs_size);
919 * Do backward dataflow analysis on a given block to calculate the antic set
920 * of Loaded addresses.
922 * @param bl the block
924 * @return non-zero if the set has changed since last iteration
926 static int backward_antic(block_t *bl) {
928 ir_node *succ = get_Block_cfg_out(bl->block, 0);
929 block_t *succ_bl = get_block_entry(succ);
932 unsigned end = env.n_mem_ops * 2 - 1;
934 memcpy(env.curr_set, succ_bl->anticL_in, BYTE_SIZE(env.rbs_size));
935 memcpy(bl->id_2_memop, succ_bl->id_2_memop, env.rbs_size * sizeof(bl->id_2_memop[0]));
937 n = get_Block_n_cfg_outs(bl->block);
941 for (i = n - 1; i > 0; --i) {
942 ir_node *succ = get_Block_cfg_out(bl->block, i);
943 block_t *succ_bl = get_block_entry(succ);
945 rbitset_and(env.curr_set, succ_bl->anticL_in, env.rbs_size);
950 /* cleanup: kill those Loads which address is not available */
951 for (pos = rbitset_next(env.curr_set, pos, 1); pos != end; pos = rbitset_next(env.curr_set, pos + 1, 1)) {
952 memop_t *op = succ_bl->id_2_memop[pos];
953 ir_node *ptr = get_Load_ptr(op->node);
954 ir_node *ptr_bl = get_nodes_block(ptr);
956 if (!block_dominates(ptr_bl, bl->block))
957 rbitset_clear(env.curr_set, pos);
961 dump_curr(bl, "AnticL_out");
963 for (op = bl->memop_backward; op != NULL; op = op->prev) {
964 switch (get_irn_opcode(op->node)) {
972 if (! (op->flags & (FLAG_KILLED_NODE|FLAG_IGNORE))) {
978 if (! (op->flags & (FLAG_KILLED_NODE|FLAG_IGNORE))) {
979 /* a Store: check which memops must be killed */
980 kill_memops(bl, &op->value);
984 switch (op->flags & (FLAG_KILL_LOADS|FLAG_KILL_STORES)) {
985 case FLAG_KILL_LOADS|FLAG_KILL_STORES:
988 case FLAG_KILL_LOADS:
991 case FLAG_KILL_STORES:
999 dump_curr(bl, "AnticL_in");
1000 if (memcmp(bl->anticL_in, env.curr_set, BYTE_SIZE(env.rbs_size)) != 0) {
1002 memcpy(bl->anticL_in, env.curr_set, env.rbs_size);
1009 * Replace a Load memop by a already known value.
1011 * @param op the Load memop
1013 static void replace_load(memop_t *op) {
1014 ir_node *load = op->node;
1015 ir_node *def = skip_Id(op->replace);
1020 DB((dbg, LEVEL_1, "Replacing %+F by definition %+F\n", load, def));
1022 if (op->flags & FLAG_EXCEPTION) {
1023 /* bad: this node is unused and executed for exception only */
1024 DB((dbg, LEVEL_1, "Unused %+F executed for exception only ...\n", load));
1027 DB((dbg, LEVEL_1, "Killing unused %+F\n", load));
1029 for (i = get_irn_n_outs(load) - 1; i >= 0; --i) {
1030 ir_node *proj = get_irn_out(load, i);
1032 switch (get_Proj_proj(proj)) {
1034 exchange(proj, get_Load_mem(load));
1037 mode = get_irn_mode(proj);
1038 if (get_irn_mode(def) != mode) {
1040 dbg_info *db = get_irn_dbg_info(load);
1041 ir_node *block = get_nodes_block(proj);
1042 def = new_rd_Conv(db, current_ir_graph, block, def, mode);
1044 exchange(proj, def);
1046 case pn_Load_X_except:
1047 exchange(proj, new_Bad());
1049 case pn_Load_X_regular:
1050 exchange(proj, new_r_Jmp(current_ir_graph, get_nodes_block(load)));
1053 panic("Unknown Proj from Load");
1059 * Remove a Store memop.
1061 * @param op the Store memop
1063 static void remove_store(memop_t *op) {
1064 ir_node *store = op->node;
1067 DB((dbg, LEVEL_1, "Removing %+F\n", store));
1068 for (i = get_irn_n_outs(store) - 1; i >= 0; --i) {
1069 ir_node *proj = get_irn_out(store, i);
1071 switch (get_Proj_proj(proj)) {
1073 exchange(proj, get_Store_mem(store));
1075 case pn_Store_X_except:
1076 exchange(proj, new_Bad());
1078 case pn_Store_X_regular:
1079 exchange(proj, new_r_Jmp(current_ir_graph, get_nodes_block(store)));
1082 panic("Unknown Proj from Store");
1089 * Do all necessary replacements for a given block.
1091 * @param bl the block
1093 static void do_replacements(block_t *bl) {
1096 for (op = bl->memop_forward; op != NULL; op = op->next) {
1097 if (op->flags & FLAG_KILLED_NODE) {
1098 switch (get_irn_opcode(op->node)) {
1111 * Calculate the Avail_out sets for all basic blocks.
1113 static void calcAvail(void) {
1117 /* calculate avail_out */
1118 DB((dbg, LEVEL_2, "Calculate Avail_out\n"));
1121 DB((dbg, LEVEL_2, "Iteration %d:\n=========\n", i));
1125 /* over all blocks in reverse post order, skip the start block */
1126 for (bl = env.forward->forward_next; bl != NULL; bl = bl->forward_next) {
1127 need_iter |= forward_avail(bl);
1130 } while (need_iter);
1132 /* copy the content of the id_2_memop map into the id_2_memop_avail map
1133 as it must be preserved for later use */
1134 for (bl = env.forward->forward_next; bl != NULL; bl = bl->forward_next) {
1135 memop_t **t = bl->id_2_memop_avail;
1137 bl->id_2_memop_avail = bl->id_2_memop;
1138 bl->id_2_memop = bl->id_2_memop_avail;
1140 DB((dbg, LEVEL_2, "Get avail set after %d iterations\n\n", i));
1144 * Calculate the Antic_in sets for all basic blocks.
1146 static void calcAntic(void) {
1149 /* calculate antic_out */
1150 DB((dbg, LEVEL_2, "Calculate Antic_in\n"));
1155 DB((dbg, LEVEL_2, "Iteration %d:\n=========\n", i));
1159 /* over all blocks in reverse post order */
1160 for (bl = env.backward->backward_next; bl != NULL; bl = bl->backward_next) {
1161 need_iter |= backward_antic(bl);
1164 } while (need_iter);
1165 DB((dbg, LEVEL_2, "Get anticipated Load set after %d iterations\n", i));
1169 * Return the node representing the last memory in a block.
1171 * @param bl the block
1173 static ir_node *find_first_memory(block_t *bl) {
1175 if (bl->memop_forward != NULL) {
1176 return bl->memop_forward->node;
1178 /* if there is NO memory in this block, go to the post dominator */
1179 bl = get_block_entry(get_Block_ipostdom(bl->block));
1184 * Return the node representing the last memory in a block.
1186 * @param bl the block
1188 static ir_node *find_last_memory(block_t *bl) {
1190 if (bl->memop_backward != NULL) {
1191 return bl->memop_backward->mem;
1193 /* if there is NO memory in this block, go to the dominator */
1194 bl = get_block_entry(get_Block_idom(bl->block));
1199 * Reroute the memory. Reroute all users of old memory
1200 * to a new memory IR-node.
1202 * @param omem the old memory IR-node
1203 * @param nmem the new memory IR-node
1205 static void reroute_mem(ir_node *omem, ir_node *nmem) {
1208 for (i = get_irn_n_outs(omem) - 1; i >= 0; --i) {
1210 ir_node *succ = get_irn_out_ex(omem, i, &n_pos);
1212 set_irn_n(succ, n_pos, nmem);
1215 /* all edges formally point to omem now point to nmem */
1216 nmem->out = omem->out;
1222 static void insert_Load(ir_node *block, void *ctx) {
1223 int *new_stuff = ctx;
1225 int i, n = get_Block_n_cfgpreds(block);
1227 unsigned end = env.n_mem_ops * 2 - 1;
1232 bl = get_block_entry(block);
1234 for (pos = rbitset_next(bl->anticL_in, pos, 1); pos != end; pos = rbitset_next(bl->anticL_in, pos + 1, 1)) {
1235 memop_t *op = bl->id_2_memop[pos];
1238 assert(is_Load(op->node));
1240 if (op->flags & FLAG_KILLED_NODE)
1244 for (i = n - 1; i >= 0; --i) {
1245 ir_node *pred = get_Block_cfgpred_block(block, i);
1246 block_t *pred_bl = get_block_entry(pred);
1247 memop_t *e = find_address_avail(pred_bl, &op->value);
1250 ir_node *block = get_nodes_block(op->value.address);
1251 if (! block_dominates(block, pred)) {
1252 /* cannot place a copy here */
1256 pred_bl->avail = NULL;
1263 ir_mode *mode = op->value.mode;
1266 NEW_ARR_A(ir_node *, in, n);
1268 for (i = n - 1; i >= 0; --i) {
1269 ir_node *pred = get_Block_cfgpred_block(block, i);
1270 block_t *pred_bl = get_block_entry(pred);
1272 if (pred_bl->avail == NULL) {
1273 /* create a new Load here and make to make it fully redundant */
1274 dbg_info *db = get_irn_dbg_info(op->node);
1275 ir_node *last_mem = find_last_memory(pred_bl);
1276 ir_node *load, *def;
1279 load = new_rd_Load(db, current_ir_graph, pred, last_mem, op->value.address, mode, cons_none);
1280 def = new_r_Proj(current_ir_graph, pred, load, mode, pn_Load_res);
1281 DB((dbg, LEVEL_1, "Created new %+F for party redundant %+F\n", load, op->node));
1283 new_op = alloc_memop(load);
1284 new_op->mem = new_r_Proj(current_ir_graph, pred, load, mode_M, pn_Load_M);
1285 new_op->value.address = op->value.address;
1286 new_op->value.id = op->value.id;
1287 new_op->value.mode = mode;
1288 new_op->value.value = def;
1290 new_op->prev = pred_bl->memop_backward;
1291 pred_bl->memop_backward = new_op;
1293 reroute_mem(last_mem, new_op->mem);
1295 /* we added this load at the end, so it will be avail anyway */
1296 add_memop_avail(pred_bl, new_op);
1297 pred_bl->avail = new_op;
1299 in[i] = pred_bl->avail->value.value;
1301 phi = new_r_Phi(current_ir_graph, block, n, in, mode);
1302 mark_replace_load(op, phi);
1305 if (get_nodes_block(op->node) == block) {
1306 /* The redundant node was in the current block:
1307 In that case, DO NOT update avail_out. If it was NOT
1308 avail although it is executed in this bLock, it is killed by a later
1312 /* The redundant node is NOT in the current block and anticipated.
1313 This can only happen IFF nothings kills the Load in the current block,
1314 so it will be avail in the next iteration.
1316 add_memop_avail(bl, op);
1318 /* TODO propagate it downwards */
1325 * Insert Loads upwards.
1327 static void insert_Loads_upwards(void) {
1330 /* calculate antic_out */
1331 DB((dbg, LEVEL_2, "Inserting Loads"));
1334 DB((dbg, LEVEL_2, "Iteration %d:\n=========\n", i));
1337 dom_tree_walk_irg(current_ir_graph, insert_Load, NULL, &need_iter);
1339 } while (need_iter);
1340 DB((dbg, LEVEL_2, "Finished Load inserting after %d iterations\n", i));
1343 int opt_ldst(ir_graph *irg) {
1345 ir_graph *rem = current_ir_graph;
1347 current_ir_graph = irg;
1349 FIRM_DBG_REGISTER(dbg, "firm.opt.ldst");
1350 firm_dbg_set_mask(dbg, SET_LEVEL_2);
1352 DB((dbg, LEVEL_1, "\nDoing Load/Store optimization on %+F\n", irg));
1354 /* we need landing pads */
1355 remove_critical_cf_edges(irg);
1357 if (get_opt_alias_analysis()) {
1358 assure_irg_entity_usage_computed(irg);
1359 assure_irp_globals_entity_usage_computed();
1362 obstack_init(&env.obst);
1363 ir_nodemap_init(&env.adr_map);
1366 env.backward = NULL;
1367 env.curr_adr_id = 0;
1370 env.start_bl = get_irg_start_block(irg);
1373 assure_irg_outs(irg);
1375 ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
1377 /* first step: allocate block entries: produces an
1378 inverse post-order list for the CFG */
1379 irg_out_block_walk(get_irg_start_block(irg), NULL, prepare_blocks, NULL);
1381 /* second step: find and sort all memory ops */
1382 walk_memory_irg(irg, collect_memops, NULL, NULL);
1384 /* create the backward links */
1385 irg_block_walk_graph(irg, NULL, collect_backward, NULL);
1387 /* check that we really start with the start / end block */
1388 assert(env.forward->block == env.start_bl);
1389 assert(env.backward->block == get_irg_end_block(irg));
1391 /* create address sets: we know that 2 * n_mem_ops - 1 is an upper bound for all possible addresses */
1392 env.rbs_size = 2 * env.n_mem_ops;
1394 /* create the current set */
1395 env.curr_set = rbitset_obstack_alloc(&env.obst, env.rbs_size);
1396 rbitset_set(env.curr_set, env.rbs_size - 1);
1398 for (bl = env.forward; bl != NULL; bl = bl->forward_next) {
1399 /* set sentinel bits */
1400 bl->avail_out = rbitset_obstack_alloc(&env.obst, env.rbs_size);
1401 rbitset_set(bl->avail_out, env.rbs_size - 1);
1403 bl->id_2_memop_avail = NEW_ARR_D(memop_t *, &env.obst, env.rbs_size);
1404 memset(bl->id_2_memop_avail, 0, env.rbs_size * sizeof(bl->id_2_memop[0]));
1406 bl->anticL_in = rbitset_obstack_alloc(&env.obst, env.rbs_size);
1407 rbitset_set(bl->anticL_in, env.rbs_size - 1);
1409 bl->id_2_memop = NEW_ARR_D(memop_t *, &env.obst, env.rbs_size);
1410 memset(bl->id_2_memop, 0, env.rbs_size * sizeof(bl->id_2_memop[0]));
1413 // dump_block_list(&env);
1418 insert_Loads_upwards();
1421 /* over all blocks in reverse post order */
1422 for (bl = env.forward; bl != NULL; bl = bl->forward_next) {
1423 do_replacements(bl);
1426 set_irg_outs_inconsistent(irg);
1427 set_irg_entity_usage_state(irg, ir_entity_usage_not_computed);
1430 ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
1431 ir_nodemap_destroy(&env.adr_map);
1432 obstack_free(&env.obst, NULL);
1434 current_ir_graph = rem;
1436 return env.changed != 0;