aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/simplify.c
diff options
Diffstat (limited to 'simplify.c')
-rw-r--r--simplify.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/simplify.c b/simplify.c
new file mode 100644
index 00000000..506c40c6
--- /dev/null
+++ b/simplify.c
@@ -0,0 +1,217 @@
+/*
+ * Simplify - do instruction simplification before CSE
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+
+/* Find the trivial parent for a phi-source */
+static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo)
+{
+ /* Can't go upwards if the pseudo is defined in the bb it came from.. */
+ if (pseudo->type == PSEUDO_REG) {
+ struct instruction *def = pseudo->def;
+ if (def->bb == source)
+ return source;
+ }
+ if (bb_list_size(source->children) != 1 || bb_list_size(source->parents) != 1)
+ return source;
+ return first_basic_block(source->parents);
+}
+
+static void clear_phi(struct instruction *insn)
+{
+ pseudo_t phi;
+
+ insn->bb = NULL;
+ FOR_EACH_PTR(insn->phi_list, phi) {
+ *THIS_ADDRESS(phi) = VOID;
+ } END_FOR_EACH_PTR(phi);
+}
+
+static int if_convert_phi(struct instruction *insn)
+{
+ pseudo_t array[3];
+ struct basic_block *parents[3];
+ struct basic_block *bb, *bb1, *bb2, *source;
+ struct instruction *br;
+ pseudo_t p1, p2;
+
+ bb = insn->bb;
+ if (linearize_ptr_list((struct ptr_list *)insn->phi_list, (void **)array, 3) != 2)
+ return 0;
+ if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2)
+ return 0;
+ p1 = array[0]->def->src1;
+ bb1 = array[0]->def->bb;
+ p2 = array[1]->def->src1;
+ bb2 = array[1]->def->bb;
+
+ /* Only try the simple "direct parents" case */
+ if ((bb1 != parents[0] || bb2 != parents[1]) &&
+ (bb1 != parents[1] || bb2 != parents[0]))
+ return 0;
+
+ /*
+ * See if we can find a common source for this..
+ */
+ source = phi_parent(bb1, p1);
+ if (phi_parent(bb2, p2) != source)
+ return 0;
+
+ /*
+ * Cool. We now know that 'source' is the exclusive
+ * parent of both phi-nodes, so the exit at the
+ * end of it fully determines which one it is, and
+ * we can turn it into a select.
+ *
+ * HOWEVER, right now we only handle regular
+ * conditional branches. No multijumps or computed
+ * stuff. Verify that here.
+ */
+ br = last_instruction(source->insns);
+ if (!br || br->opcode != OP_BR)
+ return 0;
+
+ /*
+ * We're in business. Match up true/false with p1/p2.
+ */
+ if (br->bb_true == bb2 || br->bb_false == bb1) {
+ pseudo_t p = p1;
+ p1 = p2;
+ p2 = p;
+ }
+
+ /*
+ * Ok, we can now replace that last
+ *
+ * br cond, a, b
+ *
+ * with the sequence
+ *
+ * setcc cond
+ * select pseudo, p1, p2
+ * br cond, a, b
+ *
+ * and remove the phi-node. If it then
+ * turns out that 'a' or 'b' is entirely
+ * empty (common case), and now no longer
+ * a phi-source, we'll be able to simplify
+ * the conditional branch too.
+ */
+ insert_select(source, br, insn, p1, p2);
+ clear_phi(insn);
+ return 1;
+}
+
+static int clean_up_phi(struct instruction *insn)
+{
+ pseudo_t phi;
+ struct instruction *last;
+ int same;
+
+ last = NULL;
+ same = 1;
+ FOR_EACH_PTR(insn->phi_list, phi) {
+ struct instruction *def = phi->def;
+ if (def->src1 == VOID)
+ continue;
+ if (last) {
+ if (last->src1 != def->src1)
+ same = 0;
+ continue;
+ }
+ last = def;
+ } END_FOR_EACH_PTR(phi);
+
+ if (same) {
+ pseudo_t pseudo = last ? last->src1 : VOID;
+ convert_instruction_target(insn, pseudo);
+ insn->bb = NULL;
+ return 1;
+ }
+
+ return if_convert_phi(insn);
+}
+
+static void try_to_kill(struct instruction *);
+
+static void kill_use(pseudo_t pseudo)
+{
+ if (pseudo->type == PSEUDO_REG) {
+ if (ptr_list_size((struct ptr_list *)pseudo->users) == 1) {
+ try_to_kill(pseudo->def);
+ }
+ }
+}
+
+static void try_to_kill(struct instruction *insn)
+{
+ int opcode = insn->opcode;
+ switch (opcode) {
+ case OP_BINARY ... OP_BINCMP_END:
+ insn->bb = NULL;
+ kill_use(insn->src1);
+ kill_use(insn->src2);
+ return;
+ case OP_NOT: case OP_NEG:
+ insn->bb = NULL;
+ kill_use(insn->src1);
+ return;
+
+ case OP_PHI: case OP_SETVAL:
+ insn->bb = NULL;
+ return;
+ }
+}
+
+/*
+ * Kill trivially dead instructions
+ */
+static int dead_insn(struct instruction *insn, pseudo_t src1, pseudo_t src2)
+{
+ pseudo_t *usep;
+ FOR_EACH_PTR(insn->target->users, usep) {
+ if (*usep != VOID)
+ return 0;
+ } END_FOR_EACH_PTR(usep);
+
+ insn->bb = NULL;
+ kill_use(src1);
+ kill_use(src2);
+ return 1;
+}
+
+int simplify_instruction(struct instruction *insn)
+{
+ switch (insn->opcode) {
+ case OP_BINARY ... OP_BINCMP_END:
+ if (dead_insn(insn, insn->src1, insn->src2))
+ return 1;
+ break;
+ case OP_NOT: case OP_NEG:
+ if (dead_insn(insn, insn->src1, VOID))
+ return 1;
+ break;
+ case OP_SETVAL:
+ if (dead_insn(insn, VOID, VOID))
+ return 1;
+ break;
+ case OP_PHI:
+ if (dead_insn(insn, VOID, VOID)) {
+ clear_phi(insn);
+ return 1;
+ }
+ return clean_up_phi(insn);
+ case OP_PHISOURCE:
+ if (dead_insn(insn, insn->src1, VOID))
+ return 1;
+ break;
+ }
+ return 0;
+}