# Path: xform_expr.py
# def expr_subst(expr, subst_dict):
# 
#     if isinstance(expr, (VALUE, STR, ADDR, SFUNC, TYPE)):
#         return None
# 
#     if isinstance(expr, REG):
#         new = subst_dict.get(expr)
#         if new and expr in new:
#             _log.debug("Trying to replace %s with recursively referring %s, not doing" % (expr, new))
#             return None
#         if new and len(new) > 10:
#             _log.debug("Trying to replace %s with complex [len=%d] %s, not doing" % (expr, len(new), new))
#             return None
#         return new
# 
#     if isinstance(expr, MEM):
#         new = expr_subst(expr.expr, subst_dict)
#         if new:
#             return MEM(expr.type, new)
#         return
# 
#     if isinstance(expr, COND):
#         # This performs substituations in-place, because the same
#         # COND instance is referenced in inst (to be later removed
#         # by remove_trailing_jumps) and in out edges in CFG.
#         new = expr_subst(expr.expr, subst_dict)
#         if new:
#             expr.expr = new
#             simplify_cond(expr)
#         return
# 
#     if isinstance(expr, EXPR):
#         new_args = []
#         was_new = False
#         for a in expr.args:
#             new = expr_subst(a, subst_dict)
#             if new is None:
#                 new = a
#             else:
#                 was_new = True
#             new_args.append(new)
#         if not was_new:
#             return None
# 
#         new_expr = EXPR(expr.op, new_args)
#         new_expr = simplify_expr(new_expr)
#         return new_expr
# 
#     assert 0, repr((expr, type(expr)))


from core import *
from xform_expr import expr_subst


def bblock_propagation(bblock, propagated_types, subst_insts=True):

    def kill_subst_uses(subst, kill_var):
        "Remove from subst dict any expressions involving kill_var"
        def not_used(var, expr):
            # Here we assume that expression can be at most reference to a var,
            # which is true for at most copy propagation, but to handle expr
            # propagation, need to do better
            return var not in expr
        subst = dict((k, v) for k, v in subst.items() if not_used(kill_var, v))
        # We of course should kill state for var itself
        subst.pop(kill_var, None)
        return subst

    state = bblock.props.get("state_in", {})
    for i, inst in enumerate(bblock.items):

        n_dest = inst.dest
        kill_n_dest = False
        if isinstance(n_dest, MEM):
            new = expr_subst(n_dest, state)
            if new:
                n_dest = new
                kill_n_dest = True

        args = copy.deepcopy(inst.args)

        for arg_no, arg in enumerate(args):

# Path: xform_expr.py
# def expr_subst(expr, subst_dict):
# 
#     if isinstance(expr, (VALUE, STR, ADDR, SFUNC, TYPE)):
#         return None
# 
#     if isinstance(expr, REG):
#         new = subst_dict.get(expr)
#         if new and expr in new:
#             _log.debug("Trying to replace %s with recursively referring %s, not doing" % (expr, new))
#             return None
#         if new and len(new) > 10:
#             _log.debug("Trying to replace %s with complex [len=%d] %s, not doing" % (expr, len(new), new))
#             return None
#         return new
# 
#     if isinstance(expr, MEM):
#         new = expr_subst(expr.expr, subst_dict)
#         if new:
#             return MEM(expr.type, new)
#         return
# 
#     if isinstance(expr, COND):
#         # This performs substituations in-place, because the same
#         # COND instance is referenced in inst (to be later removed
#         # by remove_trailing_jumps) and in out edges in CFG.
#         new = expr_subst(expr.expr, subst_dict)
#         if new:
#             expr.expr = new
#             simplify_cond(expr)
#         return
# 
#     if isinstance(expr, EXPR):
#         new_args = []
#         was_new = False
#         for a in expr.args:
#             new = expr_subst(a, subst_dict)
#             if new is None:
#                 new = a
#             else:
#                 was_new = True
#             new_args.append(new)
#         if not was_new:
#             return None
# 
#         new_expr = EXPR(expr.op, new_args)
#         new_expr = simplify_expr(new_expr)
#         return new_expr
# 
#     assert 0, repr((expr, type(expr)))


# Path: xform_bblock.py
from core import *
from xform_expr import expr_subst



def bblock_propagation(bblock, propagated_types, subst_insts=True):

    def kill_subst_uses(subst, kill_var):
        "Remove from subst dict any expressions involving kill_var"
        def not_used(var, expr):
            # Here we assume that expression can be at most reference to a var,
            # which is true for at most copy propagation, but to handle expr
            # propagation, need to do better
            return var not in expr
        subst = dict((k, v) for k, v in subst.items() if not_used(kill_var, v))
        # We of course should kill state for var itself
        subst.pop(kill_var, None)
        return subst

    state = bblock.props.get("state_in", {})
    for i, inst in enumerate(bblock.items):

        n_dest = inst.dest
        kill_n_dest = False
        if isinstance(n_dest, MEM):
            new = expr_subst(n_dest, state)
            if new:
                n_dest = new
                kill_n_dest = True

        args = copy.deepcopy(inst.args)

        for arg_no, arg in enumerate(args):

            repl = expr_subst(arg, state)