aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
-rw-r--r--Documentation/dev-options.rst8
-rw-r--r--Makefile5
-rw-r--r--cse.c27
-rw-r--r--dominate.c153
-rw-r--r--dominate.h9
-rw-r--r--flow.c278
-rw-r--r--flowgraph.c219
-rw-r--r--flowgraph.h13
-rw-r--r--lib.c4
-rw-r--r--lib.h2
-rw-r--r--linearize.c45
-rw-r--r--linearize.h21
-rw-r--r--optimize.c6
-rw-r--r--ptrmap.c109
-rw-r--r--ptrmap.h28
-rw-r--r--sparse-llvm.c6
-rw-r--r--ssa.c401
-rw-r--r--ssa.h6
-rw-r--r--sset.c28
-rw-r--r--sset.h56
-rw-r--r--symbol.h19
-rw-r--r--validation/crash-select.c18
-rw-r--r--validation/linear/compound-literal00.c (renamed from validation/compound-literal00.c)0
-rw-r--r--validation/linear/compound-literal01.c18
-rw-r--r--validation/linear/compound-literal02.c (renamed from validation/compound-literal01.c)10
-rw-r--r--validation/linear/logical.c70
-rw-r--r--validation/mem2reg/alias-distinct.c (renamed from validation/alias-distinct.c)0
-rw-r--r--validation/mem2reg/alias-mixed.c (renamed from validation/alias-mixed.c)0
-rw-r--r--validation/mem2reg/alias-same.c (renamed from validation/alias-same.c)0
-rw-r--r--validation/mem2reg/broken-phi02.c1
-rw-r--r--validation/mem2reg/broken-phi03.c1
-rw-r--r--validation/mem2reg/cond-expr.c1
-rw-r--r--validation/mem2reg/cond-expr5.c5
-rw-r--r--validation/mem2reg/init-global-array.c12
-rw-r--r--validation/mem2reg/init-local-array.c13
-rw-r--r--validation/mem2reg/loop02-global.c2
-rw-r--r--validation/mem2reg/missing-return.c34
-rw-r--r--validation/mem2reg/quadra00.c1
-rw-r--r--validation/mem2reg/quadra01.c27
-rw-r--r--validation/mem2reg/quadra02.c18
-rw-r--r--validation/mem2reg/reload-aliasing.c (renamed from validation/reload-aliasing.c)0
-rw-r--r--validation/mem2reg/store-deadborn.c9
-rw-r--r--validation/mem2reg/stray-phisrc.c (renamed from validation/linear/stray-phisrc.c)1
-rw-r--r--validation/mem2reg/struct.c32
-rw-r--r--validation/mem2reg/undef00.c15
-rw-r--r--validation/mem2reg/undef01.c16
-rw-r--r--validation/mem2reg/unused-var.c23
-rw-r--r--validation/optim/cse-size.c3
-rw-r--r--validation/optim/killed-insn.c (renamed from validation/mem2reg/killed-insn.c)7
-rw-r--r--validation/optim/missing-select.c1
-rw-r--r--validation/optim/null-phi.c9
-rw-r--r--validation/repeat.h24
-rw-r--r--validation/var-undef-partial.c1
53 files changed, 1439 insertions, 376 deletions
diff --git a/Documentation/dev-options.rst b/Documentation/dev-options.rst
index bf2d49a7..04fb651f 100644
--- a/Documentation/dev-options.rst
+++ b/Documentation/dev-options.rst
@@ -44,6 +44,14 @@ OPTIONS
Add ``OP_DEATHNOTE`` annotations to dead pseudos.
+.. option:: -vdomtree
+
+ Dump the dominance tree after its calculation.
+
.. option:: -ventry
Dump the IR after all optimization passes.
+
+.. option:: -vpostorder
+
+ Dump the reverse postorder traversal of the CFG.
diff --git a/Makefile b/Makefile
index abe30e02..f031fadb 100644
--- a/Makefile
+++ b/Makefile
@@ -37,10 +37,12 @@ LIB_OBJS += char.o
LIB_OBJS += compat-$(OS).o
LIB_OBJS += cse.o
LIB_OBJS += dissect.o
+LIB_OBJS += dominate.o
LIB_OBJS += evaluate.o
LIB_OBJS += expand.o
LIB_OBJS += expression.o
LIB_OBJS += flow.o
+LIB_OBJS += flowgraph.o
LIB_OBJS += inline.o
LIB_OBJS += ir.o
LIB_OBJS += lib.o
@@ -52,10 +54,13 @@ LIB_OBJS += optimize.o
LIB_OBJS += parse.o
LIB_OBJS += pre-process.o
LIB_OBJS += ptrlist.o
+LIB_OBJS += ptrmap.o
LIB_OBJS += scope.o
LIB_OBJS += show-parse.o
LIB_OBJS += simplify.o
LIB_OBJS += sort.o
+LIB_OBJS += ssa.o
+LIB_OBJS += sset.o
LIB_OBJS += stats.o
LIB_OBJS += storage.o
LIB_OBJS += symbol.o
diff --git a/cse.c b/cse.c
index 511ac88a..1395a8af 100644
--- a/cse.c
+++ b/cse.c
@@ -14,6 +14,7 @@
#include "parse.h"
#include "expression.h"
+#include "flowgraph.h"
#include "linearize.h"
#include "flow.h"
#include "cse.h"
@@ -276,28 +277,6 @@ static struct instruction * cse_one_instruction(struct instruction *insn, struct
return def;
}
-/*
- * Does "bb1" dominate "bb2"?
- */
-static int bb_dominates(struct entrypoint *ep, struct basic_block *bb1, struct basic_block *bb2, unsigned long generation)
-{
- struct basic_block *parent;
-
- /* Nothing dominates the entrypoint.. */
- if (bb2 == ep->entry->bb)
- return 0;
- FOR_EACH_PTR(bb2->parents, parent) {
- if (parent == bb1)
- continue;
- if (parent->generation == generation)
- continue;
- parent->generation = generation;
- if (!bb_dominates(ep, bb1, parent, generation))
- return 0;
- } END_FOR_EACH_PTR(parent);
- return 1;
-}
-
static struct basic_block *trivial_common_parent(struct basic_block *bb1, struct basic_block *bb2)
{
struct basic_block *parent;
@@ -351,10 +330,10 @@ static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction
warning(b1->pos, "Whaa? unable to find CSE instructions");
return i1;
}
- if (bb_dominates(ep, b1, b2, ++bb_generation))
+ if (domtree_dominates(b1, b2))
return cse_one_instruction(i2, i1);
- if (bb_dominates(ep, b2, b1, ++bb_generation))
+ if (domtree_dominates(b2, b1))
return cse_one_instruction(i1, i2);
/* No direct dominance - but we could try to find a common ancestor.. */
diff --git a/dominate.c b/dominate.c
new file mode 100644
index 00000000..8085171d
--- /dev/null
+++ b/dominate.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: MIT
+//
+// dominate.c - compute the (iterated) dominance frontier of (a set of) nodes.
+//
+// Copyright (C) 2017 - Luc Van Oostenryck
+//
+// The algorithm used is the one described in:
+// "A Linear Time Algorithm for Placing phi-nodes"
+// by Vugranam C. Sreedhar and Guang R. Gao
+//
+
+#include "dominate.h"
+#include "flowgraph.h"
+#include "linearize.h"
+#include "flow.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+struct piggy {
+ unsigned int max;
+ struct basic_block_list *lists[0];
+};
+
+struct piggy *bank_init(unsigned levels)
+{
+ struct piggy *bank;
+ bank = calloc(1, sizeof(*bank) + levels * sizeof(bank->lists[0]));
+ bank->max = levels - 1;
+ return bank;
+}
+
+static void bank_free(struct piggy *bank, unsigned int levels)
+{
+ for (; levels-- ;)
+ free_ptr_list(&bank->lists[levels]);
+ free(bank);
+}
+
+static void bank_put(struct piggy *bank, struct basic_block *bb)
+{
+ unsigned int level = bb->dom_level;
+ assert(level <= bank->max);
+ add_bb(&bank->lists[level], bb);
+}
+
+static inline struct basic_block *pop_bb(struct basic_block_list **list)
+{
+ return delete_ptr_list_last((struct ptr_list **)list);
+}
+
+static struct basic_block *bank_get(struct piggy *bank)
+{
+ int level = bank->max;
+ do {
+ struct basic_block *bb = pop_bb(&bank->lists[level]);
+ if (bb)
+ return bb;
+ if (!level)
+ return NULL;
+ bank->max = --level;
+ } while (1);
+}
+
+
+#define VISITED 0x1
+#define INPHI 0x2
+#define ALPHA 0x4
+#define FLAGS 0x7
+
+static void visit(struct piggy *bank, struct basic_block_list **idf, struct basic_block *x, int curr_level)
+{
+ struct basic_block *y;
+
+ x->generation |= 1;
+ FOR_EACH_PTR(x->children, y) {
+ unsigned flags = y->generation & FLAGS;
+ if (y->idom == x) // J-edges will be processed later
+ continue;
+ if (y->dom_level > curr_level)
+ continue;
+ if (flags & INPHI)
+ continue;
+ y->generation |= INPHI;
+ add_bb(idf, y);
+ if (flags & ALPHA)
+ continue;
+ bank_put(bank, y);
+ } END_FOR_EACH_PTR(y);
+
+ FOR_EACH_PTR(x->doms, y) {
+ if (y->generation & VISITED)
+ continue;
+ visit(bank, idf, y, curr_level);
+ } END_FOR_EACH_PTR(y);
+}
+
+void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha)
+{
+ int levels = ep->dom_levels;
+ struct piggy *bank = bank_init(levels);
+ struct basic_block *bb;
+ unsigned long generation = bb_generation;
+
+ generation = bb_generation;
+ generation += -generation & FLAGS;
+ bb_generation = generation + (FLAGS + 1);
+
+ // init all the nodes
+ FOR_EACH_PTR(ep->bbs, bb) {
+ // FIXME: this should be removed and the tests for
+ // visited/in_phi/alpha should use a sparse set
+ bb->generation = generation;
+ } END_FOR_EACH_PTR(bb);
+
+ FOR_EACH_PTR(alpha, bb) {
+ bb->generation = generation | ALPHA;
+ bank_put(bank, bb);
+ } END_FOR_EACH_PTR(bb);
+
+ while ((bb = bank_get(bank))) {
+ visit(bank, idf, bb, bb->dom_level);
+ }
+
+ bank_free(bank, levels);
+}
+
+void idf_dump(struct entrypoint *ep)
+{
+ struct basic_block *bb;
+
+ domtree_build(ep);
+
+ printf("%s's IDF:\n", show_ident(ep->name->ident));
+ FOR_EACH_PTR(ep->bbs, bb) {
+ struct basic_block_list *alpha = NULL;
+ struct basic_block_list *idf = NULL;
+ struct basic_block *df;
+
+ add_bb(&alpha, bb);
+ idf_compute(ep, &idf, alpha);
+
+ printf("\t%s\t<-", show_label(bb));
+ FOR_EACH_PTR(idf, df) {
+ printf(" %s", show_label(df));
+ } END_FOR_EACH_PTR(df);
+ printf("\n");
+
+ free_ptr_list(&idf);
+ free_ptr_list(&alpha);
+ } END_FOR_EACH_PTR(bb);
+}
diff --git a/dominate.h b/dominate.h
new file mode 100644
index 00000000..6ac515d0
--- /dev/null
+++ b/dominate.h
@@ -0,0 +1,9 @@
+#ifndef DOMINATE_H
+#define DOMINATE_H
+
+struct entrypoint;
+struct basic_block_list;
+
+void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha);
+
+#endif
diff --git a/flow.c b/flow.c
index 8bdd5984..40f1ba58 100644
--- a/flow.c
+++ b/flow.c
@@ -370,68 +370,6 @@ int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom
return 1;
}
-static int phisrc_in_bb(struct pseudo_list *list, struct basic_block *bb)
-{
- pseudo_t p;
- FOR_EACH_PTR(list, p) {
- if (p->def->bb == bb)
- return 1;
- } END_FOR_EACH_PTR(p);
-
- return 0;
-}
-
-static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn,
- struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators,
- int local)
-{
- struct basic_block *parent;
-
- if (!bb->parents)
- return !!local;
-
- FOR_EACH_PTR(bb->parents, parent) {
- struct instruction *one;
- struct instruction *br;
- pseudo_t phi;
-
- FOR_EACH_PTR_REVERSE(parent->insns, one) {
- int dominance;
- if (!one->bb)
- continue;
- if (one == insn)
- goto no_dominance;
- dominance = dominates(pseudo, insn, one, local);
- if (dominance < 0) {
- if (one->opcode == OP_LOAD)
- continue;
- return 0;
- }
- if (!dominance)
- continue;
- goto found_dominator;
- } END_FOR_EACH_PTR_REVERSE(one);
-no_dominance:
- if (parent->generation == generation)
- continue;
- parent->generation = generation;
-
- if (!find_dominating_parents(pseudo, insn, parent, generation, dominators, local))
- return 0;
- continue;
-
-found_dominator:
- if (dominators && phisrc_in_bb(*dominators, parent))
- continue;
- br = delete_last_instruction(&parent->insns);
- phi = alloc_phi(parent, one->target, one->type);
- phi->ident = phi->ident ? : pseudo->ident;
- add_instruction(&parent->insns, br);
- use_pseudo(insn, phi, add_pseudo(dominators, phi));
- } END_FOR_EACH_PTR(parent);
- return 1;
-}
-
/*
* We should probably sort the phi list just to make it easier to compare
* later for equality.
@@ -473,79 +411,6 @@ end:
repeat_phase |= REPEAT_SYMBOL_CLEANUP;
}
-static int find_dominating_stores(pseudo_t pseudo, struct instruction *insn,
- unsigned long generation, int local)
-{
- struct basic_block *bb = insn->bb;
- struct instruction *one, *dom = NULL;
- struct pseudo_list *dominators;
- int partial;
-
- /* Unreachable load? Undo it */
- if (!bb) {
- kill_use(&insn->src);
- return 1;
- }
-
- partial = 0;
- FOR_EACH_PTR(bb->insns, one) {
- int dominance;
- if (!one->bb)
- continue;
- if (one == insn)
- goto found;
- dominance = dominates(pseudo, insn, one, local);
- if (dominance < 0) {
- /* Ignore partial load dominators */
- if (one->opcode == OP_LOAD)
- continue;
- dom = NULL;
- partial = 1;
- continue;
- }
- if (!dominance)
- continue;
- dom = one;
- partial = 0;
- } END_FOR_EACH_PTR(one);
- /* Whaa? */
- warning(pseudo->sym->pos, "unable to find symbol read");
- return 0;
-found:
- if (partial)
- return 0;
-
- if (dom) {
- convert_load_instruction(insn, dom->target);
- return 1;
- }
-
- /* OK, go find the parents */
- bb->generation = generation;
-
- dominators = NULL;
- if (!find_dominating_parents(pseudo, insn, bb, generation, &dominators, local))
- return 0;
-
- /* This happens with initial assignments to structures etc.. */
- if (!dominators) {
- if (!local)
- return 0;
- check_access(insn);
- convert_load_instruction(insn, value_pseudo(0));
- return 1;
- }
-
- /*
- * If we find just one dominating instruction, we
- * can turn it into a direct thing. Otherwise we'll
- * have to turn the load into a phi-node of the
- * dominators.
- */
- rewrite_load_instruction(insn, dominators);
- return 1;
-}
-
/* Kill a pseudo that is dead on exit from the bb */
// The context is:
// * the variable is not global but may have its address used (local/non-local)
@@ -600,59 +465,6 @@ static void kill_dead_stores_bb(pseudo_t pseudo, unsigned long generation, struc
} END_FOR_EACH_PTR(parent);
}
-/*
- * This should see if the "insn" trivially dominates some previous store, and kill the
- * store if unnecessary.
- */
-static void kill_dominated_stores(pseudo_t pseudo, struct instruction *insn,
- unsigned long generation, struct basic_block *bb, int local, int found)
-{
- struct instruction *one;
- struct basic_block *parent;
-
- /* Unreachable store? Undo it */
- if (!bb) {
- kill_instruction_force(insn);
- return;
- }
- if (bb->generation == generation)
- return;
- bb->generation = generation;
- FOR_EACH_PTR_REVERSE(bb->insns, one) {
- int dominance;
- if (!one->bb)
- continue;
- if (!found) {
- if (one != insn)
- continue;
- found = 1;
- continue;
- }
- dominance = dominates(pseudo, insn, one, local);
- if (!dominance)
- continue;
- if (dominance < 0)
- return;
- if (one->opcode == OP_LOAD)
- return;
- kill_instruction_force(one);
- } END_FOR_EACH_PTR_REVERSE(one);
-
- if (!found) {
- warning(bb->pos, "Unable to find instruction");
- return;
- }
-
- FOR_EACH_PTR(bb->parents, parent) {
- struct basic_block *child;
- FOR_EACH_PTR(parent->children, child) {
- if (child && child != bb)
- return;
- } END_FOR_EACH_PTR(child);
- kill_dominated_stores(pseudo, insn, generation, parent, local, found);
- } END_FOR_EACH_PTR(parent);
-}
-
void check_access(struct instruction *insn)
{
pseudo_t pseudo = insn->src;
@@ -673,96 +485,6 @@ void check_access(struct instruction *insn)
}
}
-static void simplify_one_symbol(struct entrypoint *ep, struct symbol *sym)
-{
- pseudo_t pseudo;
- struct pseudo_user *pu;
- unsigned long mod;
- int all;
-
- /* Never used as a symbol? */
- pseudo = sym->pseudo;
- if (!pseudo)
- return;
-
- /* We don't do coverage analysis of volatiles.. */
- if (sym->ctype.modifiers & MOD_VOLATILE)
- return;
-
- /* ..and symbols with external visibility need more care */
- mod = sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE);
- if (mod)
- goto external_visibility;
-
- FOR_EACH_PTR(pseudo->users, pu) {
- /* We know that the symbol-pseudo use is the "src" in the instruction */
- struct instruction *insn = pu->insn;
-
- switch (insn->opcode) {
- case OP_STORE:
- break;
- case OP_LOAD:
- break;
- case OP_SYMADDR:
- if (!insn->bb)
- continue;
- mod |= MOD_ADDRESSABLE;
- goto external_visibility;
- case OP_NOP:
- case OP_PHI:
- continue;
- default:
- warning(sym->pos, "symbol '%s' pseudo used in unexpected way", show_ident(sym->ident));
- }
- } END_FOR_EACH_PTR(pu);
-
-external_visibility:
- all = 1;
- FOR_EACH_PTR_REVERSE(pseudo->users, pu) {
- struct instruction *insn = pu->insn;
- if (insn->opcode == OP_LOAD)
- all &= find_dominating_stores(pseudo, insn, ++bb_generation, !mod);
- } END_FOR_EACH_PTR_REVERSE(pu);
-
- /* If we converted all the loads, remove the stores. They are dead */
- if (all && !mod) {
- FOR_EACH_PTR(pseudo->users, pu) {
- struct instruction *insn = pu->insn;
- if (insn->opcode == OP_STORE)
- kill_instruction_force(insn);
- } END_FOR_EACH_PTR(pu);
- } else {
- /*
- * If we couldn't take the shortcut, see if we can at least kill some
- * of them..
- */
- FOR_EACH_PTR(pseudo->users, pu) {
- struct instruction *insn = pu->insn;
- if (insn->opcode == OP_STORE)
- kill_dominated_stores(pseudo, insn, ++bb_generation, insn->bb, !mod, 0);
- } END_FOR_EACH_PTR(pu);
-
- if (!(mod & (MOD_NONLOCAL | MOD_STATIC))) {
- struct basic_block *bb;
- FOR_EACH_PTR(ep->bbs, bb) {
- if (!bb->children)
- kill_dead_stores_bb(pseudo, ++bb_generation, bb, !mod);
- } END_FOR_EACH_PTR(bb);
- }
- }
-
- return;
-}
-
-void simplify_symbol_usage(struct entrypoint *ep)
-{
- pseudo_t pseudo;
-
- FOR_EACH_PTR(ep->accesses, pseudo) {
- simplify_one_symbol(ep, pseudo->sym);
- } END_FOR_EACH_PTR(pseudo);
-}
-
static struct pseudo_user *first_user(pseudo_t p)
{
struct pseudo_user *pu;
diff --git a/flowgraph.c b/flowgraph.c
new file mode 100644
index 00000000..8fc22dcf
--- /dev/null
+++ b/flowgraph.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: MIT
+//
+// Various utilities for flowgraphs.
+//
+// Copyright (c) 2017 Luc Van Oostenryck.
+//
+
+#include "flowgraph.h"
+#include "linearize.h"
+#include "flow.h" // for bb_generation
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+struct cfg_info {
+ struct basic_block_list *list;
+ unsigned long gen;
+ unsigned int nr;
+};
+
+
+static void label_postorder(struct basic_block *bb, struct cfg_info *info)
+{
+ struct basic_block *child;
+
+ if (bb->generation == info->gen)
+ return;
+
+ bb->generation = info->gen;
+ FOR_EACH_PTR_REVERSE(bb->children, child) {
+ label_postorder(child, info);
+ } END_FOR_EACH_PTR_REVERSE(child);
+
+ bb->postorder_nr = info->nr++;
+ add_bb(&info->list, bb);
+}
+
+static void reverse_bbs(struct basic_block_list **dst, struct basic_block_list *src)
+{
+ struct basic_block *bb;
+ FOR_EACH_PTR_REVERSE(src, bb) {
+ add_bb(dst, bb);
+ } END_FOR_EACH_PTR_REVERSE(bb);
+}
+
+static void debug_postorder(struct entrypoint *ep)
+{
+ struct basic_block *bb;
+
+ printf("%s's reverse postorder:\n", show_ident(ep->name->ident));
+ FOR_EACH_PTR(ep->bbs, bb) {
+ printf("\t.L%u: %u\n", bb->nr, bb->postorder_nr);
+ } END_FOR_EACH_PTR(bb);
+}
+
+//
+// cfg_postorder - Set the BB's reverse postorder links
+//
+// Do a postorder DFS walk and set the links
+// (which will do the reverse part).
+//
+int cfg_postorder(struct entrypoint *ep)
+{
+ struct cfg_info info = {
+ .gen = ++bb_generation,
+ };
+
+ label_postorder(ep->entry->bb, &info);
+
+ // OK, now info.list contains the node in postorder
+ // Reuse ep->bbs for the reverse postorder.
+ free_ptr_list(&ep->bbs);
+ ep->bbs = NULL;
+ reverse_bbs(&ep->bbs, info.list);
+ free_ptr_list(&info.list);
+ if (dbg_postorder)
+ debug_postorder(ep);
+ return info.nr;
+}
+
+//
+// Calculate the dominance tree following:
+// "A simple, fast dominance algorithm"
+// by K. D. Cooper, T. J. Harvey, and K. Kennedy.
+// cfr. http://www.cs.rice.edu/∼keith/EMBED/dom.pdf
+//
+static struct basic_block *intersect_dom(struct basic_block *doms[],
+ struct basic_block *b1, struct basic_block *b2)
+{
+ int f1 = b1->postorder_nr, f2 = b2->postorder_nr;
+ while (f1 != f2) {
+ while (f1 < f2) {
+ b1 = doms[f1];
+ f1 = b1->postorder_nr;
+ }
+ while (f2 < f1) {
+ b2 = doms[f2];
+ f2 = b2->postorder_nr;
+ }
+ }
+ return b1;
+}
+
+static void debug_domtree(struct entrypoint *ep)
+{
+ struct basic_block *bb = ep->entry->bb;
+
+ printf("%s's idoms:\n", show_ident(ep->name->ident));
+ FOR_EACH_PTR(ep->bbs, bb) {
+ if (bb == ep->entry->bb)
+ continue; // entry node has no idom
+ printf("\t%s <- %s\n", show_label(bb), show_label(bb->idom));
+ } END_FOR_EACH_PTR(bb);
+}
+
+void domtree_build(struct entrypoint *ep)
+{
+ struct basic_block *entry = ep->entry->bb;
+ struct basic_block **doms;
+ struct basic_block *bb;
+ unsigned int size;
+ int max_level = 0;
+ int changed;
+
+ // First calculate the (reverse) postorder.
+ // This will give use us:
+ // - the links to do a reverse postorder traversal
+ // - the order number for each block
+ size = cfg_postorder(ep);
+
+ // initialize the dominators array
+ doms = calloc(size, sizeof(*doms));
+ assert(entry->postorder_nr == size-1);
+ doms[size-1] = entry;
+
+ do {
+ struct basic_block *b;
+
+ changed = 0;
+ FOR_EACH_PTR(ep->bbs, b) {
+ struct basic_block *p;
+ int bnr = b->postorder_nr;
+ struct basic_block *new_idom = NULL;
+
+ if (b == entry)
+ continue; // ignore entry node
+
+ FOR_EACH_PTR(b->parents, p) {
+ unsigned int pnr = p->postorder_nr;
+ if (!doms[pnr])
+ continue;
+ if (!new_idom) {
+ new_idom = p;
+ continue;
+ }
+
+ new_idom = intersect_dom(doms, p, new_idom);
+ } END_FOR_EACH_PTR(p);
+
+ assert(new_idom);
+ if (doms[bnr] != new_idom) {
+ doms[bnr] = new_idom;
+ changed = 1;
+ }
+ } END_FOR_EACH_PTR(b);
+ } while (changed);
+
+ // set the idom links
+ FOR_EACH_PTR(ep->bbs, bb) {
+ struct basic_block *idom = doms[bb->postorder_nr];
+
+ if (bb == entry)
+ continue; // ignore entry node
+
+ bb->idom = idom;
+ add_bb(&idom->doms, bb);
+ } END_FOR_EACH_PTR(bb);
+ entry->idom = NULL;
+
+ // set the dominance levels
+ FOR_EACH_PTR(ep->bbs, bb) {
+ struct basic_block *idom = bb->idom;
+ int level = idom ? idom->dom_level + 1 : 0;
+
+ bb->dom_level = level;
+ if (max_level < level)
+ max_level = level;
+ } END_FOR_EACH_PTR(bb);
+ ep->dom_levels = max_level + 1;
+
+ free(doms);
+ if (dbg_domtree)
+ debug_domtree(ep);
+}
+
+// dt_dominates - does BB a dominates BB b?
+bool domtree_dominates(struct basic_block *a, struct basic_block *b)
+{
+ if (a == b) // dominance is reflexive
+ return true;
+ if (a == b->idom)
+ return true;
+ if (b == a->idom)
+ return false;
+
+ // can't dominate if deeper in the DT
+ if (a->dom_level >= b->dom_level)
+ return false;
+
+ // FIXME: can be faster if we have the DFS in-out numbers
+
+ // walk up the dominator tree
+ for (b = b->idom; b; b = b->idom) {
+ if (b == a)
+ return true;
+ }
+ return false;
+}
diff --git a/flowgraph.h b/flowgraph.h
new file mode 100644
index 00000000..7226c55f
--- /dev/null
+++ b/flowgraph.h
@@ -0,0 +1,13 @@
+#ifndef FLOWGRAPH_H
+#define FLOWGRAPH_H
+
+#include <stdbool.h>
+
+struct entrypoint;
+struct basic_block;
+
+int cfg_postorder(struct entrypoint *ep);
+void domtree_build(struct entrypoint *ep);
+bool domtree_dominates(struct basic_block *a, struct basic_block *b);
+
+#endif
diff --git a/lib.c b/lib.c
index 2d87b567..db456a63 100644
--- a/lib.c
+++ b/lib.c
@@ -289,8 +289,10 @@ int dump_macro_defs = 0;
int dbg_compound = 0;
int dbg_dead = 0;
+int dbg_domtree = 0;
int dbg_entry = 0;
int dbg_ir = 0;
+int dbg_postorder = 0;
unsigned long fdump_ir;
int fmem_report = 0;
@@ -770,8 +772,10 @@ static char **handle_switch_W(char *arg, char **next)
static struct flag debugs[] = {
{ "compound", &dbg_compound},
{ "dead", &dbg_dead},
+ { "domtree", &dbg_domtree},
{ "entry", &dbg_entry},
{ "ir", &dbg_ir},
+ { "postorder", &dbg_postorder},
};
diff --git a/lib.h b/lib.h
index 3cd8560a..8b445204 100644
--- a/lib.h
+++ b/lib.h
@@ -179,8 +179,10 @@ extern int dump_macro_defs;
extern int dbg_compound;
extern int dbg_dead;
+extern int dbg_domtree;
extern int dbg_entry;
extern int dbg_ir;
+extern int dbg_postorder;
extern unsigned int fmax_warnings;
extern int fmem_report;
diff --git a/linearize.c b/linearize.c
index 2e9a5638..a56c272f 100644
--- a/linearize.c
+++ b/linearize.c
@@ -172,6 +172,8 @@ const char *show_pseudo(pseudo_t pseudo)
if (pseudo->ident)
sprintf(buf+i, "(%s)", show_ident(pseudo->ident));
break;
+ case PSEUDO_UNDEF:
+ return "UNDEF";
default:
snprintf(buf, 64, "<bad pseudo type %d>", pseudo->type);
}
@@ -829,6 +831,13 @@ pseudo_t value_pseudo(long long val)
return pseudo;
}
+pseudo_t undef_pseudo(void)
+{
+ pseudo_t pseudo = __alloc_pseudo(0);
+ pseudo->type = PSEUDO_UNDEF;
+ return pseudo;
+}
+
static pseudo_t argument_pseudo(struct entrypoint *ep, int nr)
{
pseudo_t pseudo = __alloc_pseudo(0);
@@ -871,6 +880,42 @@ pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *t
return insn->target;
}
+struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident)
+{
+ struct instruction *phi_node = alloc_typed_instruction(OP_PHI, type);
+ pseudo_t phi;
+
+ phi = alloc_pseudo(phi_node);
+ phi->ident = ident;
+ phi->def = phi_node;
+ phi_node->target = phi;
+ phi_node->bb = bb;
+ return phi_node;
+}
+
+void add_phi_node(struct basic_block *bb, struct instruction *phi_node)
+{
+ struct instruction *insn;
+
+ FOR_EACH_PTR(bb->insns, insn) {
+ enum opcode op = insn->opcode;
+ if (op == OP_PHI)
+ continue;
+ INSERT_CURRENT(phi_node, insn);
+ return;
+ } END_FOR_EACH_PTR(insn);
+
+ // FIXME
+ add_instruction(&bb->insns, phi_node);
+}
+
+struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var)
+{
+ struct instruction *phi_node = alloc_phi_node(bb, var, var->ident);
+ add_phi_node(bb, phi_node);
+ return phi_node;
+}
+
/*
* We carry the "access_data" structure around for any accesses,
* which simplifies things a lot. It contains all the access
diff --git a/linearize.h b/linearize.h
index 1184df98..413bf013 100644
--- a/linearize.h
+++ b/linearize.h
@@ -7,6 +7,7 @@
#include "opcode.h"
#include "parse.h"
#include "symbol.h"
+#include "ptrmap.h"
struct instruction;
@@ -17,10 +18,12 @@ struct pseudo_user {
DECLARE_ALLOCATOR(pseudo_user);
DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user);
+DECLARE_PTRMAP(phi_map, struct symbol *, pseudo_t);
enum pseudo_type {
PSEUDO_VOID,
+ PSEUDO_UNDEF,
PSEUDO_REG,
PSEUDO_SYM,
PSEUDO_VAL,
@@ -99,7 +102,9 @@ struct instruction {
struct multijmp_list *multijmp_list;
};
struct /* phi_node */ {
+ pseudo_t phi_var; // used for SSA conversion
struct pseudo_list *phi_list;
+ unsigned int used:1;
};
struct /* phi source */ {
pseudo_t phi_src;
@@ -265,11 +270,18 @@ struct instruction_list;
struct basic_block {
struct position pos;
unsigned long generation;
- int context;
+ union {
+ int context;
+ int postorder_nr; /* postorder number */
+ int dom_level; /* level in the dominance tree */
+ };
struct entrypoint *ep;
struct basic_block_list *parents; /* sources */
struct basic_block_list *children; /* destinations */
struct instruction_list *insns; /* Linear list of instructions */
+ struct basic_block *idom; /* link to the immediate dominator */
+ struct basic_block_list *doms; /* list of BB idominated by this one */
+ struct phi_map *phi_map;
struct pseudo_list *needs, *defines;
union {
unsigned int nr; /* unique id for label's names */
@@ -330,7 +342,7 @@ static inline void add_pseudo_user_ptr(struct pseudo_user *user, struct pseudo_u
static inline int has_use_list(pseudo_t p)
{
- return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_VAL);
+ return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_UNDEF && p->type != PSEUDO_VAL);
}
static inline int pseudo_user_list_size(struct pseudo_user_list *list)
@@ -391,16 +403,21 @@ struct entrypoint {
struct basic_block_list *bbs;
struct basic_block *active;
struct instruction *entry;
+ unsigned int dom_levels; /* max levels in the dom tree */
};
extern void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi, pseudo_t if_true, pseudo_t if_false);
extern void insert_branch(struct basic_block *bb, struct instruction *br, struct basic_block *target);
struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type);
+struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident);
+struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var);
+void add_phi_node(struct basic_block *bb, struct instruction *phi_node);
pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type);
pseudo_t alloc_pseudo(struct instruction *def);
pseudo_t value_pseudo(long long val);
+pseudo_t undef_pseudo(void);
struct entrypoint *linearize_symbol(struct symbol *sym);
int unssa(struct entrypoint *ep);
diff --git a/optimize.c b/optimize.c
index d23ec2c6..e8cb7fc3 100644
--- a/optimize.c
+++ b/optimize.c
@@ -7,11 +7,13 @@
#include <assert.h>
#include "optimize.h"
+#include "flowgraph.h"
#include "linearize.h"
#include "liveness.h"
#include "flow.h"
#include "cse.h"
#include "ir.h"
+#include "ssa.h"
int repeat_phase;
@@ -53,11 +55,13 @@ void optimize(struct entrypoint *ep)
kill_unreachable_bbs(ep);
ir_validate(ep);
+ domtree_build(ep);
+
/*
* Turn symbols into pseudos
*/
if (fpasses & PASS_MEM2REG)
- simplify_symbol_usage(ep);
+ ssa_convert(ep);
ir_validate(ep);
if (fdump_ir & PASS_MEM2REG)
show_entry(ep);
diff --git a/ptrmap.c b/ptrmap.c
new file mode 100644
index 00000000..ff07e19a
--- /dev/null
+++ b/ptrmap.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Stupid implementation of pointer -> pointer map.
+ *
+ * Copyright (c) 2017 Luc Van Oostenryck.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "ptrmap.h"
+#include "allocate.h"
+#include <stddef.h>
+
+#define MAP_NR 7
+
+struct ptrpair {
+ void *key;
+ void *val;
+};
+struct ptrmap {
+ struct ptrmap *next;
+ int nr;
+ struct ptrpair pairs[MAP_NR];
+};
+
+DECLARE_ALLOCATOR(ptrmap);
+ALLOCATOR(ptrmap, "ptrmap");
+
+void __ptrmap_add(struct ptrmap **mapp, void *key, void *val)
+{
+ struct ptrmap *head = *mapp;
+ struct ptrmap *newmap;
+ struct ptrmap *map;
+ struct ptrpair *pair;
+ int nr;
+
+ if ((map = head)) {
+ struct ptrmap *next = map->next;
+ if (next) // head is full
+ map = next;
+ if ((nr = map->nr) < MAP_NR)
+ goto oldmap;
+ }
+
+ // need a new block
+ newmap = __alloc_ptrmap(0);
+ if (!head) {
+ *mapp = newmap;
+ } else {
+ newmap->next = head->next;
+ head->next = newmap;
+ }
+ map = newmap;
+ nr = 0;
+
+oldmap:
+ pair = &map->pairs[nr];
+ pair->key = key;
+ pair->val = val;
+ map->nr = ++nr;
+}
+
+void *__ptrmap_lookup(struct ptrmap *map, void *key)
+{
+ for (; map; map = map->next) {
+ int i, n = map->nr;
+ for (i = 0; i < n; i++) {
+ struct ptrpair *pair = &map->pairs[i];
+ if (pair->key == key)
+ return pair->val;
+ }
+ }
+ return NULL;
+}
+
+void __ptrmap_update(struct ptrmap **mapp, void *key, void *val)
+{
+ struct ptrmap *map = *mapp;
+
+ for (; map; map = map->next) {
+ int i, n = map->nr;
+ for (i = 0; i < n; i++) {
+ struct ptrpair *pair = &map->pairs[i];
+ if (pair->key == key) {
+ if (pair->val != val)
+ pair->val = val;
+ return;
+ }
+ }
+ }
+
+ __ptrmap_add(mapp, key, val);
+}
diff --git a/ptrmap.h b/ptrmap.h
new file mode 100644
index 00000000..cbbb61da
--- /dev/null
+++ b/ptrmap.h
@@ -0,0 +1,28 @@
+#ifndef PTRMAP_H
+#define PTRMAP_H
+
+struct ptrmap;
+
+#define DECLARE_PTRMAP(name, ktype, vtype) \
+ struct name ## _pair { ktype key; vtype val; }; \
+ struct name { struct name ## _pair block[1]; }; \
+ static inline \
+ void name##_add(struct name **map, ktype k, vtype v) { \
+ __ptrmap_add((struct ptrmap**)map, k, v); \
+ } \
+ static inline \
+ void name##_update(struct name **map, ktype k, vtype v) { \
+ __ptrmap_update((struct ptrmap**)map, k, v); \
+ } \
+ static inline \
+ vtype name##_lookup(struct name *map, ktype k) { \
+ vtype val = __ptrmap_lookup((struct ptrmap*)map, k); \
+ return val; \
+ } \
+
+/* ptrmap.c */
+void __ptrmap_add(struct ptrmap **mapp, void *key, void *val);
+void __ptrmap_update(struct ptrmap **mapp, void *key, void *val);
+void *__ptrmap_lookup(struct ptrmap *map, void *key);
+
+#endif
diff --git a/sparse-llvm.c b/sparse-llvm.c
index 58811309..c7a9fbb7 100644
--- a/sparse-llvm.c
+++ b/sparse-llvm.c
@@ -270,6 +270,9 @@ static const char *pseudo_name(pseudo_t pseudo, char *buf)
case PSEUDO_VOID:
buf[0] = '\0';
break;
+ case PSEUDO_UNDEF:
+ assert(0);
+ break;
default:
assert(0);
}
@@ -387,6 +390,9 @@ static LLVMValueRef pseudo_to_value(struct function *fn, struct symbol *ctype, p
case PSEUDO_VOID:
result = NULL;
break;
+ case PSEUDO_UNDEF:
+ result = LLVMGetUndef(symbol_type(ctype));
+ break;
default:
assert(0);
}
diff --git a/ssa.c b/ssa.c
new file mode 100644
index 00000000..d67c8ded
--- /dev/null
+++ b/ssa.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: MIT
+//
+// SSA conversion
+// Copyright (C) 2005 Luc Van Oostenryck
+//
+
+#include <assert.h>
+#include "lib.h"
+#include "sset.h"
+#include "dominate.h"
+#include "flowgraph.h"
+#include "linearize.h"
+#include "flow.h" // for convert_load_instruction()
+
+
+// Is it possible and desirable for this to be promoted to a pseudo?
+static inline bool is_promotable(struct symbol *type)
+{
+ struct symbol *member;
+ int bf_seen;
+ int nbr;
+
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ switch (type->type) {
+ case SYM_ENUM:
+ case SYM_BITFIELD:
+ case SYM_PTR:
+ case SYM_RESTRICT: // OK, always integer types
+ return 1;
+ case SYM_STRUCT:
+ if (type->bit_size > long_ctype.bit_size)
+ return 0;
+ // we allow a single scalar field
+ // but a run of bitfields count for 1
+ nbr = 0;
+ bf_seen = 0;
+ FOR_EACH_PTR(type->symbol_list, member) {
+ if (is_bitfield_type(member)) {
+ if (bf_seen)
+ continue;
+ bf_seen = 1;
+ } else {
+ bf_seen = 0;
+ }
+ if (!is_scalar_type(member))
+ return 0;
+ if (nbr++)
+ return 0;
+ } END_FOR_EACH_PTR(member);
+ return 1;
+ case SYM_UNION:
+ // FIXME: should be like struct but has problem
+ // when used with/for type cohercion
+ // -----> OK if only same sized integral types
+ FOR_EACH_PTR(type->symbol_list, member) {
+ if (member->bit_size != type->bit_size)
+ return 0;
+ if (!is_integral_type(member))
+ return 0;
+ } END_FOR_EACH_PTR(member);
+ return 1;
+ default:
+ break;
+ }
+ if (type->ctype.base_type == &int_type)
+ return 1;
+ if (type->ctype.base_type == &fp_type)
+ return 1;
+ return 0;
+}
+
+static bool insn_before(struct instruction *a, struct instruction *b)
+{
+ struct basic_block *bb = a->bb;
+ struct instruction *insn;
+
+ assert(b->bb == bb);
+ FOR_EACH_PTR(bb->insns, insn) {
+ if (insn == a)
+ return true;
+ if (insn == b)
+ return false;
+ } END_FOR_EACH_PTR(insn);
+ assert(0);
+}
+
+static void kill_store(struct instruction *insn)
+{
+ remove_use(&insn->src);
+ remove_use(&insn->target);
+ insn->bb = NULL;
+}
+
+static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses)
+{
+ struct instruction *insn;
+ pseudo_t val = NULL;
+
+ if (!bb)
+ return;
+
+ FOR_EACH_PTR(bb->insns, insn) {
+
+ if (!insn->bb || insn->src != addr)
+ continue;
+ switch (insn->opcode) {
+ case OP_LOAD:
+ if (!val)
+ val = undef_pseudo();
+ convert_load_instruction(insn, val);
+ break;
+ case OP_STORE:
+ val = insn->target;
+ // can't use kill_instruction() unless
+ // we add a fake user to val
+ kill_store(insn);
+ break;
+ }
+ } END_FOR_EACH_PTR(insn);
+}
+
+static bool rewrite_single_store(struct instruction *store)
+{
+ pseudo_t addr = store->src;
+ struct pseudo_user *pu;
+
+ FOR_EACH_PTR(addr->users, pu) {
+ struct instruction *insn = pu->insn;
+
+ if (insn->opcode != OP_LOAD)
+ continue;
+
+ // Let's try to replace the value of the load
+ // by the value from the store. This is only valid
+ // if the store dominate the load.
+
+ if (insn->bb == store->bb) {
+ // the load and the store are in the same BB
+ // we can convert if the load is after the store.
+ if (!insn_before(store, insn))
+ continue;
+ } else if (!domtree_dominates(store->bb, insn->bb)) {
+ // we can't convert this load
+ continue;
+ }
+
+ // OK, we can rewrite this load
+
+ // undefs ?
+
+ convert_load_instruction(insn, store->target);
+ } END_FOR_EACH_PTR(pu);
+
+ // is there some unconverted loads?
+ if (pseudo_user_list_size(addr->users) > 1)
+ return false;
+
+ kill_store(store);
+ return true;
+}
+
+static struct sset *processed;
+
+// we would like to know:
+// is there one or more stores?
+// are all loads & stores local/done in a single block?
+static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
+{
+ struct basic_block_list *alpha = NULL;
+ struct basic_block_list *idf = NULL;
+ struct basic_block *samebb = NULL;
+ struct instruction *store = NULL;
+ struct basic_block *bb;
+ struct pseudo_user *pu;
+ unsigned long mod = var->ctype.modifiers;
+ bool local = true;
+ int nbr_stores = 0;
+ int nbr_uses = 0;
+ pseudo_t addr;
+
+ /* Never used as a symbol? */
+ addr = var->pseudo;
+ if (!addr)
+ return;
+
+ /* We don't do coverage analysis of volatiles.. */
+ if (mod & MOD_VOLATILE)
+ return;
+
+ /* ..and symbols with external visibility need more care */
+ mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE);
+ if (mod)
+ goto external_visibility;
+
+ if (!is_promotable(var))
+ return;
+
+ // 1) insert in the worklist all BBs that may modify var
+ sset_reset(processed);
+ FOR_EACH_PTR(addr->users, pu) {
+ struct instruction *insn = pu->insn;
+ struct basic_block *bb = insn->bb;
+
+ switch (insn->opcode) {
+ case OP_STORE:
+ nbr_stores++;
+ store = insn;
+ if (!sset_testset(processed, bb->nr))
+ add_bb(&alpha, bb);
+ /* fall through */
+ case OP_LOAD:
+ if (local) {
+ if (!samebb)
+ samebb = bb;
+ else if (samebb != bb)
+ local = false;
+ }
+ nbr_uses++;
+ break;
+ case OP_SYMADDR:
+ mod |= MOD_ADDRESSABLE;
+ goto external_visibility;
+ default:
+ warning(var->pos, "symbol '%s' pseudo used in unexpected way",
+ show_ident(var->ident));
+ }
+ } END_FOR_EACH_PTR(pu);
+
+ if (nbr_stores == 1) {
+ if (rewrite_single_store(store))
+ return;
+ }
+
+ // if all uses are local to a single block
+ // they can easily be rewritten and doesn't need phi-nodes
+ // FIXME: could be done for extended BB too
+ if (local) {
+ rewrite_local_var(samebb, addr, nbr_stores, nbr_uses);
+ return;
+ }
+
+ idf_compute(ep, &idf, alpha);
+ FOR_EACH_PTR(idf, bb) {
+ struct instruction *node = insert_phi_node(bb, var);
+ node->phi_var = var->pseudo;
+ } END_FOR_EACH_PTR(bb);
+ var->torename = 1;
+
+external_visibility:
+ if (mod & (MOD_NONLOCAL | MOD_STATIC))
+ return;
+ kill_dead_stores(ep, addr, !mod);
+}
+
+static pseudo_t lookup_var(struct basic_block *bb, struct symbol *var)
+{
+ do {
+ pseudo_t val = phi_map_lookup(bb->phi_map, var);
+ if (val)
+ return val;
+ } while ((bb = bb->idom));
+ return undef_pseudo();
+}
+
+static struct instruction_list *phis_all;
+static struct instruction_list *phis_used;
+
+static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
+{
+ struct symbol *var;
+ pseudo_t addr;
+ pseudo_t val;
+
+ switch (insn->opcode) {
+ case OP_STORE:
+ addr = insn->src;
+ if (addr->type != PSEUDO_SYM)
+ break;
+ var = addr->sym;
+ if (!var || !var->torename)
+ break;
+ phi_map_update(&bb->phi_map, var, insn->target);
+ kill_store(insn);
+ break;
+ case OP_LOAD:
+ addr = insn->src;
+ if (addr->type != PSEUDO_SYM)
+ break;
+ var = addr->sym;
+ if (!var || !var->torename)
+ break;
+ val = lookup_var(bb, var);
+ convert_load_instruction(insn, val);
+ break;
+ case OP_PHI:
+ var = insn->type;
+ if (!var || !var->torename)
+ break;
+ phi_map_update(&bb->phi_map, var, insn->target);
+ add_instruction(&phis_all, insn);
+ break;
+ }
+}
+
+static void ssa_rename_insns(struct entrypoint *ep)
+{
+ struct basic_block *bb;
+
+ FOR_EACH_PTR(ep->bbs, bb) {
+ struct instruction *insn;
+ FOR_EACH_PTR(bb->insns, insn) {
+ if (!insn->bb)
+ continue;
+ ssa_rename_insn(bb, insn);
+ } END_FOR_EACH_PTR(insn);
+ } END_FOR_EACH_PTR(bb);
+}
+
+static void mark_phi_used(pseudo_t val)
+{
+ struct instruction *node;
+
+ if (val->type != PSEUDO_REG)
+ return;
+ node = val->def;
+ if (node->opcode != OP_PHI)
+ return;
+ if (node->used)
+ return;
+ node->used = 1;
+ add_instruction(&phis_used, node);
+}
+
+static void ssa_rename_phi(struct instruction *insn)
+{
+ struct basic_block *par;
+ struct symbol *var;
+
+ if (!insn->phi_var)
+ return;
+ var = insn->phi_var->sym;
+ if (!var->torename)
+ return;
+ FOR_EACH_PTR(insn->bb->parents, par) {
+ struct instruction *term = delete_last_instruction(&par->insns);
+ pseudo_t val = lookup_var(par, var);
+ pseudo_t phi = alloc_phi(par, val, var);
+ phi->ident = var->ident;
+ add_instruction(&par->insns, term);
+ use_pseudo(insn, phi, add_pseudo(&insn->phi_list, phi));
+ mark_phi_used(val);
+ } END_FOR_EACH_PTR(par);
+}
+
+static void ssa_rename_phis(struct entrypoint *ep)
+{
+ struct instruction *phi;
+
+ phis_used = NULL;
+ FOR_EACH_PTR(phis_all, phi) {
+ if (has_users(phi->target)) {
+ phi->used = 1;
+ add_instruction(&phis_used, phi);
+ }
+ } END_FOR_EACH_PTR(phi);
+
+ FOR_EACH_PTR(phis_used, phi) {
+ if (!phi->bb)
+ continue;
+ ssa_rename_phi(phi);
+ } END_FOR_EACH_PTR(phi);
+}
+
+void ssa_convert(struct entrypoint *ep)
+{
+ struct basic_block *bb;
+ pseudo_t pseudo;
+ int first, last;
+
+ // calculate the number of BBs
+ first = ep->entry->bb->nr;
+ last = first;
+ FOR_EACH_PTR(ep->bbs, bb) {
+ int nr = bb->nr;
+ if (nr > last)
+ last = nr;
+ } END_FOR_EACH_PTR(bb);
+
+ processed = sset_init(first, last);
+
+ // try to promote memory accesses to pseudos
+ FOR_EACH_PTR(ep->accesses, pseudo) {
+ ssa_convert_one_var(ep, pseudo->sym);
+ } END_FOR_EACH_PTR(pseudo);
+
+ // rename the converted accesses
+ phis_all = phis_used = NULL;
+ ssa_rename_insns(ep);
+ ssa_rename_phis(ep);
+}
diff --git a/ssa.h b/ssa.h
new file mode 100644
index 00000000..cbcbc3a4
--- /dev/null
+++ b/ssa.h
@@ -0,0 +1,6 @@
+#ifndef SSA_H
+#define SSA_H
+
+void ssa_convert(struct entrypoint *ep);
+
+#endif
diff --git a/sset.c b/sset.c
new file mode 100644
index 00000000..e9681e00
--- /dev/null
+++ b/sset.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MIT
+//
+// sset.c - an all O(1) implementation of sparse sets as presented in:
+// "An Efficient Representation for Sparse Sets"
+// by Preston Briggs and Linda Torczon
+//
+// Copyright (C) 2017 - Luc Van Oostenryck
+
+#include "sset.h"
+#include "lib.h"
+#include <stdlib.h>
+
+
+struct sset *sset_init(unsigned int first, unsigned int last)
+{
+ unsigned int size = last - first + 1;
+ struct sset *s = malloc(sizeof(*s) + size * 2 * sizeof(s->sets[0]));
+
+ s->size = size;
+ s->off = first;
+ s->nbr = 0;
+ return s;
+}
+
+void sset_free(struct sset *s)
+{
+ free(s);
+}
diff --git a/sset.h b/sset.h
new file mode 100644
index 00000000..69cee18a
--- /dev/null
+++ b/sset.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT
+
+#ifndef SSET_H
+#define SSET_H
+
+/*
+ * sset.h - an all O(1) implementation of sparse sets as presented in:
+ * "An Efficient Representation for Sparse Sets"
+ * by Preston Briggs and Linda Torczon
+ *
+ * Copyright (C) 2017 - Luc Van Oostenryck
+ */
+
+#include <stdbool.h>
+
+struct sset {
+ unsigned int nbr;
+ unsigned int off;
+ unsigned int size;
+ unsigned int sets[0];
+};
+
+extern struct sset *sset_init(unsigned int size, unsigned int off);
+extern void sset_free(struct sset *s);
+
+
+static inline void sset_reset(struct sset *s)
+{
+ s->nbr = 0;
+}
+
+static inline void sset_add(struct sset *s, unsigned int idx)
+{
+ unsigned int __idx = idx - s->off;
+ unsigned int n = s->nbr++;
+ s->sets[__idx] = n;
+ s->sets[s->size + n] = __idx;
+}
+
+static inline bool sset_test(struct sset *s, unsigned int idx)
+{
+ unsigned int __idx = idx - s->off;
+ unsigned int n = s->sets[__idx];
+
+ return (n < s->nbr) && (s->sets[s->size + n] == __idx);
+}
+
+static inline bool sset_testset(struct sset *s, unsigned int idx)
+{
+ if (sset_test(s, idx))
+ return true;
+ sset_add(s, idx);
+ return false;
+}
+
+#endif
diff --git a/symbol.h b/symbol.h
index 1cad6d05..81c185ee 100644
--- a/symbol.h
+++ b/symbol.h
@@ -175,6 +175,7 @@ struct symbol {
forced_arg:1,
accessed:1,
builtin:1,
+ torename:1,
transparent_union:1;
struct expression *array_size;
struct ctype ctype;
@@ -432,6 +433,24 @@ static inline int is_scalar_type(struct symbol *type)
return 0;
}
+/// return true for integer & pointer types
+static inline bool is_integral_type(struct symbol *type)
+{
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ switch (type->type) {
+ case SYM_ENUM:
+ case SYM_PTR:
+ case SYM_RESTRICT: // OK, always integer types
+ return 1;
+ default:
+ break;
+ }
+ if (type->ctype.base_type == &int_type)
+ return 1;
+ return 0;
+}
+
static inline int is_function(struct symbol *type)
{
return type && type->type == SYM_FN;
diff --git a/validation/crash-select.c b/validation/crash-select.c
new file mode 100644
index 00000000..cec00baf
--- /dev/null
+++ b/validation/crash-select.c
@@ -0,0 +1,18 @@
+struct s {
+ void *b;
+ long c;
+};
+
+long d(void);
+static long f(void)
+{
+ struct s s;
+ s.c = d();
+ if (s.c)
+ s.c = 2;
+ return s.c;
+}
+
+/*
+ * check-name: crash-select
+ */
diff --git a/validation/compound-literal00.c b/validation/linear/compound-literal00.c
index f3069d2c..f3069d2c 100644
--- a/validation/compound-literal00.c
+++ b/validation/linear/compound-literal00.c
diff --git a/validation/linear/compound-literal01.c b/validation/linear/compound-literal01.c
new file mode 100644
index 00000000..e45fade3
--- /dev/null
+++ b/validation/linear/compound-literal01.c
@@ -0,0 +1,18 @@
+struct bfs {
+ int a: 2;
+ int b: 30;
+};
+
+int foo(void)
+{
+ struct bfs bf = { .a = 1, .b = 2 };
+ return (struct bfs[]){bf}[0].b;
+}
+
+/*
+ * check-name: compound-literal01.c
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*\\$2
+ */
diff --git a/validation/compound-literal01.c b/validation/linear/compound-literal02.c
index 8a4935ea..87b98d76 100644
--- a/validation/compound-literal01.c
+++ b/validation/linear/compound-literal02.c
@@ -3,12 +3,6 @@ struct bfs {
int b: 30;
};
-int foo(void)
-{
- struct bfs bf = { .a = 1, .b = 2 };
- return (struct bfs[]){bf}[0].b;
-}
-
int bar(void)
{
struct bfs bf = { .a = 1, .b = 4 };
@@ -16,12 +10,10 @@ int bar(void)
}
/*
- * check-name: compound-literal01.c
+ * check-name: compound-literal02.c
* check-command: test-linearize -Wno-decl $file
*
* check-known-to-fail
* check-output-ignore
- * check-output-contains: ret\\..*\\$2
* check-output-contains: ret\\..*\\$6
- * check-error-end
*/
diff --git a/validation/linear/logical.c b/validation/linear/logical.c
index 148ad427..0f502c6b 100644
--- a/validation/linear/logical.c
+++ b/validation/linear/logical.c
@@ -56,7 +56,7 @@ ou:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi5 <- $1
+ phisrc.32 %phi4 <- $1
load.32 %r11 <- 0[i]
setne.1 %r12 <- %r11, $0
cbr %r12, .L7, .L6
@@ -68,16 +68,16 @@ ou:
trunc.3 %r16 <- (32) %r15
setne.1 %r17 <- %r16, $0
zext.32 %r18 <- (1) %r17
- phisrc.32 %phi6 <- %r18
+ phisrc.32 %phi5 <- %r18
br .L7
.L7:
- phi.32 %r19 <- %phi5, %phi6
- phisrc.32 %phi7(return) <- %r19
+ phi.32 %r19 <- %phi4, %phi5
+ phisrc.32 %phi6(return) <- %r19
br .L5
.L5:
- phi.32 %r20 <- %phi7(return)
+ phi.32 %r20 <- %phi6(return)
ret.32 %r20
@@ -86,7 +86,7 @@ ol:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi9 <- $1
+ phisrc.32 %phi7 <- $1
load.32 %r21 <- 0[i]
setne.1 %r22 <- %r21, $0
cbr %r22, .L11, .L10
@@ -96,16 +96,16 @@ ol:
load.64 %r24 <- 8[%r23]
setne.1 %r25 <- %r24, $0
zext.32 %r26 <- (1) %r25
- phisrc.32 %phi10 <- %r26
+ phisrc.32 %phi8 <- %r26
br .L11
.L11:
- phi.32 %r27 <- %phi9, %phi10
- phisrc.32 %phi11(return) <- %r27
+ phi.32 %r27 <- %phi7, %phi8
+ phisrc.32 %phi9(return) <- %r27
br .L9
.L9:
- phi.32 %r28 <- %phi11(return)
+ phi.32 %r28 <- %phi9(return)
ret.32 %r28
@@ -114,7 +114,7 @@ od:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi13 <- $1
+ phisrc.32 %phi10 <- $1
load.32 %r29 <- 0[i]
setne.1 %r30 <- %r29, $0
cbr %r30, .L15, .L14
@@ -125,16 +125,16 @@ od:
setfval.64 %r33 <- 0.000000e+00
fcmpune.1 %r34 <- %r32, %r33
zext.32 %r35 <- (1) %r34
- phisrc.32 %phi14 <- %r35
+ phisrc.32 %phi11 <- %r35
br .L15
.L15:
- phi.32 %r36 <- %phi13, %phi14
- phisrc.32 %phi15(return) <- %r36
+ phi.32 %r36 <- %phi10, %phi11
+ phisrc.32 %phi12(return) <- %r36
br .L13
.L13:
- phi.32 %r37 <- %phi15(return)
+ phi.32 %r37 <- %phi12(return)
ret.32 %r37
@@ -143,7 +143,7 @@ as:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi17 <- $0
+ phisrc.32 %phi13 <- $0
load.32 %r38 <- 0[i]
setne.1 %r39 <- %r38, $0
cbr %r39, .L18, .L19
@@ -155,16 +155,16 @@ as:
trunc.2 %r43 <- (32) %r42
setne.1 %r44 <- %r43, $0
zext.32 %r45 <- (1) %r44
- phisrc.32 %phi18 <- %r45
+ phisrc.32 %phi14 <- %r45
br .L19
.L19:
- phi.32 %r46 <- %phi18, %phi17
- phisrc.32 %phi19(return) <- %r46
+ phi.32 %r46 <- %phi14, %phi13
+ phisrc.32 %phi15(return) <- %r46
br .L17
.L17:
- phi.32 %r47 <- %phi19(return)
+ phi.32 %r47 <- %phi15(return)
ret.32 %r47
@@ -173,7 +173,7 @@ au:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi21 <- $0
+ phisrc.32 %phi16 <- $0
load.32 %r48 <- 0[i]
setne.1 %r49 <- %r48, $0
cbr %r49, .L22, .L23
@@ -185,16 +185,16 @@ au:
trunc.3 %r53 <- (32) %r52
setne.1 %r54 <- %r53, $0
zext.32 %r55 <- (1) %r54
- phisrc.32 %phi22 <- %r55
+ phisrc.32 %phi17 <- %r55
br .L23
.L23:
- phi.32 %r56 <- %phi22, %phi21
- phisrc.32 %phi23(return) <- %r56
+ phi.32 %r56 <- %phi17, %phi16
+ phisrc.32 %phi18(return) <- %r56
br .L21
.L21:
- phi.32 %r57 <- %phi23(return)
+ phi.32 %r57 <- %phi18(return)
ret.32 %r57
@@ -203,7 +203,7 @@ al:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi25 <- $0
+ phisrc.32 %phi19 <- $0
load.32 %r58 <- 0[i]
setne.1 %r59 <- %r58, $0
cbr %r59, .L26, .L27
@@ -213,16 +213,16 @@ al:
load.64 %r61 <- 8[%r60]
setne.1 %r62 <- %r61, $0
zext.32 %r63 <- (1) %r62
- phisrc.32 %phi26 <- %r63
+ phisrc.32 %phi20 <- %r63
br .L27
.L27:
- phi.32 %r64 <- %phi26, %phi25
- phisrc.32 %phi27(return) <- %r64
+ phi.32 %r64 <- %phi20, %phi19
+ phisrc.32 %phi21(return) <- %r64
br .L25
.L25:
- phi.32 %r65 <- %phi27(return)
+ phi.32 %r65 <- %phi21(return)
ret.32 %r65
@@ -231,7 +231,7 @@ ad:
<entry-point>
store.32 %arg1 -> 0[i]
store.64 %arg2 -> 0[b]
- phisrc.32 %phi29 <- $0
+ phisrc.32 %phi22 <- $0
load.32 %r66 <- 0[i]
setne.1 %r67 <- %r66, $0
cbr %r67, .L30, .L31
@@ -242,16 +242,16 @@ ad:
setfval.64 %r70 <- 0.000000e+00
fcmpune.1 %r71 <- %r69, %r70
zext.32 %r72 <- (1) %r71
- phisrc.32 %phi30 <- %r72
+ phisrc.32 %phi23 <- %r72
br .L31
.L31:
- phi.32 %r73 <- %phi30, %phi29
- phisrc.32 %phi31(return) <- %r73
+ phi.32 %r73 <- %phi23, %phi22
+ phisrc.32 %phi24(return) <- %r73
br .L29
.L29:
- phi.32 %r74 <- %phi31(return)
+ phi.32 %r74 <- %phi24(return)
ret.32 %r74
diff --git a/validation/alias-distinct.c b/validation/mem2reg/alias-distinct.c
index 42937b24..42937b24 100644
--- a/validation/alias-distinct.c
+++ b/validation/mem2reg/alias-distinct.c
diff --git a/validation/alias-mixed.c b/validation/mem2reg/alias-mixed.c
index 0cfbe36b..0cfbe36b 100644
--- a/validation/alias-mixed.c
+++ b/validation/mem2reg/alias-mixed.c
diff --git a/validation/alias-same.c b/validation/mem2reg/alias-same.c
index 55cf4244..55cf4244 100644
--- a/validation/alias-same.c
+++ b/validation/mem2reg/alias-same.c
diff --git a/validation/mem2reg/broken-phi02.c b/validation/mem2reg/broken-phi02.c
index 69776e0f..5aa65071 100644
--- a/validation/mem2reg/broken-phi02.c
+++ b/validation/mem2reg/broken-phi02.c
@@ -22,7 +22,6 @@ int foo(int a, int b)
* is used) causes a missed select-conversion at later stage.
*
* check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
* check-output-ignore
* check-output-contains: select\\.
*/
diff --git a/validation/mem2reg/broken-phi03.c b/validation/mem2reg/broken-phi03.c
index 58b47979..eff1ff8a 100644
--- a/validation/mem2reg/broken-phi03.c
+++ b/validation/mem2reg/broken-phi03.c
@@ -23,7 +23,6 @@ int foo(int a, int b)
* is used) causes a missed select-conversion at later stage.
*
* check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
* check-output-ignore
* check-output-contains: select\\.
*/
diff --git a/validation/mem2reg/cond-expr.c b/validation/mem2reg/cond-expr.c
index f38564ef..8acb00ac 100644
--- a/validation/mem2reg/cond-expr.c
+++ b/validation/mem2reg/cond-expr.c
@@ -10,4 +10,5 @@ int foo(int a, int b, int c)
* check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
* check-output-ignore
* check-output-pattern(2): phi\\.
+ * check-output-pattern(3): phisrc\\.
*/
diff --git a/validation/mem2reg/cond-expr5.c b/validation/mem2reg/cond-expr5.c
index 6c1e1c34..a3ce5e3a 100644
--- a/validation/mem2reg/cond-expr5.c
+++ b/validation/mem2reg/cond-expr5.c
@@ -11,8 +11,11 @@ int foo(int p, int q, int a)
/*
* check-name: cond-expr5
* check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
+ *
* check-output-ignore
* check-output-excludes: load\\.
* check-output-excludes: store\\.
- * check-output-pattern(2): phi\\.
+ * check-output-excludes: phi\\..*, .*, .*
+ * check-output-pattern(3): phi\\.
+ * check-output-pattern(5): phisrc\\.
*/
diff --git a/validation/mem2reg/init-global-array.c b/validation/mem2reg/init-global-array.c
index aea4135a..51ca50e3 100644
--- a/validation/mem2reg/init-global-array.c
+++ b/validation/mem2reg/init-global-array.c
@@ -1,8 +1,11 @@
-struct {
+struct s {
int a[2];
-} s;
+};
-int sarray(void)
+
+static struct s s;
+
+static int sarray(void)
{
s.a[1] = 1;
return s.a[1];
@@ -10,8 +13,9 @@ int sarray(void)
/*
* check-name: init global array
- * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
+ * check-command: test-linearize $file
* check-output-ignore
* check-output-excludes: load\\.
* check-output-pattern(1): store\\.
+ * check-output-pattern(1): ret.32 *\\$1
*/
diff --git a/validation/mem2reg/init-local-array.c b/validation/mem2reg/init-local-array.c
index 2ac53bc7..639a74f1 100644
--- a/validation/mem2reg/init-local-array.c
+++ b/validation/mem2reg/init-local-array.c
@@ -1,25 +1,28 @@
-int array(void)
+static int array(void)
{
int a[2];
a[1] = 1;
+ a[0] = 0;
return a[1];
}
-int sarray(void)
+static int sarray(void)
{
struct {
int a[2];
} s;
s.a[1] = 1;
+ s.a[0] = 0;
return s.a[1];
}
/*
* check-name: init local array
- * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
+ * check-command: test-linearize $file
* check-output-ignore
- * check-output-excludes: load\\.
- * check-output-excludes: store\\.
+ * check-output-excludes: load
+ * check-output-excludes: store
+ * check-output-pattern(2): ret.32 *\\$1
*/
diff --git a/validation/mem2reg/loop02-global.c b/validation/mem2reg/loop02-global.c
index a0a8b42b..b627b33d 100644
--- a/validation/mem2reg/loop02-global.c
+++ b/validation/mem2reg/loop02-global.c
@@ -16,7 +16,7 @@ int foo(void)
/*
* check-name: loop02 global
- * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
+ * check-command: test-linearize -Wno-decl $file
* check-output-ignore
* check-output-excludes: load\\.
*/
diff --git a/validation/mem2reg/missing-return.c b/validation/mem2reg/missing-return.c
new file mode 100644
index 00000000..06f6e4d5
--- /dev/null
+++ b/validation/mem2reg/missing-return.c
@@ -0,0 +1,34 @@
+int f1(void)
+{
+ if (1)
+ return 1;
+}
+
+int f0(void)
+{
+ if (0)
+ return 0;
+}
+
+int fx(int p)
+{
+ if (p)
+ return 0;
+}
+
+int bar(int p)
+{
+ if (p)
+ return 0;
+ p++;
+}
+
+/*
+ * check-name: missing-return
+ * check-command: test-linearize -m32 -fdump-ir=mem2reg -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-pattern(1): ret.32 *\\$1
+ * check-output-pattern(3): ret.32 *UNDEF
+ */
diff --git a/validation/mem2reg/quadra00.c b/validation/mem2reg/quadra00.c
index 63b489c9..69ddc977 100644
--- a/validation/mem2reg/quadra00.c
+++ b/validation/mem2reg/quadra00.c
@@ -20,7 +20,6 @@ int foo(int *a, int b, int c)
/*
* check-name: quadratic phisrc
* check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
* check-output-ignore
* check-output-excludes: phi\\..*, .*, .*
* check-output-excludes: phi\\..*, .*, .*, .*
diff --git a/validation/mem2reg/quadra01.c b/validation/mem2reg/quadra01.c
new file mode 100644
index 00000000..b71f4696
--- /dev/null
+++ b/validation/mem2reg/quadra01.c
@@ -0,0 +1,27 @@
+#include "repeat.h"
+
+void use(void *, void *, void *, void *);
+void *def(void);
+
+#define BLOCK(n) { \
+ void *label; \
+ use(&&w##n, &&x##n, &&y##n, &&z##n); \
+w##n: label = def(); goto *label; \
+x##n: label = def(); goto *label; \
+y##n: label = def(); goto *label; \
+z##n: label = def(); goto *label; \
+}
+
+static void foo(void) {
+ REPEAT2(5, BLOCK)
+}
+
+/*
+ * check-name: quadratic @ liveness
+ * check-command: test-linearize -I. $file
+ * check-timeout:
+ *
+ * check-output-ignore
+ * check-output-excludes: phi\\.
+ * check-output-excludes: phisrc\\.
+ */
diff --git a/validation/mem2reg/quadra02.c b/validation/mem2reg/quadra02.c
new file mode 100644
index 00000000..6475c780
--- /dev/null
+++ b/validation/mem2reg/quadra02.c
@@ -0,0 +1,18 @@
+#include "repeat.h"
+
+#define PAT(X) int a##X = X;
+static void foo(void)
+{
+ REPEAT2(12, PAT)
+}
+
+/*
+ * check-name: quadratic vars
+ * check-command: test-linearize -I. $file
+ * check-timeout:
+ *
+ * check-output-ignore
+ * check-output-excludes: phi\\.
+ * check-output-excludes: phisrc\\.
+ * check-output-excludes: store\\.
+ */
diff --git a/validation/reload-aliasing.c b/validation/mem2reg/reload-aliasing.c
index 3aad317b..3aad317b 100644
--- a/validation/reload-aliasing.c
+++ b/validation/mem2reg/reload-aliasing.c
diff --git a/validation/mem2reg/store-deadborn.c b/validation/mem2reg/store-deadborn.c
new file mode 100644
index 00000000..cca34d59
--- /dev/null
+++ b/validation/mem2reg/store-deadborn.c
@@ -0,0 +1,9 @@
+static void foo(int a)
+{
+ return;
+ a = 0;
+}
+
+/*
+ * check-name: store-deadborn
+ */
diff --git a/validation/linear/stray-phisrc.c b/validation/mem2reg/stray-phisrc.c
index e9f35c89..0da7df7a 100644
--- a/validation/linear/stray-phisrc.c
+++ b/validation/mem2reg/stray-phisrc.c
@@ -18,7 +18,6 @@ static int foo(int **g)
/*
* check-name: stray phisrc
* check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
*
* check-output-ignore
* check-output-excludes: phisrc\\.
diff --git a/validation/mem2reg/struct.c b/validation/mem2reg/struct.c
new file mode 100644
index 00000000..13962a8e
--- /dev/null
+++ b/validation/mem2reg/struct.c
@@ -0,0 +1,32 @@
+struct s {
+ int a;
+ int b;
+};
+
+int f0(void)
+{
+ struct s s;
+
+ s.a = 0;
+ s.b = 1;
+
+ return s.a;
+}
+
+int f1(void)
+{
+ struct s s;
+
+ s.a = 1;
+ s.b = 0;
+
+ return s.b;
+}
+
+/*
+ * check-name: struct
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-pattern(2): ret.32 *\\$0
+ */
diff --git a/validation/mem2reg/undef00.c b/validation/mem2reg/undef00.c
index ba9ba915..20ea962c 100644
--- a/validation/mem2reg/undef00.c
+++ b/validation/mem2reg/undef00.c
@@ -1,14 +1,21 @@
-void bad0(void)
+static int badr(void)
{
int *a;
- *a++;
+ return *a;
+}
+
+static void badw(int v)
+{
+ int *a;
+ *a = v;
}
/*
* check-name: undef00
- * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
- * check-known-to-fail
+ * check-command: test-linearize -fdump-ir=mem2reg $file
* check-output-ignore
* check-output-pattern(1): load\\.
* check-output-pattern(1): load\\..*\\[UNDEF\\]
+ * check-output-pattern(1): store\\.
+ * check-output-pattern(1): store\\..*\\[UNDEF\\]
*/
diff --git a/validation/mem2reg/undef01.c b/validation/mem2reg/undef01.c
new file mode 100644
index 00000000..985c73d6
--- /dev/null
+++ b/validation/mem2reg/undef01.c
@@ -0,0 +1,16 @@
+static void foo(void)
+{
+ int *b;
+ for (;;)
+ *b++ = 0;
+}
+
+/*
+ * check-name: undef01
+ * check-command: sparse -Wmaybe-uninitialized $file
+ * check-known-to-fail
+ *
+ * check-error-start
+crazy04.c:3:13: warning: variable 'b' may be uninitialized
+ * check-error-end
+ */
diff --git a/validation/mem2reg/unused-var.c b/validation/mem2reg/unused-var.c
new file mode 100644
index 00000000..ac394582
--- /dev/null
+++ b/validation/mem2reg/unused-var.c
@@ -0,0 +1,23 @@
+int foo(int a)
+{
+ switch (a) {
+ int u = 1;
+
+ default:
+ return a;
+ }
+}
+
+/*
+ * check-name: unused-var
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ ret.32 %arg1
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/cse-size.c b/validation/optim/cse-size.c
index b75e4bfe..e98c3cde 100644
--- a/validation/optim/cse-size.c
+++ b/validation/optim/cse-size.c
@@ -13,6 +13,5 @@ static void foo(void)
* check-command: test-linearize -Wno-decl $file
*
* check-output-ignore
- * check-output-excludes: phi\\.
- * check-output-excludes: phisrc\\.
+ * check-output-pattern(2): phi\\.
*/
diff --git a/validation/mem2reg/killed-insn.c b/validation/optim/killed-insn.c
index adbef980..d1cdd02e 100644
--- a/validation/mem2reg/killed-insn.c
+++ b/validation/optim/killed-insn.c
@@ -1,14 +1,13 @@
-static int g;
-static void foo(void)
+static void foo(int v)
{
int a[2] = { };
a;
- a[1] = g;
+ a[1] = v;
}
/*
* check-name: killed-insn
- * check-command: test-linearize -fdump-ir=mem2reg $file
+ * check-command: test-linearize $file
*
* check-output-ignore
* check-output-excludes: store\\.
diff --git a/validation/optim/missing-select.c b/validation/optim/missing-select.c
index 4796e058..07ea008f 100644
--- a/validation/optim/missing-select.c
+++ b/validation/optim/missing-select.c
@@ -17,7 +17,6 @@ static int foo(int **g)
/*
* check-name: missing-select
* check-command: test-linearize -Wno-decl $file
- * check-known-to-fail
*
* check-output-ignore
* check-output-contains: select\\.
diff --git a/validation/optim/null-phi.c b/validation/optim/null-phi.c
new file mode 100644
index 00000000..1f9de4d5
--- /dev/null
+++ b/validation/optim/null-phi.c
@@ -0,0 +1,9 @@
+static int foo(void)
+{
+ if (0)
+ return 0;
+}
+
+/*
+ * check-name: null-phi
+ */
diff --git a/validation/repeat.h b/validation/repeat.h
new file mode 100644
index 00000000..83433b2a
--- /dev/null
+++ b/validation/repeat.h
@@ -0,0 +1,24 @@
+#define R0(P, S) P(S)
+#define R1(P, S) R0(P,S##0) R0(P,S##1)
+#define R2(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3)
+#define R3(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3) R0(P,S##4) R0(P,S##5) R0(P,S##6) R0(P,S##7)
+#define R4(P, S) R3(P,S##0) R3(P,S##1)
+#define R5(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3)
+#define R6(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3) R3(P,S##4) R3(P,S##5) R3(P,S##6) R3(P,S##7)
+#define R7(P, S) R6(P,S##0) R6(P,S##1)
+#define R8(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3)
+#define R9(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3) R6(P,S##4) R6(P,S##5) R6(P,S##6) R6(P,S##7)
+#define R10(P, S) R9(P,S##0) R9(P,S##1)
+#define R11(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3)
+#define R12(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3) R9(P,S##4) R9(P,S##5) R9(P,S##6) R9(P,S##7)
+#define R13(P, S) R12(P,S##0) R12(P,S##1)
+#define R14(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3)
+#define R15(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3) R12(P,S##4) R12(P,S##5) R12(P,S##6) R12(P,S##7)
+#define R16(P, S) R15(P,S##0) R15(P,S##1)
+#define R17(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3)
+#define R18(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3) R15(P,S##4) R15(P,S##5) R15(P,S##6) R15(P,S##7)
+#define R19(P, S) R18(P,S##0) R18(P,S##1)
+#define R20(P, S) R18(P,S##0) R18(P,S##1) R18(P,S##2) R18(P,S##3)
+
+#define REPEAT_(RN, P) RN(P,)
+#define REPEAT2(N, P) REPEAT_(R##N,P)
diff --git a/validation/var-undef-partial.c b/validation/var-undef-partial.c
index 2b665834..f1a07bea 100644
--- a/validation/var-undef-partial.c
+++ b/validation/var-undef-partial.c
@@ -16,6 +16,5 @@ int foo(int a, int b)
* check-description: trigger a bug in symbol/memop simplification
* check-description: sparse-llvm is used here as semantic checker of sparse's IR
* check-command: sparse-llvm -Wno-decl $file
- * check-known-to-fail
* check-output-ignore
*/