From: Michael Beck Date: Thu, 10 Jul 2008 13:46:32 +0000 (+0000) Subject: initial version of Cliff Click's Combo Optimization X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=090736ef38c9b9406e0fc59d82c21a92715e0ca6;p=libfirm initial version of Cliff Click's Combo Optimization [r20410] --- diff --git a/ir/opt/combo.c b/ir/opt/combo.c new file mode 100644 index 000000000..1fe140d77 --- /dev/null +++ b/ir/opt/combo.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved. + * + * This file is part of libFirm. + * + * This file may be distributed and/or modified under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation and appearing in the file LICENSE.GPL included in the + * packaging of this file. + * + * Licensees holding valid libFirm Professional Edition licenses may use + * this file in accordance with the libFirm Commercial License. + * Agreement provided with the Software. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +/** + * @file + * @brief Cliff Click's Combined Analysis/Optimization + * @author Michael Beck + * @version $Id$ + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "iroptimize.h" +#include +#include "list.h" +#include "obstack.h" +#include "irgraph_t.h" +#include "irnode_t.h" +#include "irgwalk.h" +#include "irop.h" +#include "irouts.h" +#include "irgmod.h" +#include "debug.h" + +typedef struct partition_entry_t partition_entry_t; +typedef struct partition_t partition_t; + +/** + * A partition entry. + */ +struct partition_entry_t { + ir_node *node; /**< The node itself. */ + list_head list; /**< double-linked list */ + partition_t *part; /**< points to the partition this entry belongs to */ + partition_entry_t *touched_next; /**< Next entry on partition.touched set. */ + unsigned on_touched:1; /**< Set, if this entry is on the partition.touched set. */ +}; + +/** + * A partition containing congruent nodes. + */ +struct partition_t { + list_head entries; /**< The partition entries. */ + partition_t *wl_next; /**< Next entry in the work list if any. */ + partition_t *touched_next; /**< points to the next partition in the touched set. */ + partition_entry_t *touched; /**< the partition.touched set of this partition. */ + unsigned n_entries; /**< number of entries in this partition. */ + unsigned n_touched; /**< number of entries in the partition.touched. */ + int n_inputs; /**< Maximum number of inputs of all entries. */ + unsigned on_worklist:1; /**< Set, if this partition is in the work list. */ + unsigned on_touched:1; /**< Set, if this partition is on the touched set. */ +}; + +typedef struct environment_t { + struct obstack obst; /**< obstack to allocate data structures. */ + partition_t *worklist; /**< The work list. */ + partition_t *touched; /**< the touched set. */ + partition_t *opcode_map[iro_Last]; /**< The initial partition set. */ +} environment_t; + +#define get_irn_entry(irn) ((partition_entry_t *)get_irn_link(irn)) +#define set_irn_entry(irn, entry) set_irn_link(irn, entry) + +/** The debug module handle. */ +DEBUG_ONLY(static firm_dbg_module_t *dbg;) + +#ifdef DEBUG_libfirm +static void dump_partition(partition_t *part) { + partition_entry_t *entry; + + DB((dbg, LEVEL_2, "{ ")); + list_for_each_entry(partition_entry_t, entry, &part->entries, list) { + DB((dbg, LEVEL_2, "%+F, ", entry->node)); + } + DB((dbg, LEVEL_2, "}\n")); +} +#else +#define dump_partition(part) +#endif + +/** + * Create a new empty partition. + */ +static INLINE partition_t *new_partition(environment_t *env) { + partition_t *part = obstack_alloc(&env->obst, sizeof(*part)); + + INIT_LIST_HEAD(&part->entries); + part->wl_next = env->worklist; + part->touched_next = NULL; + part->touched = NULL; + part->n_entries = 0; + part->n_touched = 0; + part->n_inputs = 0; + part->on_worklist = 0; + part->on_touched = 0; + + return part; +} + +/** + * Get the partition for a given opcode. + */ +static INLINE partition_t *get_partition(ir_opcode code, environment_t *env) { + partition_t *part = env->opcode_map[code]; + + if (part == NULL) { + /* create a new partition and place it on the wait queue */ + part = new_partition(env); + + part->on_worklist = 1; + env->worklist = part; + env->opcode_map[code] = part; + } + return part; +} + +/** + * Walker, creates the initial partitions, one for every opcode and place them + * on the worklist. + */ +static void create_initial_partitions(ir_node *irn, void *ctx) { + environment_t *env = ctx; + ir_opcode code = get_irn_opcode(irn); + partition_t *part = get_partition(code, env); + int arity; + + /* create a partition entry and place it in the partition */ + partition_entry_t *entry = obstack_alloc(&env->obst, sizeof(*entry)); + + INIT_LIST_HEAD(&entry->list); + entry->node = irn; + entry->part = part; + entry->touched_next = NULL; + entry->on_touched = 0; + set_irn_entry(irn, entry); + + list_add_tail(&entry->list, &part->entries); + ++part->n_entries; + + arity = get_irn_arity(irn); + if (arity > part->n_inputs) + part->n_inputs = arity; +} + +/** + * Add a partition to the touched set if not already there. + */ +static INLINE void add_to_touched(partition_t *part, environment_t *env) { + if (part->on_touched == 0) { + part->touched_next = env->touched; + env->touched = part; + part->on_touched = 1; + } +} + +/** + * Add an entry to the entry.partition.touched set if not already there.. + */ +static INLINE void add_to_partition_touched(partition_entry_t *y) { + if (y->on_touched == 0) { + partition_t *part = y->part; + + y->touched_next = part->touched; + part->touched = y; + ++part->n_touched; + y->on_touched = 1; + } +} + +/** + * Split a partition by the touched set. + */ +static partition_t *split(partition_t *Z, partition_entry_t *g, environment_t *env) { + partition_t *Z_prime; + partition_entry_t *entry; + unsigned n = 0; + + /* Remove g from Z. */ + for (entry = g; entry != NULL; entry = entry->touched_next) { + list_del(&entry->list); + entry->on_touched = 0; + ++n; + } + Z->n_entries -= n; + + /* Move g to a new partition, Z’. */ + Z_prime = new_partition(env); + for (entry = g; entry != NULL; entry = entry->touched_next) { + list_add(&entry->list, &Z_prime->entries); + entry->part = Z_prime; + } + Z_prime->n_entries = n; + + /* If Z is on worklist then add Z’ to worklist. + Else add the smaller of Z and Z’ to worklist. */ + if (Z->on_worklist || Z_prime->n_entries < Z->n_entries) { + Z_prime->on_worklist = 1; + Z_prime->wl_next = env->worklist; + env->worklist = Z_prime; + } else { + Z->on_worklist = 1; + Z->wl_next = env->worklist; + env->worklist = Z; + } + return Z_prime; +} + +/** + * Split the partitions if caused by the first entry on the worklist. + */ +static void cause_splits(environment_t *env) { + partition_t *X, *Z; + partition_entry_t *x, *y, *e; + int i; + + /* remove the first partition from the worklist */ + X = env->worklist; + env->worklist = X->wl_next; + X->on_worklist = 0; + + for (i = X->n_inputs - 1; i >= -1; --i) { + /* empty the touched set: already done, just clear the list */ + env->touched = NULL; + + list_for_each_entry(partition_entry_t, x, &X->entries, list) { + if (i < get_irn_arity(x->node) && (!is_Block(x->node) || i >= 0)) { + y = get_irn_entry(get_irn_n(x->node, i)); + + add_to_touched(y->part, env); + add_to_partition_touched(y); + } + } + + for (Z = env->touched; Z != NULL; Z = Z->touched_next) { + /* remove it from the touched set */ + Z->on_touched = 0; + + if (Z->n_entries != Z->n_touched) { + split(Z, Z->touched, env); + } + /* Empty Z.touched. */ + for (e = Z->touched; e != NULL; e = e->touched_next) { + e->on_touched = 0; + } + Z->touched = NULL; + } + + } +} + +/** + * Get the leader for a given node from its congruence class. + * + * @param irn the node + */ +static ir_node *get_leader(ir_node *irn) { + partition_t *part = get_irn_entry(irn)->part; + + if (part->n_entries > 1) { + DB((dbg, LEVEL_2, "Found congruence class for %+F ", irn)); + dump_partition(part); + } + return irn; +} + +/** + * Post-Walker, apply the analysis results; + */ +static void apply_result(ir_node *irn, void *ctx) { + environment_t *env = ctx; + + ir_node *leader = get_leader(irn); + + if (leader != irn) { + exchange(irn, leader); + } +} + +void combo(ir_graph *irg) { + environment_t env; + + /* register a debug mask */ + FIRM_DBG_REGISTER(dbg, "firm.opt.combo"); + firm_dbg_set_mask(dbg, SET_LEVEL_2); + + obstack_init(&env.obst); + env.worklist = NULL; + env.touched = NULL; + memset(env.opcode_map, 0, sizeof(env.opcode_map)); + + assure_irg_outs(irg); + + /* create the initial partitions */ + irg_walk_graph(irg, NULL, create_initial_partitions, &env); + + while (env.worklist != NULL) + cause_splits(&env); + + /* apply the result */ + irg_walk_graph(irg, NULL, apply_result, &env); + + obstack_free(&env.obst, NULL); +}