about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCamille Mougey <commial@gmail.com>2019-01-17 07:48:36 +0100
committerGitHub <noreply@github.com>2019-01-17 07:48:36 +0100
commit04f2665bdc67dc350f469841c748542c70ea9747 (patch)
treea51838c1d0ed49cf79d7e1044352c4c655e755bf
parent28285a330da5fd586e31dceb152a35c6171698ba (diff)
parent10c47fb2530e05efb0b6521c16ae60f6582e09b4 (diff)
downloadmiasm-04f2665bdc67dc350f469841c748542c70ea9747.tar.gz
miasm-04f2665bdc67dc350f469841c748542c70ea9747.zip
Merge pull request #899 from serpilliere/tssa_to_cssa
SSA: fix ssa to cssa, and naive out of cssa
-rw-r--r--example/disasm/full.py90
-rw-r--r--example/ida/graph_ir.py331
-rw-r--r--example/ida/menu.py19
-rw-r--r--miasm2/analysis/data_flow.py208
-rw-r--r--miasm2/analysis/ssa.py634
-rw-r--r--test/analysis/unssa.py652
-rwxr-xr-xtest/test_all.py13
7 files changed, 1686 insertions, 261 deletions
diff --git a/example/disasm/full.py b/example/disasm/full.py
index 4da32f67..153666f2 100644
--- a/example/disasm/full.py
+++ b/example/disasm/full.py
@@ -10,9 +10,11 @@ from miasm2.analysis.data_flow import dead_simp, DiGraphDefUse, \
     ReachingDefinitions, merge_blocks, remove_empty_assignblks, \
     PropagateExpr, replace_stack_vars, load_from_int
 from miasm2.expression.simplifications import expr_simp
-from miasm2.analysis.ssa import SSADiGraph, remove_phi
+from miasm2.analysis.ssa import SSADiGraph, UnSSADiGraph, DiGraphLivenessSSA
 from miasm2.ir.ir import AssignBlock, IRBlock
 
+
+
 log = logging.getLogger("dis")
 console_handler = logging.StreamHandler()
 console_handler.setFormatter(logging.Formatter("%(levelname)-5s: %(message)s"))
@@ -63,6 +65,10 @@ parser.add_argument('-y', "--stack2var", action="store_true",
                     help="*Try* to do transform stack accesses into variables. "
                     "Use only with --propagexpr option. "
                     "WARNING: not reliable, may fail.")
+parser.add_argument('-e', "--loadint", action="store_true",
+                    help="Load integers from binary in fixed memory lookup.")
+parser.add_argument('-j', "--calldontmodstack", action="store_true",
+                    help="Consider stack high is not modified in subcalls")
 
 
 args = parser.parse_args()
@@ -202,12 +208,29 @@ log.info('total lines %s' % total_l)
 if args.propagexpr:
     args.gen_ir = True
 
+
+class IRADelModCallStack(ira):
+
+        def call_effects(self, addr, instr):
+            assignblks, extra = super(IRADelModCallStack, self).call_effects(addr, instr)
+            if not args.calldontmodstack:
+                return assignblks, extra
+            out = []
+            for assignblk in assignblks:
+                dct = dict(assignblk)
+                dct = {
+                    dst:src for (dst, src) in dct.iteritems() if dst != self.sp
+                }
+                out.append(AssignBlock(dct, assignblk.instr))
+            return out, extra
+
+
 # Bonus, generate IR graph
 if args.gen_ir:
     log.info("generating IR and IR analysis")
 
     ir_arch = ir(mdis.loc_db)
-    ir_arch_a = ira(mdis.loc_db)
+    ir_arch_a = IRADelModCallStack(mdis.loc_db)
 
     ircfg = ir_arch.new_ircfg()
     ircfg_a = ir_arch.new_ircfg()
@@ -231,7 +254,9 @@ if args.gen_ir:
         print block
 
     if args.simplify > 0:
+        log.info("dead simp...")
         dead_simp(ir_arch_a, ircfg_a)
+        log.info("ok...")
 
     if args.defuse:
         reachings = ReachingDefinitions(ircfg_a)
@@ -250,7 +275,6 @@ if args.gen_ir:
             modified = False
             modified |= dead_simp(ir_arch_a, ircfg_a)
             modified |= remove_empty_assignblks(ircfg_a)
-            modified |= merge_blocks(ircfg_a, entry_points)
 
         open('graph_irflow_reduced.dot', 'w').write(ircfg_a.dot())
 
@@ -281,7 +305,6 @@ if args.propagexpr:
                         out[reg] = dst
             return set(out.values())
 
-
     # Add dummy dependency to uncover out regs affectation
     for loc in ircfg_a.leaves():
         irblock = ircfg_a.blocks.get(loc)
@@ -327,27 +350,22 @@ if args.propagexpr:
 
     head = list(entry_points)[0]
     heads = set([head])
-    all_ssa_vars = set()
+    all_ssa_vars = {}
 
     propagate_expr = PropagateExpr()
-
-
+    ssa_variable_to_expr = {}
 
     while modified:
         ssa = SSADiGraph(ircfg_a)
         ssa.immutable_ids.update(ssa_forbidden_regs)
-
+        ssa.ssa_variable_to_expr.update(all_ssa_vars)
         ssa.transform(head)
-
-        all_ssa_vars.update(ssa._ssa_variable_to_expr)
-
-        ssa_regs = [reg for reg in ssa.expressions if reg.is_id()]
-        ssa_forbidden_regs.update(ssa_regs)
+        all_ssa_vars.update(ssa.ssa_variable_to_expr)
 
         if args.verbose > 3:
             open("ssa_%d.dot" % index, "wb").write(ircfg_a.dot())
 
-        ir_arch_a.ssa_var.update(ssa._ssa_variable_to_expr)
+        ir_arch_a.ssa_var.update(ssa.ssa_variable_to_expr)
         if args.verbose > 3:
             open("ssa_orig.dot", "wb").write(ircfg_a.dot())
 
@@ -369,13 +387,16 @@ if args.propagexpr:
                 if args.verbose > 3:
                     open('tmp_before_%d.dot' % index, 'w').write(ircfg_a.dot())
                 simp_modified = False
+                log.info("dead simp...")
                 simp_modified |= dead_simp(ir_arch_a, ircfg_a)
+                log.info("ok...")
+
                 index += 1
                 if args.verbose > 3:
                     open('tmp_after_%d.dot' % index, 'w').write(ircfg_a.dot())
                 simp_modified |= remove_empty_assignblks(ircfg_a)
-                simp_modified |= merge_blocks(ircfg_a, heads)
-                simp_modified |= load_from_int(ircfg_a, bs, is_addr_ro_variable)
+                if args.loadint:
+                    simp_modified |= load_from_int(ircfg_a, bs, is_addr_ro_variable)
                 modified |= simp_modified
                 index += 1
         if args.verbose > 3:
@@ -386,19 +407,46 @@ if args.propagexpr:
     if args.verbose > 3:
         open('final_pre.dot', 'w').write(ircfg_a.dot())
 
-    merge_blocks(ircfg_a, heads)
     if args.verbose > 3:
         open('final_merge.dot', 'w').write(ircfg_a.dot())
     ssa = SSADiGraph(ircfg_a)
     ssa.immutable_ids.update(ssa_forbidden_regs)
+    ssa.ssa_variable_to_expr.update(all_ssa_vars)
     ssa.transform(head)
-    all_ssa_vars.update(ssa._ssa_variable_to_expr)
     print '*'*80, "Remove phi"
-    ssa._ssa_variable_to_expr = all_ssa_vars
     if args.verbose > 3:
         open('final_ssa.dot', 'w').write(ircfg_a.dot())
-    remove_phi(ssa, head)
+
+    cfg_liveness = DiGraphLivenessSSA(ircfg_a)
+    cfg_liveness.init_var_info(ir_arch_a)
+    cfg_liveness.compute_liveness()
+
+    unssa = UnSSADiGraph(ssa, head, cfg_liveness)
+
     if args.verbose > 3:
         open('final_no_phi.dot', 'w').write(ircfg_a.dot())
-    dead_simp(ir_arch_a, ircfg_a)
+
+    modified = True
+    while modified:
+        log.debug('Loop %d', index)
+        index += 1
+        modified = False
+        modified |= ircfg_a.simplify(expr_simp)
+        if args.verbose > 3:
+            open('tmp_simp_%d.dot' % index, 'w').write(ircfg_a.dot())
+        simp_modified = True
+        while simp_modified:
+            index += 1
+            if args.verbose > 3:
+                open('tmp_before_%d.dot' % index, 'w').write(ircfg_a.dot())
+            simp_modified = False
+            simp_modified |= dead_simp(ir_arch_a, ircfg_a)
+            index += 1
+            if args.verbose > 3:
+                open('tmp_after_%d.dot' % index, 'w').write(ircfg_a.dot())
+            simp_modified |= remove_empty_assignblks(ircfg_a)
+            simp_modified |= merge_blocks(ircfg_a, heads)
+            modified |= simp_modified
+            index += 1
+
     open('final.dot', 'w').write(ircfg_a.dot())
diff --git a/example/ida/graph_ir.py b/example/ida/graph_ir.py
index b204b2b8..b04979ac 100644
--- a/example/ida/graph_ir.py
+++ b/example/ida/graph_ir.py
@@ -2,23 +2,81 @@ import os
 import tempfile
 
 import idaapi
+import ida_kernwin
 import idc
+import ida_funcs
 import idautils
-
-from miasm2.core.bin_stream_ida import bin_stream_ida
 from miasm2.core.asmblock import is_int
+from miasm2.core.bin_stream_ida import bin_stream_ida
 from miasm2.expression.simplifications import expr_simp
+from miasm2.ir.ir import IRBlock, AssignBlock
+
+from miasm2.analysis.ssa import SSADiGraph, UnSSADiGraph, DiGraphLivenessSSA
+
+from miasm2.analysis.data_flow import dead_simp,  \
+    merge_blocks, remove_empty_assignblks, \
+    PropagateExpr, load_from_int
 
-from miasm2.analysis.data_flow import dead_simp, DiGraphDefUse, \
-    ReachingDefinitions, merge_blocks, remove_empty_assignblks, \
-    PropagateExpr, replace_stack_vars, load_from_int, dead_simp, remove_empty_assignblks, \
-    read_mem, get_memlookup
 
-from miasm2.expression.simplifications import expr_simp
-from miasm2.analysis.ssa import SSADiGraph, remove_phi
-from miasm2.ir.ir import AssignBlock, IRBlock
 from utils import guess_machine, expr2colorstr
-from miasm2.expression.expression import ExprLoc, ExprMem, ExprId, ExprInt
+
+
+
+
+TYPE_GRAPH_IR = 0
+TYPE_GRAPH_IRSSA = 1
+TYPE_GRAPH_IRSSAUNSSA = 2
+
+OPTION_GRAPH_CODESIMPLIFY = 1
+OPTION_GRAPH_DONTMODSTACK = 2
+OPTION_GRAPH_LOADMEMINT = 4
+
+
+class GraphIRForm(ida_kernwin.Form):
+
+    def __init__(self):
+        ida_kernwin.Form.__init__(
+            self,
+            r"""BUTTON YES* Launch
+BUTTON CANCEL NONE
+Graph IR Settings
+
+{FormChangeCb}
+Analysis:
+<Graph IR :{rGraphIR}>
+<Graph IR + SSA :{rGraphIRSSA}>
+<Graph IR + SSA + UnSSA :{rGraphIRSSAUNSSA}>{cScope}>
+
+Options:
+<Simplify code:{rCodeSimplify}>
+<Subcalls dont change stack:{rDontModStack}>
+<Load static memory:{rLoadMemInt}>{cOptions}>
+""",
+            {
+                'FormChangeCb': ida_kernwin.Form.FormChangeCb(self.OnFormChange),
+                'cScope': ida_kernwin.Form.RadGroupControl(
+                    (
+                        "rGraphIR",
+                        "rGraphIRSSA",
+                        "rGraphIRSSAUNSSA"
+                    )
+                ),
+                'cOptions': ida_kernwin.Form.ChkGroupControl(
+                    (
+                        "rCodeSimplify",
+                        "rDontModStack",
+                        "rLoadMemInt"
+                    )
+                ),
+            }
+        )
+        form, _ = self.Compile()
+        form.rCodeSimplify.checked = True
+        form.rDontModStack.checked = False
+        form.rLoadMemInt.checked = False
+
+    def OnFormChange(self, _):
+        return 1
 
 
 # Override Miasm asmblock default label naming convention to shrink block size
@@ -34,17 +92,17 @@ def label_init(self, name="", offset=None):
         self.offset = None
     else:
         self.offset = int(offset)
+
+
 def label_str(self):
     if isinstance(self.offset, (int, long)):
         return "%s:0x%x" % (self.name, self.offset)
-    else:
-        return "%s:%s" % (self.name, str(self.offset))
-
+    return "%s:%s" % (self.name, str(self.offset))
 
 
 def color_irblock(irblock, ir_arch):
     out = []
-    lbl = idaapi.COLSTR(ir_arch.loc_db.pretty_str(irblock.loc_key), idaapi.SCOLOR_INSN)
+    lbl = idaapi.COLSTR("%s:" % ir_arch.loc_db.pretty_str(irblock.loc_key), idaapi.SCOLOR_INSN)
     out.append(lbl)
     for assignblk in irblock:
         for dst, src in sorted(assignblk.iteritems()):
@@ -54,9 +112,6 @@ def color_irblock(irblock, ir_arch):
             out.append('    %s' % line)
         out.append("")
     out.pop()
-    dst = str('    Dst: %s' % irblock.dst)
-    dst = idaapi.COLSTR(dst, idaapi.SCOLOR_RPTCMT)
-    out.append(dst)
     return "\n".join(out)
 
 
@@ -93,10 +148,10 @@ class GraphMiasmIR(idaapi.GraphViewer):
     def OnGetText(self, node_id):
         return str(self[node_id])
 
-    def OnSelect(self, node_id):
+    def OnSelect(self, _):
         return True
 
-    def OnClick(self, node_id):
+    def OnClick(self, _):
         return True
 
     def Show(self):
@@ -105,12 +160,40 @@ class GraphMiasmIR(idaapi.GraphViewer):
         return True
 
 
-def build_graph(verbose=False, simplify=False, ssa=False, ssa_simplify=False):
-    start_addr = idc.ScreenEA()
+def is_addr_ro_variable(bs, addr, size):
+    """
+    Return True if address at @addr is a read-only variable.
+    WARNING: Quick & Dirty
+
+    @addr: integer representing the address of the variable
+    @size: size in bits
 
+    """
+    try:
+        _ = bs.getbytes(addr, size/8)
+    except IOError:
+        return False
+    return True
+
+
+def build_graph(start_addr, type_graph, simplify=False, dontmodstack=True, loadint=False, verbose=False):
     machine = guess_machine(addr=start_addr)
     dis_engine, ira = machine.dis_engine, machine.ira
 
+    class IRADelModCallStack(ira):
+        def call_effects(self, addr, instr):
+            assignblks, extra = super(IRADelModCallStack, self).call_effects(addr, instr)
+            if not dontmodstack:
+                return assignblks, extra
+            out = []
+            for assignblk in assignblks:
+                dct = dict(assignblk)
+                dct = {
+                    dst:src for (dst, src) in dct.iteritems() if dst != self.sp
+                }
+                out.append(AssignBlock(dct, assignblk.instr))
+            return out, extra
+
 
     if verbose:
         print "Arch", dis_engine
@@ -121,7 +204,8 @@ def build_graph(verbose=False, simplify=False, ssa=False, ssa_simplify=False):
 
     bs = bin_stream_ida()
     mdis = dis_engine(bs)
-    ir_arch = ira(mdis.loc_db)
+    ir_arch = IRADelModCallStack(mdis.loc_db)
+
 
     # populate symbols with ida names
     for addr, name in idautils.Names():
@@ -136,15 +220,13 @@ def build_graph(verbose=False, simplify=False, ssa=False, ssa_simplify=False):
     if verbose:
         print "start disasm"
     if verbose:
-        print hex(addr)
+        print hex(start_addr)
 
     asmcfg = mdis.dis_multiblock(start_addr)
-
     entry_points = set([mdis.loc_db.get_offset_location(start_addr)])
     if verbose:
         print "generating graph"
         open('asm_flow.dot', 'w').write(asmcfg.dot())
-
         print "generating IR... %x" % start_addr
 
     ircfg = ir_arch.new_ircfg_from_asmcfg(asmcfg)
@@ -167,9 +249,9 @@ def build_graph(verbose=False, simplify=False, ssa=False, ssa_simplify=False):
         open(os.path.join(tempfile.gettempdir(), 'graph.dot'), 'wb').write(out)
     title = "Miasm IR graph"
 
+
     if simplify:
         dead_simp(ir_arch, ircfg)
-
         ircfg.simplify(expr_simp)
         modified = True
         while modified:
@@ -179,126 +261,143 @@ def build_graph(verbose=False, simplify=False, ssa=False, ssa_simplify=False):
             modified |= merge_blocks(ircfg, entry_points)
         title += " (simplified)"
 
-    graph = GraphMiasmIR(ircfg, title, None)
-
-    if ssa:
-        if len(entry_points) != 1:
-            raise RuntimeError("Your graph should have only one head")
-        head = list(entry_points)[0]
-        ssa = SSADiGraph(ircfg)
-        ssa.transform(head)
-        title += " (SSA)"
-        graph = GraphMiasmIR(ssa.graph, title, None)
-
-    if ssa_simplify:
-        class IRAOutRegs(ira):
-            def get_out_regs(self, block):
-                regs_todo = super(self.__class__, self).get_out_regs(block)
-                out = {}
-                for assignblk in block:
-                    for dst in assignblk:
-                        reg = self.ssa_var.get(dst, None)
-                        if reg is None:
-                            continue
-                        if reg in regs_todo:
-                            out[reg] = dst
-                return set(out.values())
-
-        # Add dummy dependency to uncover out regs affectation
-        for loc in ircfg.leaves():
-            irblock = ircfg.blocks.get(loc)
-            if irblock is None:
-                continue
-            regs = {}
-            for reg in ir_arch.get_out_regs(irblock):
-                regs[reg] = reg
-            assignblks = list(irblock)
-            new_assiblk = AssignBlock(regs, assignblks[-1].instr)
-            assignblks.append(new_assiblk)
-            new_irblock = IRBlock(irblock.loc_key, assignblks)
-            ircfg.blocks[loc] = new_irblock
-
+    if type_graph == TYPE_GRAPH_IR:
+        graph = GraphMiasmIR(ircfg, title, None)
+        graph.Show()
+        return
 
+    head = list(entry_points)[0]
 
-        ir_arch = IRAOutRegs(mdis.loc_db)
 
-        def is_addr_ro_variable(bs, addr, size):
-            """
-            Return True if address at @addr is a read-only variable.
-            WARNING: Quick & Dirty
+    class IRAOutRegs(ira):
+        def get_out_regs(self, block):
+            regs_todo = super(IRAOutRegs, self).get_out_regs(block)
+            out = {}
+            for assignblk in block:
+                for dst in assignblk:
+                    reg = self.ssa_var.get(dst, None)
+                    if reg is None:
+                        continue
+                    if reg in regs_todo:
+                        out[reg] = dst
+            return set(out.values())
 
-            @addr: integer representing the address of the variable
-            @size: size in bits
 
-            """
-            try:
-                _ = bs.getbytes(addr, size/8)
-            except IOError:
-                return False
-            return True
-
-
-        ir_arch.ssa_var = {}
-        index = 0
-        modified = True
-        ssa_forbidden_regs = set([
-            ir_arch.pc,
-            ir_arch.IRDst,
-            ir_arch.arch.regs.exception_flags
-        ])
 
-        head = list(entry_points)[0]
-        heads = set([head])
-        all_ssa_vars = set()
-
-        propagate_expr = PropagateExpr()
+    # Add dummy dependency to uncover out regs affectation
+    for loc in ircfg.leaves():
+        irblock = ircfg.blocks.get(loc)
+        if irblock is None:
+            continue
+        regs = {}
+        for reg in ir_arch.get_out_regs(irblock):
+            regs[reg] = reg
+        assignblks = list(irblock)
+        new_assiblk = AssignBlock(regs, assignblks[-1].instr)
+        assignblks.append(new_assiblk)
+        new_irblock = IRBlock(irblock.loc_key, assignblks)
+        ircfg.blocks[loc] = new_irblock
+
+    ir_arch = IRAOutRegs(mdis.loc_db)
+    ir_arch.ssa_var = {}
+    modified = True
+    ssa_forbidden_regs = set([
+        ir_arch.pc,
+        ir_arch.IRDst,
+        ir_arch.arch.regs.exception_flags
+    ])
+
+    head = list(entry_points)[0]
+    heads = set([head])
+    all_ssa_vars = {}
+
+    propagate_expr = PropagateExpr()
+
+    ssa = SSADiGraph(ircfg)
+    ssa.immutable_ids.update(ssa_forbidden_regs)
+    ssa.ssa_variable_to_expr.update(all_ssa_vars)
+    ssa.transform(head)
+    all_ssa_vars.update(ssa.ssa_variable_to_expr)
+
+    ir_arch.ssa_var.update(ssa.ssa_variable_to_expr)
 
+    if simplify:
 
         while modified:
             ssa = SSADiGraph(ircfg)
             ssa.immutable_ids.update(ssa_forbidden_regs)
-
+            ssa.ssa_variable_to_expr.update(all_ssa_vars)
             ssa.transform(head)
+            all_ssa_vars.update(ssa.ssa_variable_to_expr)
 
-            all_ssa_vars.update(ssa._ssa_variable_to_expr)
-
-            ssa_regs = [reg for reg in ssa.expressions if reg.is_id()]
-            ssa_forbidden_regs.update(ssa_regs)
-
-
-            ir_arch.ssa_var.update(ssa._ssa_variable_to_expr)
+            ir_arch.ssa_var.update(ssa.ssa_variable_to_expr)
 
             while modified:
-                index += 1
                 modified = False
                 modified |= propagate_expr.propagate(ssa, head)
                 modified |= ircfg.simplify(expr_simp)
                 simp_modified = True
                 while simp_modified:
-                    index += 1
                     simp_modified = False
                     simp_modified |= dead_simp(ir_arch, ircfg)
-                    index += 1
                     simp_modified |= remove_empty_assignblks(ircfg)
-                    simp_modified |= merge_blocks(ircfg, heads)
                     simp_modified |= load_from_int(ircfg, bs, is_addr_ro_variable)
                     modified |= simp_modified
-                    index += 1
 
 
-        merge_blocks(ircfg, heads)
-        ssa = SSADiGraph(ircfg)
-        ssa.immutable_ids.update(ssa_forbidden_regs)
-        ssa.transform(head)
-        all_ssa_vars.update(ssa._ssa_variable_to_expr)
-        ssa._ssa_variable_to_expr = all_ssa_vars
-        dead_simp(ir_arch, ssa.graph)
+    ssa = SSADiGraph(ircfg)
+    ssa.immutable_ids.update(ssa_forbidden_regs)
+    ssa.ssa_variable_to_expr.update(all_ssa_vars)
+    ssa.transform(head)
+    all_ssa_vars.update(ssa.ssa_variable_to_expr)
 
-        title += " (SSA Simplified)"
+    if type_graph == TYPE_GRAPH_IRSSA:
         graph = GraphMiasmIR(ssa.graph, title, None)
+        graph.Show()
+        return
+
+    if type_graph == TYPE_GRAPH_IRSSAUNSSA:
+
+        cfg_liveness = DiGraphLivenessSSA(ssa.graph)
+        cfg_liveness.init_var_info(ir_arch)
+        cfg_liveness.compute_liveness()
+
+        UnSSADiGraph(ssa, head, cfg_liveness)
+        if simplify:
+            modified = True
+            while modified:
+                modified = False
+                modified |= ssa.graph.simplify(expr_simp)
+                simp_modified = True
+                while simp_modified:
+                    simp_modified = False
+                    simp_modified |= dead_simp(ir_arch, ssa.graph)
+                    simp_modified |= remove_empty_assignblks(ssa.graph)
+                    simp_modified |= merge_blocks(ssa.graph, heads)
+                    modified |= simp_modified
+        graph = GraphMiasmIR(ssa.graph, title, None)
+        graph.Show()
+
+
+def function_graph_ir():
+    # Get settings
+    settings = GraphIRForm()
+    ret = settings.Execute()
+    if not ret:
+        return
 
+    func = ida_funcs.get_func(idc.ScreenEA())
+    func_addr = func.startEA
 
-    graph.Show()
+    build_graph(
+        func_addr,
+        settings.cScope.value,
+        simplify=settings.cOptions.value & OPTION_GRAPH_CODESIMPLIFY,
+        dontmodstack=settings.cOptions.value & OPTION_GRAPH_DONTMODSTACK,
+        loadint=settings.cOptions.value & OPTION_GRAPH_LOADMEMINT,
+        verbose=False
+    )
+    return
 
 if __name__ == "__main__":
-    build_graph(verbose=True, simplify=False)
+    function_graph_ir()
diff --git a/example/ida/menu.py b/example/ida/menu.py
index a0eecf67..f98bd75e 100644
--- a/example/ida/menu.py
+++ b/example/ida/menu.py
@@ -3,13 +3,12 @@
 - Miasm > Symbolic execution (icon 81, F3)
 - Miasm > Dependency graph (icon 79, F4)
 - Miasm > Graph IR (icon 188, F7)
-- Miasm > Graph IR (simplified) (icon 191, F8)
 - Miasm > RPYC server (icon 182, F10)
 - Miasm > Type propagation (icon 38, F11)
 """
 
 from symbol_exec import symbolic_exec
-from graph_ir import build_graph
+from graph_ir import function_graph_ir
 try:
     from rpyc_ida import serve_threaded
 except ImportError:
@@ -59,22 +58,10 @@ handler_symb.attach_to_menu("Miasm/Symbolic exec")
 handler_depgraph = Handler(launch_depgraph)
 handler_depgraph.register("miasm:depgraph", "Dependency graph", shortcut="F4", icon=79)
 handler_depgraph.attach_to_menu("Miasm/Dependency graph")
-handler_graph = Handler(build_graph)
+
+handler_graph = Handler(function_graph_ir)
 handler_graph.register("miasm:graphir", "Graph IR", shortcut="F7", icon=188)
 handler_graph.attach_to_menu("Miasm/Graph IR")
-handler_graph_simp = Handler(lambda: build_graph(simplify=True))
-handler_graph_simp.register("miasm:graphirsimp",
-                            "Graph IR (simplified)", shortcut="F8", icon=191)
-handler_graph_simp.attach_to_menu("Miasm/Graph IR (simplified)")
-handler_graph_simp = Handler(lambda: build_graph(simplify=True, ssa=True))
-handler_graph_simp.register("miasm:graphirssa",
-                            "Graph IR (SSA)", shortcut="F8", icon=191)
-handler_graph_simp.attach_to_menu("Miasm/Graph IR (SSA)")
-
-handler_graph_simp = Handler(lambda: build_graph(simplify=True, ssa_simplify=True))
-handler_graph_simp.register("miasm:graphirssasimple",
-                            "Graph IR (SSA Simplified)", shortcut="F8", icon=191)
-handler_graph_simp.attach_to_menu("Miasm/Graph IR (SSA Simplified)")
 
 if serve_threaded is not None:
     handler_rpyc = Handler(serve_threaded)
diff --git a/miasm2/analysis/data_flow.py b/miasm2/analysis/data_flow.py
index 23e2f77e..b90bdaf9 100644
--- a/miasm2/analysis/data_flow.py
+++ b/miasm2/analysis/data_flow.py
@@ -3,7 +3,8 @@
 from collections import namedtuple
 from miasm2.core.graph import DiGraph
 from miasm2.ir.ir import AssignBlock, IRBlock
-from miasm2.expression.expression import ExprLoc, ExprMem, ExprId, ExprInt
+from miasm2.expression.expression import ExprLoc, ExprMem, ExprId, ExprInt,\
+    ExprAssign
 from miasm2.expression.simplifications import expr_simp
 from miasm2.core.interval import interval
 
@@ -1006,3 +1007,208 @@ def load_from_int(ir_arch, bs, is_addr_ro_variable):
         block = IRBlock(block.loc_key, assignblks)
         ir_arch.blocks[block.loc_key] = block
     return modified
+
+
+class AssignBlockLivenessInfos(object):
+    """
+    Description of live in / live out of an AssignBlock
+    """
+
+    __slots__ = ["gen", "kill", "var_in", "var_out", "live", "assignblk"]
+
+    def __init__(self, assignblk, gen, kill):
+        self.gen = gen
+        self.kill = kill
+        self.var_in = set()
+        self.var_out = set()
+        self.live = set()
+        self.assignblk = assignblk
+
+    def __str__(self):
+        out = []
+        out.append("\tVarIn:" + ", ".join(str(x) for x in self.var_in))
+        out.append("\tGen:" + ", ".join(str(x) for x in self.gen))
+        out.append("\tKill:" + ", ".join(str(x) for x in self.kill))
+        out.append(
+            '\n'.join(
+                "\t%s = %s" % (dst, src)
+                for (dst, src) in self.assignblk.iteritems()
+            )
+        )
+        out.append("\tVarOut:" + ", ".join(str(x) for x in self.var_out))
+        return '\n'.join(out)
+
+
+class IRBlockLivenessInfos(object):
+    """
+    Description of live in / live out of an AssignBlock
+    """
+    __slots__ = ["loc_key", "infos", "assignblks"]
+
+
+    def __init__(self, irblock):
+        self.loc_key = irblock.loc_key
+        self.infos = []
+        self.assignblks = []
+        for assignblk in irblock:
+            gens, kills = set(), set()
+            for dst, src in assignblk.iteritems():
+                expr = ExprAssign(dst, src)
+                read = expr.get_r(mem_read=True)
+                write = expr.get_w()
+                gens.update(read)
+                kills.update(write)
+            self.infos.append(AssignBlockLivenessInfos(assignblk, gens, kills))
+            self.assignblks.append(assignblk)
+
+    def __getitem__(self, index):
+        """Getitem on assignblks"""
+        return self.assignblks.__getitem__(index)
+
+    def __str__(self):
+        out = []
+        out.append("%s:" % self.loc_key)
+        for info in self.infos:
+            out.append(str(info))
+            out.append('')
+        return "\n".join(out)
+
+
+class DiGraphLiveness(DiGraph):
+    """
+    DiGraph representing variable liveness
+    """
+
+    def __init__(self, ircfg, loc_db=None):
+        super(DiGraphLiveness, self).__init__()
+        self.ircfg = ircfg
+        self.loc_db = loc_db
+        self._blocks = {}
+        # Add irblocks gen/kill
+        for node in ircfg.nodes():
+            irblock = ircfg.blocks[node]
+            irblockinfos = IRBlockLivenessInfos(irblock)
+            self.add_node(irblockinfos.loc_key)
+            self.blocks[irblockinfos.loc_key] = irblockinfos
+            for succ in ircfg.successors(node):
+                self.add_uniq_edge(node, succ)
+            for pred in ircfg.predecessors(node):
+                self.add_uniq_edge(pred, node)
+
+    @property
+    def blocks(self):
+        return self._blocks
+
+    def init_var_info(self):
+        """Add ircfg out regs"""
+        raise NotImplementedError("Abstract method")
+
+    def node2lines(self, node):
+        """
+        Output liveness information in dot format
+        """
+        if self.loc_db is None:
+            node_name = str(node)
+        else:
+            names = self.loc_db.get_location_names(node)
+            if not names:
+                node_name = self.loc_db.pretty_str(node)
+            else:
+                node_name = "".join("%s:\n" % name for name in names)
+        yield self.DotCellDescription(
+            text="%s" % node_name,
+            attr={
+                'align': 'center',
+                'colspan': 2,
+                'bgcolor': 'grey',
+            }
+        )
+        if node not in self._blocks:
+            yield [self.DotCellDescription(text="NOT PRESENT", attr={})]
+            raise StopIteration
+
+        for i, info in enumerate(self._blocks[node].infos):
+            var_in = "VarIn:" + ", ".join(str(x) for x in info.var_in)
+            var_out = "VarOut:" + ", ".join(str(x) for x in info.var_out)
+
+            assignmnts = ["%s = %s" % (dst, src) for (dst, src) in info.assignblk.iteritems()]
+
+            if i == 0:
+                yield self.DotCellDescription(
+                    text=var_in,
+                    attr={
+                        'bgcolor': 'green',
+                    }
+                )
+
+            for assign in assignmnts:
+                yield self.DotCellDescription(text=assign, attr={})
+            yield self.DotCellDescription(
+                text=var_out,
+                attr={
+                    'bgcolor': 'green',
+                }
+            )
+            yield self.DotCellDescription(text="", attr={})
+
+    def back_propagate_compute(self, block):
+        """
+        Compute the liveness information in the @block.
+        @block: AssignBlockLivenessInfos instance
+        """
+        infos = block.infos
+        modified = False
+        for i in reversed(xrange(len(infos))):
+            new_vars = set(infos[i].gen.union(infos[i].var_out.difference(infos[i].kill)))
+            if infos[i].var_in != new_vars:
+                modified = True
+                infos[i].var_in = new_vars
+            if i > 0 and infos[i - 1].var_out != set(infos[i].var_in):
+                modified = True
+                infos[i - 1].var_out = set(infos[i].var_in)
+        return modified
+
+    def back_propagate_to_parent(self, todo, node, parent):
+        """
+        Back propagate the liveness information from @node to @parent.
+        @node: loc_key of the source node
+        @parent: loc_key of the node to update
+        """
+        parent_block = self.blocks[parent]
+        cur_block = self.blocks[node]
+        if cur_block.infos[0].var_in == parent_block.infos[-1].var_out:
+            return
+        var_info = cur_block.infos[0].var_in.union(parent_block.infos[-1].var_out)
+        parent_block.infos[-1].var_out = var_info
+        todo.add(parent)
+
+    def compute_liveness(self):
+        """
+        Compute the liveness information for the digraph.
+        """
+        todo = set(self.leaves())
+        while todo:
+            node = todo.pop()
+            cur_block = self.blocks[node]
+            modified = self.back_propagate_compute(cur_block)
+            if not modified:
+                continue
+            # We modified parent in, propagate to parents
+            for pred in self.predecessors(node):
+                self.back_propagate_to_parent(todo, node, pred)
+        return True
+
+
+class DiGraphLivenessIRA(DiGraphLiveness):
+    """
+    DiGraph representing variable liveness for IRA
+    """
+
+    def init_var_info(self, ir_arch_a):
+        """Add ircfg out regs"""
+
+        for node in self.leaves():
+            irblock = self.ircfg.blocks[node]
+            var_out = ir_arch_a.get_out_regs(irblock)
+            irblock_liveness = self.blocks[node]
+            irblock_liveness.infos[-1].var_out = var_out
diff --git a/miasm2/analysis/ssa.py b/miasm2/analysis/ssa.py
index c22aae59..b68eef7c 100644
--- a/miasm2/analysis/ssa.py
+++ b/miasm2/analysis/ssa.py
@@ -1,10 +1,10 @@
 from collections import deque
 
+from miasm2.analysis.data_flow import DiGraphLivenessIRA, remove_empty_assignblks
 from miasm2.expression.expression import ExprId, ExprAssign, ExprOp, get_expr_ids
 from miasm2.ir.ir import AssignBlock, IRBlock
 
 
-
 class SSA(object):
     """
     Generic class for static single assignment (SSA) transformation
@@ -44,7 +44,7 @@ class SSA(object):
         # stack for LHS
         self._stack_lhs = {}
 
-        self._ssa_variable_to_expr = {}
+        self.ssa_variable_to_expr = {}
 
         # dict of SSA expressions
         self.expressions = {}
@@ -78,7 +78,7 @@ class SSA(object):
         :param ssa_var: ExprId, variable in SSA form
         :return: ExprId, variable in non-SSA form
         """
-        expr = self._ssa_variable_to_expr.get(ssa_var, ssa_var)
+        expr = self.ssa_variable_to_expr.get(ssa_var, ssa_var)
         return expr
 
     def reset(self):
@@ -99,7 +99,7 @@ class SSA(object):
         index = stack[expr]
         name = "%s.%d" % (expr.name, index)
         ssa_var = ExprId(name, expr.size)
-        self._ssa_variable_to_expr[ssa_var] = expr
+        self.ssa_variable_to_expr[ssa_var] = expr
 
         return ssa_var
 
@@ -250,7 +250,7 @@ class SSA(object):
 
             # transform LHS
             for expr in instructions:
-                if expr.dst in self.immutable_ids:
+                if expr.dst in self.immutable_ids or expr.dst in self.ssa_variable_to_expr:
                     dst_ssa = expr.dst
                 else:
                     dst_ssa = self._transform_expression_lhs(expr.dst)
@@ -372,6 +372,7 @@ class SSADiGraph(SSA):
         self._rename(head)
         self._insert_phi()
         self._convert_phi()
+        self._fix_no_def_var(head)
 
     def reset(self):
         """Resets SSA transformation"""
@@ -400,7 +401,7 @@ class SSADiGraph(SSA):
                     # enforce ExprId
                     if dst.is_id():
                         # exclude immutable ids
-                        if dst in self.immutable_ids:
+                        if dst in self.immutable_ids or dst in self.ssa_variable_to_expr:
                             continue
                         # map variable definition to blocks
                         self.defs.setdefault(dst, set()).add(irblock.loc_key)
@@ -438,13 +439,11 @@ class SSADiGraph(SSA):
                 for node in frontier.get(loc_key, []):
                     if node in done:
                         continue
-
                     # place empty phi functions for a variable
                     empty_phi = self._gen_empty_phi(variable)
 
                     # add empty phi node for variable in node
                     self._phinodes.setdefault(node, {})[variable] = empty_phi.src
-
                     done.add(node)
 
                     if node not in intodo:
@@ -575,120 +574,553 @@ class SSADiGraph(SSA):
             new_irs = IRBlock(loc_key, [assignblk] + list(irblock.assignblks))
             self.ircfg.blocks[loc_key] = new_irs
 
+    def _fix_no_def_var(self, head):
+        """
+        Replace phi source variables which are not ssa vars by ssa vars.
+        @head: loc_key of the graph head
+        """
+        var_to_insert = set()
+        for loc_key in self._phinodes:
+            for dst, sources in self._phinodes[loc_key].iteritems():
+                for src in sources.args:
+                    if src in self.ssa_variable_to_expr:
+                        continue
+                    var_to_insert.add(src)
+        var_to_newname = {}
+        newname_to_var = {}
+        for var in var_to_insert:
+            new_var = self._transform_var_lhs(var)
+            var_to_newname[var] = new_var
+            newname_to_var[new_var] = var
+
+        # Replace non modified node used in phi with new variable
+        self.ircfg.simplify(lambda expr:expr.replace_expr(var_to_newname))
+
+        irblock = self.ircfg.blocks[head]
+        assignblks = list(irblock)
+        assignblks[0:0] = [AssignBlock(newname_to_var, assignblks[0].instr)]
+        self.ircfg.blocks[head] = IRBlock(head, assignblks)
+
+        # Updt structure
+        for loc_key in self._phinodes:
+            for dst, sources in self._phinodes[loc_key].items():
+                self._phinodes[loc_key][dst] = sources.replace_expr(var_to_newname)
+
+        for var, (loc_key, index) in self.ssa_to_location.items():
+            if loc_key == head:
+                self.ssa_to_location[var] = loc_key, index + 1
+
+        for newname, var in newname_to_var.iteritems():
+            self.ssa_to_location[newname] = head, 0
+            self.ssa_variable_to_expr[newname] = var
+            self.immutable_ids.add(newname)
+            self.expressions[newname] = var
 
 
-def get_assignblk(graph, loc, index):
+def irblock_has_phi(irblock):
     """
-    Return the dictionary of the AssignBlock from @graph at location @loc at
-    @index
-    @graph: IRCFG instance
-    @loc: Location instance
-    @index: assignblock index
+    Return True if @irblock has Phi assignments
+    @irblock: IRBlock instance
     """
+    if not irblock.assignblks:
+        return False
+    for src in irblock[0].itervalues():
+        return src.is_op('Phi')
+    return False
 
-    irblock = graph.blocks[loc]
-    assignblks = irblock.assignblks
-    assignblk = assignblks[index]
-    assignblk_dct = dict(assignblk)
-    return assignblk_dct
 
+class Varinfo(object):
+    """Store liveness information for a variable"""
+    __slots__ = ["live_index", "loc_key", "index"]
+
+    def __init__(self, live_index, loc_key, index):
+        self.live_index = live_index
+        self.loc_key = loc_key
+        self.index = index
 
-def set_assignblk(graph, loc, index, assignblk_dct):
-    """
-    Set the Assignblock in @graph at location @loc at @index using dictionary
-    @assignblk_dct
 
-    @graph: IRCFG instance
-    @loc: Location instance
-    @index: assignblock index
-    @assignblk_dct: dictionary representing the AssignBlock
+def get_var_assignment_src(ircfg, node, variables):
+    """
+    Return the variable of @variables which is written by the irblock at @node
+    @node: Location
+    @variables: a set of variable to test
     """
+    irblock = ircfg.blocks[node]
+    for assignblk in irblock:
+        result = set(assignblk).intersection(variables)
+        if not result:
+            continue
+        assert len(result) == 1
+        return list(result)[0]
+    return None
 
-    irblock = graph.blocks[loc]
-    assignblks = list(irblock.assignblks)
-    assignblk = assignblks[index]
 
-    assignblks[index] = AssignBlock(
-        assignblk_dct,
-        assignblk.instr
-    )
-    new_irblock = IRBlock(loc, assignblks)
-    return new_irblock
+def get_phi_sources_parent_block(ircfg, loc_key, sources):
+    """
+    Return a dictionary linking a variable to it's direct parent label
+    which belong to a path which affects the node.
+    @loc_key: the starting node
+    @sources: set of variables to resolve
+    """
+    source_to_parent = {}
+    for parent in ircfg.predecessors(loc_key):
+        done = set()
+        todo = set([parent])
+        found = False
+        while todo:
+            node = todo.pop()
+            if node in done:
+                continue
+            done.add(node)
+            ret = get_var_assignment_src(ircfg, node, sources)
+            if ret:
+                source_to_parent.setdefault(ret, set()).add(parent)
+                found = True
+                break
+            for pred in ircfg.predecessors(node):
+                todo.add(pred)
+        assert found
+    return source_to_parent
 
 
-def remove_phi(ssa, head):
+class UnSSADiGraph(object):
     """
-    Remove Phi using naive algorithm
-    Note: The _ssa_variable_to_expr must be up to date
-
-    @ssa: a SSADiGraph instance
-    @head: the loc_key of the graph head
+    Implements unssa algorithm
+    Revisiting Out-of-SSA Translation for Correctness, Code Quality, and
+    Efficiency
     """
 
-    phivar2var = {}
+    def __init__(self, ssa, head, cfg_liveness):
+        self.cfg_liveness = cfg_liveness
+        self.ssa = ssa
+        self.head = head
+
+        # Set of created variables
+        self.copy_vars = set()
+        # Virtual parallel copies
+
+        # On loc_key's Phi node dst -> set((parent, src))
+        self.phi_parent_sources = {}
+        # On loc_key's Phi node, loc_key -> set(Phi dsts)
+        self.phi_destinations = {}
+        # Phi's dst -> new var
+        self.phi_new_var = {}
+        # For a new_var representing dst:
+        # new_var -> set(parents of Phi's src in dst = Phi(src,...))
+        self.new_var_to_srcs_parents = {}
+        # new_var -> set(variables to be coalesced with, named "merge_set")
+        self.merge_state = {}
+
+        # Launch the algorithm in several steps
+        self.isolate_phi_nodes_block()
+        self.init_phis_merge_state()
+        self.order_ssa_var_dom()
+        self.aggressive_coalesce_block()
+        self.insert_parallel_copy()
+        self.replace_merge_sets()
+        self.remove_assign_eq()
+        remove_empty_assignblks(self.ssa.graph)
+
+    def insert_parallel_copy(self):
+        """
+        Naive Out-of-SSA from CSSA (without coalescing for now)
+        - Replace Phi
+        - Create room for parallel copies in Phi's parents
+        """
+        ircfg = self.ssa.graph
 
-    all_ssa_vars = ssa._ssa_variable_to_expr
+        for irblock in ircfg.blocks.values():
+            if not irblock_has_phi(irblock):
+                continue
 
-    # Retrieve Phi nodes
-    phi_nodes = []
-    for irblock in ssa.graph.blocks.itervalues():
-        for index, assignblk in enumerate(irblock):
-            for dst, src in assignblk.iteritems():
-                if src.is_op('Phi'):
-                    phi_nodes.append((irblock.loc_key, index))
+            # Replace Phi with Phi's dst = new_var
+            parallel_copies = {}
+            for dst in self.phi_destinations[irblock.loc_key]:
+                new_var = self.phi_new_var[dst]
+                parallel_copies[dst] = new_var
+
+            assignblks = list(irblock)
+            assignblks[0] = AssignBlock(parallel_copies, irblock[0].instr)
+            new_irblock = IRBlock(irblock.loc_key, assignblks)
+            ircfg.blocks[irblock.loc_key] = new_irblock
+
+            # Insert new_var = src in each Phi's parent, at the end of the block
+            parent_to_parallel_copies = {}
+            parallel_copies = {}
+            for dst in irblock[0]:
+                new_var = self.phi_new_var[dst]
+                for parent, src in self.phi_parent_sources[dst]:
+                    parent_to_parallel_copies.setdefault(parent, {})[new_var] = src
+
+            for parent, parallel_copies in parent_to_parallel_copies.iteritems():
+                parent = ircfg.blocks[parent]
+                assignblks = list(parent)
+                assignblks.append(AssignBlock(parallel_copies, parent[-1].instr))
+                new_irblock = IRBlock(parent.loc_key, assignblks)
+                ircfg.blocks[parent.loc_key] = new_irblock
+
+    def create_copy_var(self, var):
+        """
+        Generate a new var standing for @var
+        @var: variable to replace
+        """
+        new_var = ExprId('var%d' % len(self.copy_vars), var.size)
+        self.copy_vars.add(new_var)
+        return new_var
 
+    def isolate_phi_nodes_block(self):
+        """
+        Init structures and virtually insert parallel copy before/after each phi
+        node
+        """
+        ircfg = self.ssa.graph
+        for irblock in ircfg.blocks.itervalues():
+            if not irblock_has_phi(irblock):
+                continue
+            for dst, sources in irblock[0].iteritems():
+                assert sources.is_op('Phi')
+                new_var = self.create_copy_var(dst)
+                self.phi_new_var[dst] = new_var
+
+                var_to_parents = get_phi_sources_parent_block(
+                    self.ssa.graph,
+                    irblock.loc_key,
+                    sources.args
+                )
+
+                for src in sources.args:
+                    parents = var_to_parents[src]
+                    self.new_var_to_srcs_parents.setdefault(new_var, set()).update(parents)
+                    parent_dsts = set((parent, src) for parent in parents)
+                    for parent in parents:
+                        self.phi_parent_sources.setdefault(dst, set()).add((parent, src))
+
+            self.phi_destinations[irblock.loc_key] = set(irblock[0])
+
+    def init_phis_merge_state(self):
+        """
+        Generate trivial coalescing of phi variable and itself
+        """
+        for phi_new_var in self.phi_new_var.itervalues():
+            self.merge_state.setdefault(phi_new_var, set([phi_new_var]))
 
-    for phi_loc, phi_index in phi_nodes:
-        assignblk_dct = get_assignblk(ssa.graph, phi_loc, phi_index)
-        for dst, src in assignblk_dct.iteritems():
-            if src.is_op('Phi'):
-                break
-        else:
-            raise RuntimeError('Cannot find phi?')
-        node_src = src
-        var = dst
-
-        # Create new variable
-        new_var = ExprId('var%d' % len(phivar2var), var.size)
-        phivar2var[var] = new_var
-        phi_sources = set(node_src.args)
-
-        # Place var init for non ssa variables
-        to_remove = set()
-        for phi_source in list(phi_sources):
-            if phi_source not in all_ssa_vars.union(phivar2var.values()):
-                assignblk_dct = get_assignblk(ssa.graph, head, 0)
-                assignblk_dct[new_var] = phi_source
-                new_irblock = set_assignblk(ssa.graph, head, 0, assignblk_dct)
-                ssa.graph.blocks[head] = new_irblock
-                to_remove.add(phi_source)
-        phi_sources.difference_update(to_remove)
-
-        var_to_replace = set([var])
-        var_to_replace.update(phi_sources)
-
-
-
-
-        # Replace variables
-        to_replace_dct = {x:new_var for x in var_to_replace}
-        for loc in ssa.graph.blocks:
-            irblock = ssa.graph.blocks[loc]
-            assignblks = []
-            for assignblk in irblock:
-                assignblk_dct = {}
+    def order_ssa_var_dom(self):
+        """Compute dominance order of each ssa variable"""
+        ircfg = self.ssa.graph
+
+        # compute dominator tree
+        dominator_tree = ircfg.compute_dominator_tree(self.head)
+
+        # variable -> Varinfo
+        self.var_to_varinfo = {}
+        # live_index can later be used to compare dominance of AssignBlocks
+        live_index = 0
+
+        # walk in DFS over the dominator tree
+        for loc_key in dominator_tree.walk_depth_first_forward(self.head):
+            irblock = ircfg.blocks[loc_key]
+
+            # Create live index for phi new vars
+            # They do not exist in the graph yet, so index is set to None
+            if irblock_has_phi(irblock):
+                for dst in irblock[0]:
+                    if not dst.is_id():
+                        continue
+                    new_var = self.phi_new_var[dst]
+                    self.var_to_varinfo[new_var] = Varinfo(live_index, loc_key, None)
+
+                live_index += 1
+
+            # Create live index for remaining assignments
+            for index, assignblk in enumerate(irblock):
+                used = False
+                for dst in assignblk:
+                    if not dst.is_id():
+                        continue
+                    if dst in self.ssa.immutable_ids:
+                        # Will not be considered by the current algo, ignore it
+                        # (for instance, IRDst)
+                        continue
+
+                    assert dst not in self.var_to_varinfo
+                    self.var_to_varinfo[dst] = Varinfo(live_index, loc_key, index)
+                    used = True
+                if used:
+                    live_index += 1
+
+
+    def ssa_def_dominates(self, node_a, node_b):
+        """
+        Return living index order of @node_a and @node_b
+        @node_a: Varinfo instance
+        @node_b: Varinfo instance
+        """
+        ret = self.var_to_varinfo[node_a].live_index <= self.var_to_varinfo[node_b].live_index
+        return ret
+
+    def merge_set_sort(self, merge_set):
+        """
+        Return a sorted list of (live_index, var) from @merge_set in dominance
+        order
+        @merge_set: set of coalescing variables
+        """
+        return sorted(
+            (self.var_to_varinfo[var].live_index, var)
+            for var in merge_set
+        )
+
+    def ssa_def_is_live_at(self, node_a, node_b, parent):
+        """
+        Return True if @node_a is live during @node_b definition
+        If @parent is None, this is a liveness test for a post phi variable;
+        Else, it is a liveness test for a variable source of the phi node
+
+        @node_a: Varinfo instance
+        @node_b: Varinfo instance
+        @parent: Optional parent location of the phi source
+        """
+        loc_key_b, index_b = self.var_to_varinfo[node_b].loc_key, self.var_to_varinfo[node_b].index
+        if parent and index_b is None:
+            index_b = 0
+        if node_a not in self.new_var_to_srcs_parents:
+            # node_a is not a new var (it is a "classic" var)
+            # -> use a basic liveness test
+            liveness_b = self.cfg_liveness.blocks[loc_key_b].infos[index_b]
+            return node_a in liveness_b.var_out
+
+        for def_loc_key in self.new_var_to_srcs_parents[node_a]:
+            # Consider node_a as defined at the end of its parents blocks
+            # and compute liveness check accordingly
+
+            if def_loc_key == parent:
+                # Same path as node_a definition, so SSA ensure b cannot be live
+                # on this path (otherwise, a Phi would already happen earlier)
+                continue
+            liveness_end_block = self.cfg_liveness.blocks[def_loc_key].infos[-1]
+            if node_b in liveness_end_block.var_out:
+                return True
+        return False
+
+    def merge_nodes_interfere(self, node_a, node_b, parent):
+        """
+        Return True if @node_a and @node_b interfere
+        @node_a: variable
+        @node_b: variable
+        @parent: Optional parent location of the phi source for liveness tests
+
+        Interference check is: is x live at y definition (or reverse)
+        TODO: add Value-based interference improvement
+        """
+        if self.var_to_varinfo[node_a].live_index == self.var_to_varinfo[node_b].live_index:
+            # Defined in the same AssignBlock -> interfere
+            return True
+
+        if self.var_to_varinfo[node_a].live_index < self.var_to_varinfo[node_b].live_index:
+            return self.ssa_def_is_live_at(node_a, node_b, parent)
+        return self.ssa_def_is_live_at(node_b, node_a, parent)
+
+    def merge_sets_interfere(self, merge_a, merge_b, parent):
+        """
+        Return True if no variable in @merge_a and @merge_b interferes.
+
+        Implementation of "Algorithm 2: Check intersection in a set of variables"
+
+        @merge_a: a dom ordered list of equivalent variables
+        @merge_b: a dom ordered list of equivalent variables
+        @parent: Optional parent location of the phi source for liveness tests
+        """
+        if merge_a == merge_b:
+            # No need to consider interference if equal
+            return False
+
+        merge_a_list = self.merge_set_sort(merge_a)
+        merge_b_list = self.merge_set_sort(merge_b)
+        dom = []
+        while merge_a_list or merge_b_list:
+            if not merge_a_list:
+                _, current = merge_b_list.pop(0)
+            elif not merge_b_list:
+                _, current = merge_a_list.pop(0)
+            else:
+                # compare live_indexes (standing for dominance)
+                if merge_a_list[-1] < merge_b_list[-1]:
+                    _, current = merge_a_list.pop(0)
+                else:
+                    _, current = merge_b_list.pop(0)
+            while dom and not self.ssa_def_dominates(dom[-1], current):
+                dom.pop()
+
+            # Don't test node in same merge_set
+            if (
+                    # Is stack not empty?
+                    dom and
+                    # Trivial non-interference if dom.top() and current come
+                    # from the same merge set
+                    not (dom[-1] in merge_a and current in merge_a) and
+                    not (dom[-1] in merge_b and current in merge_b) and
+                    # Actually test for interference
+                    self.merge_nodes_interfere(current, dom[-1], parent)
+            ):
+                    return True
+            dom.append(current)
+        return False
+
+    def aggressive_coalesce_parallel_copy(self, parallel_copies, parent):
+        """
+        Try to coalesce variables each dst/src couple together from
+        @parallel_copies
+
+        @parallel_copies: a dictionary representing dst/src parallel
+        assignments.
+        @parent: Optional parent location of the phi source for liveness tests
+        """
+        for dst, src in parallel_copies.iteritems():
+            dst_merge = self.merge_state.setdefault(dst, set([dst]))
+            src_merge = self.merge_state.setdefault(src, set([src]))
+            if not self.merge_sets_interfere(dst_merge, src_merge, parent):
+                dst_merge.update(src_merge)
+                for node in dst_merge:
+                    self.merge_state[node] = dst_merge
+
+    def aggressive_coalesce_block(self):
+        """Try to coalesce phi var with their pre/post variables"""
+
+        ircfg = self.ssa.graph
+
+        # Run coalesce on the post phi parallel copy
+        for irblock in ircfg.blocks.values():
+            if not irblock_has_phi(irblock):
+                continue
+            parallel_copies = {}
+            for dst in self.phi_destinations[irblock.loc_key]:
+                parallel_copies[dst] = self.phi_new_var[dst]
+            self.aggressive_coalesce_parallel_copy(parallel_copies, None)
+
+            # Run coalesce on the pre phi parallel copy
+
+            # Stand for the virtual parallel copies at the end of Phi's block
+            # parents
+            parent_to_parallel_copies = {}
+            for dst in irblock[0]:
+                new_var = self.phi_new_var[dst]
+                for parent, src in self.phi_parent_sources[dst]:
+                    parent_to_parallel_copies.setdefault(parent, {})[new_var] = src
+
+            for parent, parallel_copies in parent_to_parallel_copies.iteritems():
+                self.aggressive_coalesce_parallel_copy(parallel_copies, parent)
+
+    def get_best_merge_set_name(self, merge_set):
+        """
+        For a given @merge_set, prefer an original SSA variable instead of a
+        created copy. In other case, take a random name.
+        @merge_set: set of equivalent expressions
+        """
+        if not merge_set:
+            raise RuntimeError("Merge set should not be empty")
+        for var in merge_set:
+            if var not in self.copy_vars:
+                return var
+        # Get random name
+        return var
+
+
+    def replace_merge_sets(self):
+        """
+        In the graph, replace all variables from merge state by their
+        representative variable
+        """
+        replace = {}
+        merge_sets = set()
+
+        # Elect representative for merge sets
+        merge_set_to_name = {}
+        for merge_set in self.merge_state.itervalues():
+            frozen_merge_set = frozenset(merge_set)
+            merge_sets.add(frozen_merge_set)
+            var_name = self.get_best_merge_set_name(merge_set)
+            merge_set_to_name[frozen_merge_set] = var_name
+
+        # Generate replacement of variable by their representative
+        for merge_set in merge_sets:
+            var_name = merge_set_to_name[merge_set]
+            merge_set = list(merge_set)
+            for var in merge_set:
+                replace[var] = var_name
+
+        self.ssa.graph.simplify(lambda x: x.replace_expr(replace))
+
+    def remove_phi(self):
+        """
+        Remove phi operators in @ifcfg
+        @ircfg: IRDiGraph instance
+        """
+
+        for irblock in self.ssa.graph.blocks.values():
+            assignblks = list(irblock)
+            out = {}
+            for dst, src in assignblks[0].iteritems():
+                if src.is_op('Phi'):
+                    assert set([dst]) == set(src.args)
+                    continue
+                out[dst] = src
+            assignblks[0] = AssignBlock(out, assignblks[0].instr)
+            self.ssa.graph.blocks[irblock.loc_key] = IRBlock(irblock.loc_key, assignblks)
+
+    def remove_assign_eq(self):
+        """
+        Remove trivial expressions (a=a) in the current graph
+        """
+        for irblock in self.ssa.graph.blocks.values():
+            assignblks = list(irblock)
+            for i, assignblk in enumerate(assignblks):
+                out = {}
                 for dst, src in assignblk.iteritems():
-                    dst = dst.replace_expr(to_replace_dct)
-                    src = src.replace_expr(to_replace_dct)
-                    assignblk_dct[dst] = src
-                assignblks.append(AssignBlock(assignblk_dct, assignblk.instr))
-            new_irblock = IRBlock(loc, assignblks)
-            ssa.graph.blocks[loc] = new_irblock
+                    if dst == src:
+                        continue
+                    out[dst] = src
+                assignblks[i] = AssignBlock(out, assignblk.instr)
+            self.ssa.graph.blocks[irblock.loc_key] = IRBlock(irblock.loc_key, assignblks)
+
+
+class DiGraphLivenessSSA(DiGraphLivenessIRA):
+    """
+    DiGraph representing variable liveness is a SSA graph
+    """
+    def __init__(self, ircfg):
+        super(DiGraphLivenessSSA, self).__init__(ircfg)
+
+        self.loc_key_to_phi_parents = {}
+        for irblock in self.blocks.values():
+            if not irblock_has_phi(irblock):
+                continue
+            out = {}
+            for sources in irblock[0].itervalues():
+                var_to_parents = get_phi_sources_parent_block(self, irblock.loc_key, sources.args)
+                for var, var_parents in var_to_parents.iteritems():
+                    out.setdefault(var, set()).update(var_parents)
+            self.loc_key_to_phi_parents[irblock.loc_key] = out
+
+    def back_propagate_to_parent(self, todo, node, parent):
+        parent_block = self.blocks[parent]
+        cur_block = self.blocks[node]
+        irblock = self.ircfg.blocks[node]
+        if cur_block.infos[0].var_in == parent_block.infos[-1].var_out:
+            return
+        var_info = cur_block.infos[0].var_in.union(parent_block.infos[-1].var_out)
+
+        if irblock_has_phi(irblock):
+            # Remove phi special case
+            out = set()
+            phi_sources = self.loc_key_to_phi_parents[irblock.loc_key]
+            for var in var_info:
+                if var not in phi_sources:
+                    out.add(var)
+                    continue
+                if parent in phi_sources[var]:
+                    out.add(var)
+            var_info = out
 
-        # Remove phi
-        assignblk_dct = get_assignblk(ssa.graph, phi_loc, phi_index)
-        del assignblk_dct[new_var]
+        parent_block.infos[-1].var_out = var_info
+        todo.add(parent)
 
 
-        new_irblock = set_assignblk(ssa.graph, phi_loc, phi_index, assignblk_dct)
-        ssa.graph.blocks[phi_loc] = new_irblock
diff --git a/test/analysis/unssa.py b/test/analysis/unssa.py
new file mode 100644
index 00000000..244b5436
--- /dev/null
+++ b/test/analysis/unssa.py
@@ -0,0 +1,652 @@
+""" Test cases for dead code elimination"""
+from pdb import pm
+from pprint import pprint as pp
+from miasm2.expression.expression import ExprId, ExprInt, ExprAssign, ExprMem, \
+    ExprCond, ExprOp
+from miasm2.core.locationdb import LocationDB
+from miasm2.analysis.data_flow import *
+from miasm2.ir.analysis import ira
+from miasm2.ir.ir import IRCFG, IRBlock, AssignBlock
+from miasm2.analysis.ssa import SSADiGraph, UnSSADiGraph, DiGraphLivenessSSA
+
+loc_db = LocationDB()
+
+a = ExprId("a", 32)
+b = ExprId("b", 32)
+c = ExprId("c", 32)
+d = ExprId("d", 32)
+r = ExprId("r", 32)
+x = ExprId("x", 32)
+y = ExprId("y", 32)
+u8 = ExprId("u8", 8)
+zf = ExprId('zf', 1)
+
+a_init = ExprId("a_init", 32)
+b_init = ExprId("b_init", 32)
+c_init = ExprId("c_init", 32)
+d_init = ExprId("d_init", 32)
+r_init = ExprId("r_init", 32) # Return register
+
+pc = ExprId("pc", 32)
+sp = ExprId("sp", 32)
+
+CST0 = ExprInt(0x0, 32)
+CST1 = ExprInt(0x1, 32)
+CST2 = ExprInt(0x2, 32)
+CST3 = ExprInt(0x3, 32)
+CSTX_8 = ExprInt(12, 8)
+
+LBL0 = loc_db.add_location("lbl0", 0)
+LBL1 = loc_db.add_location("lbl1", 1)
+LBL2 = loc_db.add_location("lbl2", 2)
+LBL3 = loc_db.add_location("lbl3", 3)
+LBL4 = loc_db.add_location("lbl4", 4)
+LBL5 = loc_db.add_location("lbl5", 5)
+LBL6 = loc_db.add_location("lbl6", 6)
+LBL7 = loc_db.add_location("lbl7", 7)
+
+IRDst = ExprId('IRDst', 32)
+dummy = ExprId('dummy', 32)
+
+
+def gen_irblock(label, exprs_list):
+    irs = []
+    for exprs in exprs_list:
+        if isinstance(exprs, AssignBlock):
+            irs.append(exprs)
+        else:
+            irs.append(AssignBlock(exprs))
+
+    irbl = IRBlock(label, irs)
+    return irbl
+
+
+class Regs(object):
+    regs_init = {a: a_init, b: b_init, c: c_init, d: d_init, r: r_init}
+    all_regs_ids = [a, b, c, d, r, sp, pc]
+
+class Arch(object):
+    regs = Regs()
+
+    def getpc(self, _):
+        return pc
+
+    def getsp(self, _):
+        return sp
+
+class IRATest(ira):
+
+    """Fake IRA class for tests"""
+
+    def __init__(self, loc_db=None):
+        arch = Arch()
+        super(IRATest, self).__init__(arch, 32, loc_db)
+        self.IRDst = IRDst
+        self.ret_reg = r
+
+    def get_out_regs(self, xx):
+        out = set()
+        """
+        for assignblk in xx:
+            for dst in assignblk:
+                if str(dst).startswith("r"):
+                    out.add(dst)
+        """
+        out.add(r)
+        return out
+
+IRA = IRATest(loc_db)
+END = ExprId("END", IRDst.size)
+
+G0_IRA = IRA.new_ircfg()
+
+G0_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G0_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(a, a+CST1)],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+G0_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G0_IRB0, G0_IRB1, G0_IRB2]:
+    G0_IRA.add_irblock(irb)
+
+
+
+G1_IRA = IRA.new_ircfg()
+
+G1_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST1)],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+G1_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(a, a+CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL3, 32))]
+])
+G1_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(a, a+CST2)],
+    [ExprAssign(IRDst, ExprLoc(LBL3, 32))]
+])
+G1_IRB3 = gen_irblock(LBL3, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G1_IRB0, G1_IRB1, G1_IRB2, G1_IRB3]:
+    G1_IRA.add_irblock(irb)
+
+
+
+
+
+G2_IRA = IRA.new_ircfg()
+
+G2_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST1)],
+    [ExprAssign(b, CST2)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G2_IRB1 = gen_irblock(LBL1, [
+    [
+        ExprAssign(a, b),
+        ExprAssign(b, a),
+    ],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+G2_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G2_IRB0, G2_IRB1, G2_IRB2]:
+    G2_IRA.add_irblock(irb)
+
+
+
+
+G3_IRA = IRA.new_ircfg()
+
+G3_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G3_IRB1 = gen_irblock(LBL1, [
+    [
+        ExprAssign(a, a + CST1),
+    ],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL2, 32),
+                                ExprCond(y,
+                                         ExprLoc(LBL3, 32),
+                                         ExprLoc(LBL5, 32)
+                                )
+    ))]
+])
+
+G3_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(a, a + CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+
+G3_IRB3 = gen_irblock(LBL3, [
+    [ExprAssign(a, a + CST2)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+
+G3_IRB4 = gen_irblock(LBL4, [
+    [ExprAssign(r, a + CST3)],
+    [
+        ExprAssign(IRDst,
+                   ExprCond(y,
+                            ExprLoc(LBL1, 32),
+                            ExprLoc(LBL5, 32)
+                   )
+        )
+    ]
+])
+
+
+G3_IRB5 = gen_irblock(LBL5, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G3_IRB0, G3_IRB1, G3_IRB2, G3_IRB3, G3_IRB5]:
+    G3_IRA.add_irblock(irb)
+
+
+
+
+
+
+G4_IRA = IRA.new_ircfg()
+
+G4_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G4_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL2, 32),
+                                ExprLoc(LBL3, 32)
+                                )
+    )]
+])
+G4_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(a, a+CST2)],
+    [ExprAssign(IRDst, ExprLoc(LBL4, 32))]
+])
+G4_IRB3 = gen_irblock(LBL3, [
+    [ExprAssign(a, a+CST3)],
+    [ExprAssign(IRDst, ExprLoc(LBL4, 32))]
+])
+G4_IRB4 = gen_irblock(LBL4, [
+    [ExprAssign(a, a+CST1)],
+    [
+        ExprAssign(
+            IRDst,
+            ExprCond(
+                x,
+                ExprLoc(LBL5, 32),
+                ExprLoc(LBL1, 32)
+            )
+        )
+    ]
+])
+
+G4_IRB5 = gen_irblock(LBL5, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G4_IRB0, G4_IRB1, G4_IRB2, G4_IRB3, G4_IRB4, G4_IRB5]:
+    G4_IRA.add_irblock(irb)
+
+
+
+
+
+G5_IRA = IRA.new_ircfg()
+
+G5_IRB0 = gen_irblock(LBL0, [
+    [
+        ExprAssign(a, CST1),
+        ExprAssign(b, CST1),
+    ],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+G5_IRB1 = gen_irblock(LBL1, [
+    [
+        ExprAssign(b, a),
+        ExprAssign(a, a+CST1)
+    ],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+G5_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(r, b)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G5_IRB0, G5_IRB1, G5_IRB2]:
+    G5_IRA.add_irblock(irb)
+
+
+
+G6_IRA = IRA.new_ircfg()
+
+G6_IRB0 = gen_irblock(LBL0, [
+    [
+        ExprAssign(a, CST1),
+        ExprAssign(b, CST1),
+    ],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+
+
+G6_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(a, a + CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL5, 32))]
+])
+
+
+G6_IRB2 = gen_irblock(LBL2, [
+    [
+        ExprAssign(a, a + CST1),
+    ],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL3, 32),
+                                ExprLoc(LBL4, 32)
+                                )
+    )]
+])
+
+G6_IRB3 = gen_irblock(LBL3, [
+    [
+        ExprAssign(b, a + CST1),
+    ],
+    [ExprAssign(IRDst, ExprLoc(LBL5, 32))],
+])
+
+
+G6_IRB4 = gen_irblock(LBL4, [
+    [
+        ExprAssign(b, a + CST1),
+    ],
+    [ExprAssign(IRDst, ExprLoc(LBL5, 32))],
+])
+
+G6_IRB5 = gen_irblock(LBL5, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G6_IRB0, G6_IRB1, G6_IRB2, G6_IRB3, G6_IRB4, G6_IRB5]:
+    G6_IRA.add_irblock(irb)
+
+
+
+
+
+G7_IRA = IRA.new_ircfg()
+
+G7_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, a + CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G7_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL2, 32),
+                                ExprLoc(LBL3, 32)
+                                )
+    )]
+])
+G7_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(IRDst, ExprLoc(LBL4, 32))]
+])
+G7_IRB3 = gen_irblock(LBL3, [
+    [ExprAssign(a, a+CST3)],
+    [ExprAssign(IRDst, ExprLoc(LBL4, 32))]
+])
+G7_IRB4 = gen_irblock(LBL4, [
+    [
+        ExprAssign(
+            IRDst,
+            ExprCond(
+                x,
+                ExprLoc(LBL5, 32),
+                ExprLoc(LBL1, 32)
+            )
+        )
+    ]
+])
+
+G7_IRB5 = gen_irblock(LBL5, [
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G7_IRB0, G7_IRB1, G7_IRB2, G7_IRB3, G7_IRB4, G7_IRB5]:
+    G7_IRA.add_irblock(irb)
+
+
+
+
+
+
+G8_IRA = IRA.new_ircfg()
+
+G8_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST0)],
+    [ExprAssign(b, c)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+
+G8_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(u8, ExprMem(b, 8))],
+    [
+        ExprAssign(
+            IRDst,
+            ExprCond(
+                u8,
+                ExprLoc(LBL2, 32),
+                ExprLoc(LBL7, 32)
+            )
+        )
+    ]
+])
+
+
+G8_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(b, b + CST1)],
+    [
+        ExprAssign(
+            IRDst,
+            ExprCond(
+                u8 + CSTX_8,
+                ExprLoc(LBL1, 32),
+                ExprLoc(LBL3, 32)
+            )
+        )
+    ]
+])
+
+
+
+G8_IRB3 = gen_irblock(LBL3, [
+    [
+        ExprAssign(a, (ExprMem(b, 8) + u8).zeroExtend(32))
+    ],
+    [
+        ExprAssign(
+            IRDst,
+            ExprLoc(LBL1, 32)
+        )
+    ]
+])
+
+
+
+G8_IRB4 = gen_irblock(LBL4, [
+    [ExprAssign(b, b + CST1)],
+    [ExprAssign(d, CST0)],
+    [ExprAssign(IRDst, ExprLoc(LBL6, 32))]
+])
+
+
+G8_IRB5 = gen_irblock(LBL5, [
+    [ExprAssign(d, CST1)],
+    [ExprAssign(IRDst, ExprLoc(LBL6, 32))]
+])
+
+
+G8_IRB6 = gen_irblock(LBL6, [
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+G8_IRB7 = gen_irblock(LBL7, [
+    [ExprAssign(b, CST2)],
+    [ExprAssign(r, a)],
+    [ExprAssign(IRDst, END)]
+])
+
+
+for irb in [G8_IRB0, G8_IRB1, G8_IRB2, G8_IRB3, G8_IRB7]:
+    G8_IRA.add_irblock(irb)
+
+
+
+G9_IRA = IRA.new_ircfg()
+
+G9_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+G9_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(b, CST1)],
+    [ExprAssign(IRDst, ExprCond(x,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+G9_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(r, b)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G9_IRB0, G9_IRB1, G9_IRB2]:
+    G9_IRA.add_irblock(irb)
+
+
+
+
+
+G10_IRA = IRA.new_ircfg()
+
+G10_IRB0 = gen_irblock(LBL0, [
+    [ExprAssign(a, CST0)],
+    [ExprAssign(IRDst, ExprLoc(LBL1, 32))]
+])
+
+G10_IRB1 = gen_irblock(LBL1, [
+    [ExprAssign(a, a+CST1)],
+    [ExprAssign(IRDst, ExprCond(a,
+                                ExprLoc(LBL1, 32),
+                                ExprLoc(LBL2, 32)
+                                )
+    )]
+])
+
+G10_IRB2 = gen_irblock(LBL2, [
+    [ExprAssign(r, CST1)],
+    [ExprAssign(IRDst, END)]
+])
+
+for irb in [G10_IRB0, G10_IRB1, G10_IRB2]:
+    G10_IRA.add_irblock(irb)
+
+
+
+
+ExprId.__repr__ = ExprId.__str__
+
+
+
+class IRAOutRegs(IRATest):
+    def get_out_regs(self, block):
+        regs_todo = super(self.__class__, self).get_out_regs(block)
+        out = {}
+        for assignblk in block:
+            for dst in assignblk:
+                reg = self.ssa_var.get(dst, None)
+                if reg is None:
+                    continue
+                if reg in regs_todo:
+                    out[reg] = dst
+        return set(out.values())
+
+
+
+def add_out_reg_end(ir_arch_a, ircfg_a):
+    # Add dummy dependency to uncover out regs affectation
+    for loc in ircfg_a.leaves():
+        irblock = ircfg_a.blocks.get(loc)
+        if irblock is None:
+            continue
+        regs = {}
+        for reg in ir_arch_a.get_out_regs(irblock):
+            regs[reg] = reg
+        assignblks = list(irblock)
+        new_assiblk = AssignBlock(regs, assignblks[-1].instr)
+        assignblks.append(new_assiblk)
+        new_irblock = IRBlock(irblock.loc_key, assignblks)
+        ircfg_a.blocks[loc] = new_irblock
+
+
+ir_arch_a = IRAOutRegs(loc_db)
+
+
+
+for test_nb, ircfg in enumerate(
+        [
+            G0_IRA,
+            G1_IRA,
+            G2_IRA,
+            G3_IRA,
+            G4_IRA,
+            G5_IRA,
+            G6_IRA,
+            G7_IRA,
+            G8_IRA,
+            G9_IRA,
+            G10_IRA,
+        ]):
+
+    open('graph_%d.dot' % test_nb, 'w').write(ircfg.dot())
+
+    # Save a copy of ircfg
+    ircfg_orig = IRCFG(IRDst, loc_db)
+    for irblock in ircfg.blocks.values():
+        ircfg_orig.add_irblock(irblock)
+
+    # SSA
+    head = LBL0
+    ssa = SSADiGraph(ircfg)
+    ssa.transform(head)
+
+    ir_arch_a.ssa_var = ssa.ssa_variable_to_expr
+    dead_simp(ir_arch_a, ssa.graph)
+
+    open("ssa_%d.dot" % test_nb, "wb").write(ssa.graph.dot())
+
+
+
+    # Un SSA
+    ir_arch_a.ssa_var = ssa.ssa_variable_to_expr
+
+    propagate_expr = PropagateExpr()
+    modified = True
+    while modified:
+        modified = False
+        modified |= propagate_expr.propagate(ssa, head)
+
+    open('tmp_%d.dot' % test_nb, 'w').write(ssa.graph.dot())
+
+    cfg_liveness = DiGraphLivenessSSA(ssa.graph)
+    cfg_liveness.init_var_info(ir_arch_a)
+    cfg_liveness.compute_liveness()
+
+    open('liveness_%d.dot' % test_nb, 'w').write(cfg_liveness.dot())
+
+    unssa = UnSSADiGraph(ssa, head, cfg_liveness)
+    open('final_%d.dot' % test_nb, 'w').write(unssa.ssa.graph.dot())
+
+    # XXX TODO: add real regression test
diff --git a/test/test_all.py b/test/test_all.py
index f3bcc477..008d837f 100755
--- a/test/test_all.py
+++ b/test/test_all.py
@@ -384,6 +384,7 @@ testset += RegressionTest(["data_flow.py"], base_dir="analysis",
             ["simp_graph_%02d.dot" % test_nb, "graph_%02d.dot" % test_nb]
             for test_nb in xrange(1, 18))
                                     for fname in fnames])
+testset += RegressionTest(["unssa.py"], base_dir="analysis")
 
 for i in xrange(1, 21):
     input_name = "cst_propag/x86_32_sc_%d" % i
@@ -604,17 +605,17 @@ class ExampleDisasmFull(ExampleDisassembler):
 
 
 testset += ExampleDisasmFull(["arml", Example.get_sample("demo_arm_l.bin"),
-                              "0"], depends=[test_arml])
+                              "0x2c", "-z"], depends=[test_arml])
 testset += ExampleDisasmFull(["armb", Example.get_sample("demo_arm_b.bin"),
-                              "0"], depends=[test_armb])
+                              "0x2c", "-z"], depends=[test_armb])
 testset += ExampleDisasmFull(["arml", Example.get_sample("demo_arm2_l.bin"),
-                              "0"], depends=[test_arml_sc])
+                              "0x0", "-z"], depends=[test_arml_sc])
 testset += ExampleDisasmFull(["armb", Example.get_sample("demo_arm2_b.bin"),
-                              "0"], depends=[test_armb_sc])
+                              "0x0", "-z"], depends=[test_armb_sc])
 testset += ExampleDisasmFull(["armtl", Example.get_sample("demo_armt_l.bin"),
-                              "0"], depends=[test_armtl])
+                              "0x2c", "-z"], depends=[test_armtl])
 testset += ExampleDisasmFull(["armtb", Example.get_sample("demo_armt_b.bin"),
-                              "0"], depends=[test_armtb])
+                              "0x2c", "-z"], depends=[test_armtb])
 testset += ExampleDisasmFull(["aarch64l", Example.get_sample("demo_aarch64_l.bin"),
                               "0"], depends=[test_aarch64l])
 testset += ExampleDisasmFull(["aarch64b", Example.get_sample("demo_aarch64_b.bin"),