public static void DoSplitBlocksIntoAssignmentsAndPredicates(ControlFlowGraph cfg) { cfg.Vertices.ForEach(v => { if (v.BalancedCode.IsEmpty() || v.Residue.IsEmpty()) return; var ass = (v.BalancedCode.Last() is Assign) ? v.BalancedCode.Last().AssertCast<Assign>() : null; var lhs = ass == null ? null : ass.Lhs.AssertCast<Ref>().Sym; var @ref = v.Residue.AssertSingle() is Ref ? v.Residue.AssertSingle().AssertCast<Ref>().Sym : null; if (lhs != null && @ref != null && lhs.ProtoId == @ref.ProtoId) { // todo. this introduces a nasty bug if assigned variable is reused later v.BalancedCode.RemoveLast(); v.Residue.SetElements(ass.Rhs); } if (v.BalancedCode.IsEmpty() || v.Residue.IsEmpty()) return; var v_test = new ControlFlowBlock(); v_test.Residue.Add(v.Residue.AssertSingle()); v.Residue.RemoveElements(); var outEdges = cfg.Vedges(v, null); cfg.RemoveEdges(outEdges); cfg.AddVertex(v_test); cfg.AddEdge(new ControlFlowEdge(v, v_test)); outEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(v_test, e.Target, e.Tag))); }); }
public static void DoRemoveReturnThunk(ControlFlowGraph cfg) { var preRets = cfg.Vedges(null, cfg.Finish); if (preRets.Count() > 1) return; var wannabe = preRets.AssertSingle().Source; if (wannabe.BalancedCode.IsEmpty() && wannabe.Residue.SingleOrDefault() is Ref) { var retThunk = wannabe; retThunk.BalancedCode.AssertEmpty(); var auxLocal = retThunk.Residue.AssertSingle().AssertCast<Ref>().Sym; var rets = cfg.Vedges(null, retThunk); rets.AssertEach(ret => ret.Tag == null); cfg.RemoveVertex(retThunk); rets.ForEach(ret => { var src = ret.Source; src.Residue.AssertEmpty(); var ass = src.BalancedCode.Last().AssertCast<Assign>(); var lhs = ass.Lhs.AssertCast<Ref>(); var rhs = ass.Rhs.AssertCast<Expression>(); (lhs.Sym.ProtoId == auxLocal.ProtoId).AssertTrue(); cfg.AddEdge(new ControlFlowEdge(src, cfg.Finish)); src.BalancedCode.RemoveLast(); src.Residue.Add(rhs); }); } }
public static void DoDecompileComplexConditions(ControlFlowGraph cfg) { var flow = cfg.Cflow(cfg.Start); while (true) { var vs = flow.FirstOrDefault(v => v.Residue.Count() == 1 && cfg.Vedges(v, null).Count() == 2 && cfg.Vedges(v, null).All(e => e.Target.BalancedCode.IsEmpty() && e.Target.Residue.Count() == 1)); if (vs == null) break; var conv = cfg.ConvStrict(vs); var ass = conv.BalancedCode.AssertFirst().AssertCast<Assign>(); (ass.Lhs is Ref && ass.Rhs is Loophole).AssertTrue(); var parts = cfg.Cflow(vs, conv); var innerEdges = cfg.Edges(parts, parts).ToList(); cfg.Edges(null, parts).Except(innerEdges).AssertEach(e => e.Target == vs); cfg.Edges(parts, null).Except(innerEdges).AssertEach(e => e.Source == vs || e.Source == conv); while (true) { var somethingWasChanged = false; foreach (var pivot in parts) { var pivot_inEdges = cfg.Vedges(null, pivot); if (pivot_inEdges.Count() != 1) continue; var e_pred2pivot = pivot_inEdges.AssertSingle(); var pred = e_pred2pivot.Source; var pred_outEdges = cfg.Vedges(pred, null); if (pred_outEdges.Count() != 2) continue; var e_pred2target1 = pred_outEdges.AssertSingle(e => e.Target != pivot); var target1 = e_pred2target1.Target; var e_pivot2target1 = cfg.Vedge(pivot, target1); if (e_pivot2target1 == null) continue; var pivot_outEdges = cfg.Vedges(pivot, null); if (pivot_outEdges.Count() != 2) continue; var e_pivot2target2 = pivot_outEdges.AssertSingle(e => e.Target != target1); var target2 = e_pivot2target2.Target; var @operator = e_pred2target1.Condition == PredicateType.IsTrue ? OperatorType.OrElse : e_pred2target1.Condition == PredicateType.IsFalse ? OperatorType.AndAlso : ((Func<OperatorType>)(() => { throw AssertionHelper.Fail(); }))(); var clause_left = pred.Residue.AssertSingle(); var clause_right = pivot.Residue.AssertSingle(); var negate_rhs = e_pred2target1.Condition != e_pivot2target1.Condition; if (negate_rhs) clause_right = Operator.Not(clause_right); var junction = Operator.Create(@operator, clause_left, clause_right); cfg.RemoveVertex(pivot); cfg.AddEdge(new ControlFlowEdge(pred, target2, e_pred2target1.Condition.Negate())); pred.Residue.SetElements(junction); somethingWasChanged |= true; } if (!somethingWasChanged) break; } parts = cfg.Cflow(vs, conv); (parts.Count() == 4).AssertTrue(); var @const = parts.Except(vs, conv).AssertSingle(v => v.Residue.AssertSingle() is Const); var vnext = parts.Except(vs, conv, @const).AssertSingle(); (cfg.Vedge(@const, vnext) == null && cfg.Vedge(vnext, @const) == null).AssertTrue(); cfg.Vedge(vs, vnext).IsConditional.AssertTrue(); cfg.Vedge(vs, @const).IsConditional.AssertTrue(); cfg.Vedge(vnext, conv).IsUnconditional.AssertTrue(); cfg.Vedge(@const, conv).IsUnconditional.AssertTrue(); var estart = vs.Residue.AssertSingle(); var enext = vnext.Residue.AssertSingle(); var cond_const = @const.Residue.AssertSingle().AssertCast<Const>().Value.AssertCast<int>(); var cond_edge = cfg.Vedge(vs, @const).Condition; var val_const = cond_const == 1 ? true : cond_const == 0 ? false : ((Func<bool>)(() => { throw AssertionHelper.Fail(); }))(); var val_edge = cond_edge == PredicateType.IsTrue ? true : cond_edge == PredicateType.IsFalse ? false : ((Func<bool>)(() => { throw AssertionHelper.Fail(); }))(); var operator1 = val_const ? OperatorType.OrElse : OperatorType.AndAlso; var clause_left1 = val_edge && val_const ? estart : Operator.Not(estart); var clause_right1 = !val_edge && !val_const ? Operator.Not(enext) : enext; var junction1 = Operator.Create(operator1, clause_left1, clause_right1); var conv_outEdges = cfg.Vedges(conv, null); var conv_inEdges = cfg.Vedges(null, conv).Except(cfg.Vedges(parts, conv)); cfg.RemoveVertices(@const, vnext, conv); conv_outEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(vs, e.Target, e.Tag))); conv_inEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(e.Source, vs, e.Tag))); vs.BalancedCode.Add(new Assign(ass.Lhs, junction1)); vs.BalancedCode.AddElements(conv.BalancedCode.Skip(1)); vs.Residue.SetElements(conv.Residue); } cfg.Edges().AssertEach(e => e.Tag.Arity() <= 1); cfg.Vertices.Where(v => v.Residue.IsNotEmpty()).AssertEach(v => v.Residue.Count() == 1); cfg.Vertices.AssertNone(v => v.Residue.IsNotEmpty() && cfg.Vedges(v, null).Any(e => e.IsUnconditional && e.Target != cfg.Finish)); }
public static ControlFlowGraph DoCreateCarcass(IMethodBody cil, out ReadOnlyDictionary<ControlFlowBlock, ReadOnlyCollection<IILOp>> blocks2parts) { // create the control flow graph var cfg = new ControlFlowGraph(); // partition the code into blocks with continuous control flow // todo. support switches and protected regions var targets = new HashSet<IILOp>(cil.OfType<Branch>().Select(br => br.Target)); var l_partitions = new List<ReadOnlyCollection<IILOp>>(); var l_partition = new List<IILOp>(); Action qualifyPartition = () => { if (l_partition.IsNotEmpty()) { l_partitions.Add(l_partition.ToReadOnly()); l_partition = new List<IILOp>(); } }; foreach (var op in cil) { if (op is Branch || op is Ret) qualifyPartition(); else { if (targets.Contains(op)) qualifyPartition(); l_partition.Add(op); if (op is Throw) qualifyPartition(); } } qualifyPartition(); var partitions = l_partitions.ToReadOnly(); // create blocks and map those to ops and partitions blocks2parts = partitions.ToDictionary(p => new ControlFlowBlock(), p => p).ToReadOnly(); blocks2parts.ForEach(kvp => cfg.AddVertex(kvp.Key)); var op2blocks = new Dictionary<IILOp, ControlFlowBlock>(); blocks2parts.ForEach(kvp => kvp.Value.ForEach(op => op2blocks.Add(op, kvp.Key))); cil.ForEach(op => { if (!op2blocks.ContainsKey(op)) op2blocks.Add(op, null); }); // prepare to link the blocks Action<IILOp, IILOp, CilPredicateType?> link = (op1, op2, cil_pred) => { var source = op1 == null ? cfg.Start : op2blocks[op1]; var target = op2 == null ? cfg.Finish : op2blocks[op2]; var hir_pred = cil_pred == null ? (HirPredicateType?)null : (HirPredicateType)Enum.Parse(typeof(HirPredicateType), cil_pred.Value.ToString()); cfg.AddEdge(new ControlFlowEdge(source, target, hir_pred)); }; // link the blocks (down from 300+ LOC to this simple loop =)) if (cil.IsEmpty()) link(null, null, null); foreach (var op in cil) { // todo. support switches here if (op is Switch) throw AssertionHelper.Fail(); // todo. support general case of control flow // n0te. throw needs something on stack, so br > throw is impossible Func<IILOp, bool> isJmp = op1 => op1 is Ret || op1 is Branch; if (isJmp(op) && isJmp(op.Prev)) continue; if (isJmp(op)) { Func<IILOp, CilPredicateType?> pred = op1 => op1 is Ret ? null : op1 is Branch ? ((Branch)op1).PredicateType : ((Func<CilPredicateType?>)(() => { throw AssertionHelper.Fail(); }))(); Func<IILOp, bool> uncond = op1 => isJmp(op1) && pred(op1) == null; Func<IILOp, bool> cond = op1 => isJmp(op1) && pred(op1) != null; Func<IILOp, IILOp> target = null; target = op1 => op1 is Ret ? null : op1 is Branch ? target(((Branch)op1).Target) : op1; (target(op) is Branch).AssertFalse(); if (target(op) is Ret) link(op.Prev, null, pred(op)); else link(op.Prev, target(op), pred(op)); isJmp(op.Next).AssertImplies(uncond(op.Next)); if (cond(op)) link(op.Prev, target(op.Next), pred(op).Negate()); } else if (op is Throw) { // do nothing - throw doesn't create links } else { if (op.Prev == null) link(null, op, null); if (isJmp(op.Next)) continue; var blk = op2blocks.GetOrDefault(op); var blk_next = op2blocks.GetOrDefault(op.Next); if (blk != blk_next) link(op, op.Next, null); } } // yield control to the next step of the pipeline return cfg; }