initial version of Cliff Click's Combo Optimization
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 10 Jul 2008 13:46:32 +0000 (13:46 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 10 Jul 2008 13:46:32 +0000 (13:46 +0000)
[r20410]

ir/opt/combo.c [new file with mode: 0644]

diff --git a/ir/opt/combo.c b/ir/opt/combo.c
new file mode 100644 (file)
index 0000000..1fe140d
--- /dev/null
@@ -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 <assert.h>
+#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\92. */
+       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\92 to worklist.
+          Else add the smaller of Z and Z\92 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);
+}