public Offspring(IScope neighborScope, ControlFlowBlock root, ControlFlowBlock head) { Scope = neighborScope; Root = root; Head = head; CalculateBodyAndPivots(); InitializeControlFlowGraph(); }
public ViewOfControlFlowGraph InferBranch(ControlFlowBlock headOfBranch, out ReadOnlyCollection <Offspring> offsprings_out) { var cfg = LocalCfg; var cflow = cfg.Cflow(headOfBranch, _conv).Except(_conv); var wannabes = cfg.Cflow(headOfBranch).Except(cfg.Cflow(_conv)); var closure = cflow.Closure(wannabes, (vin, vout) => vout != _head && cfg.Vedge(vout, vin) != null); var vertices = closure.Except(_conv).OrderBy(v => cfg.Cflow().IndexOf(v)).ToReadOnly(); if (vertices.IsEmpty()) { var branch = cfg.CreateView(vertices).CreateEigenStartAndFinish(); branch.AddEigenEdge(new ControlFlowEdge(branch.Start, branch.Finish)); offsprings_out = Seq.Empty <Offspring>().ToReadOnly(); return(branch); } else { var offsprings = new List <Offspring>(); var branch = cfg.CreateView(vertices, (e, vcfg) => { if (vertices.Contains(e.Target)) { (e.Source == _head && e.Target == headOfBranch && e.IsConditional).AssertTrue(); vcfg.AddEigenEdge(new ControlFlowEdge(vcfg.Start, e.Target)); } else { if (e.Target == _conv) { vcfg.AddEigenEdge(new ControlFlowEdge(e.Source, vcfg.Finish, e.Tag)); } else { (cfg.Vedges(e.Source, null).Count() == 2).AssertTrue(); offsprings.Add(new Offspring(this, e.Source, e.Target)); } } }); (branch.Start != null && branch.Finish != null).AssertTrue(); offsprings_out = offsprings.ToReadOnly(); return(branch); } }
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) { return; } // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); Instruction first; if (key.Type == BlockKeyType.Incremental) { body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldloc, ctx.StateVariable)); switch (ctx.Random.NextInt32(3)) { case 0: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState ^ key.ExitState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Xor)); break; case 1: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.ExitState - key.EntryState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Add)); break; case 2: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState - key.ExitState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Sub)); break; } body.Instructions.Insert(targetIndex + 3, Instruction.Create(OpCodes.Stloc, ctx.StateVariable)); } else { body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldc_I4, (int)key.ExitState)); body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Stloc, ctx.StateVariable)); } ctx.Graph.Body.ReplaceReference(block.Header, first); }
private IfScope(BlockScope parent, ControlFlowBlock head) { _parent = parent.AssertNotNull(); _head = head.AssertNotNull(); parent.Hir.AddElements(head.BalancedCode); _if.Test = head.Residue.AssertSingle(); var v_true = LocalCfg.TreeVedges(head, null).AssertSingle(e => e.Tag == PredicateType.IsTrue).Target; _ifTrue = this.InferBranch(v_true, out _trueOffsprings); var v_false = LocalCfg.TreeVedges(head, null).AssertSingle(e => e.Tag == PredicateType.IsFalse).Target; _ifFalse = this.InferBranch(v_false, out _falseOffsprings); _if.IfTrue = BlockScope.Decompile(this, _ifTrue).Hir; _if.IfFalse = BlockScope.Decompile(this, _ifFalse).Hir; }
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))); }); }
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) return; // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); Instruction first; if (key.Type == BlockKeyType.Incremental) { body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldloc, ctx.StateVariable)); switch (ctx.Random.NextInt32(3)) { case 0: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState ^ key.ExitState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Xor)); break; case 1: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.ExitState - key.EntryState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Add)); break; case 2: body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState - key.ExitState))); body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Sub)); break; } body.Instructions.Insert(targetIndex + 3, Instruction.Create(OpCodes.Stloc, ctx.StateVariable)); } else { body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldc_I4, (int)key.ExitState)); body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Stloc, ctx.StateVariable)); } ctx.Graph.Body.ReplaceReference(block.Header, first); }
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) { return; } Instruction first = null; // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); CFGState entry; if (!ctx.StatesMap.TryGetValue(key.EntryState, out entry)) { key.Type = BlockKeyType.Explicit; } if (key.Type == BlockKeyType.Incremental) { // Incremental CFGState exit; if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) { // Create new exit state // Update one of the entry states to be exit state exit = entry; int updateId = ctx.Random.NextInt32(3); uint targetValue = ctx.Random.NextUInt32(); exit.UpdateExplicit(updateId, targetValue); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(false, updateId, getId); var incr = entry.GetIncrementalUpdate(updateId, targetValue); body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); ctx.StatesMap[key.ExitState] = exit; } else { // Scan for updated state var headerIndex = targetIndex; for (int stateId = 0; stateId < 4; stateId++) { if (entry.Get(stateId) == exit.Get(stateId)) { continue; } uint targetValue = exit.Get(stateId); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(false, stateId, getId); var incr = entry.GetIncrementalUpdate(stateId, targetValue); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); } first = body.Instructions[headerIndex]; } } else { // Explicit CFGState exit; if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) { // Create new exit state from random seed var seed = ctx.Random.NextUInt32(); exit = new CFGState(seed); body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)seed)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxCtor)); ctx.StatesMap[key.ExitState] = exit; } else { // Scan for updated state var headerIndex = targetIndex; for (int stateId = 0; stateId < 4; stateId++) { uint targetValue = exit.Get(stateId); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(true, stateId, getId); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)targetValue)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); } first = body.Instructions[headerIndex]; } } ctx.Graph.Body.ReplaceReference(block.Header, first); }
private LoopScope(BlockScope parent, ControlFlowBlock head) { _parent = parent.AssertNotNull(); _head = head.AssertNotNull(); var cfg = LocalCfg; var jumpsToHead = cfg.BackVedges(null, _head).Select(e => e.Source).ToReadOnly(); jumpsToHead.AssertNotEmpty(); _bot = jumpsToHead.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); var cflow = cfg.Cflow(_head, _bot); // todo. check this // // all loops can be divided into four categories: // 1) while-do (head is test, either bot or head is continue label), // 2) while-true (neither head, nor bot are tests; actually, head is hardcoded to "CS$... = 1" // also either bot or head serve as continue label), // 3) do-while (bot is test, it is also the only option for continue // (since here we don't need to separate iteration block from other body instructions, // and because of (nota bene - never knew it) the fact that continue in do-while // first tests exit condition and only then proceeds with the next iteration), // 4) some loop with offspring embedded into either head or bot // // in the first two cases before proceeding to detect subscopes and offsprings // we need to find out what's the continue label. that's done as follows. // todo. we don't process case #4, since that's just too much for me // // if cflow features multiple jumps to head, then head is a continue label // else (if there's only one jump, i.e. from bot), then bot is a continue label // // now how we distinguish an offspring from loop's body? // answer: using regular closure algorithm, i.e. // 1) we pick a bot2 vertex (see below for options), // 2) we init the closure with cflow(head, bot2) + bot2 // 3) we expand the closure using universum of parent's local cfg // 4) ... and relation of being reachable w/o touching _test and _iter // // so, bot2 is a logical bottom of the loop's body (while iter exists on its own!) // 1) if bot ain't a continue label, then bot2 = bot // 2) if bot is a continue label, then check number of jumps to bot // 3) if the latter is one, then bot2 = bot // 4) else bot2 = the last one that jumps to bot // // ah, the last point... how do we find convergence? // for cases 1 and 3 conv is the node that receives a jump from either head or bot // for case 2 conv is the node that receives a jump from the loop's body // todo. here we use a hack, assuming that there's exactly 1 jump from the loop's body // if there're two or more jumps we will just work incorrectly // if there're zero jumps, then it's fail anyways, since we've just entered an infinite loop // todo. also verify that test has supported type // also don't forget that IL allows using almost everything as a condition of a test // cf. brzero === brnull === brfalse, brtrue === brinst var hcond = _head.Residue.IsNotEmpty(); var bcond = _bot.Residue.IsNotEmpty(); (hcond && bcond).AssertFalse(); // case #1. while-do if (hcond && !bcond) { _loop.IsWhileDo = true; _test = _head; _loop.Test = _test.Residue.AssertSingle(); parent.Hir.AddElements(_test.BalancedCode); var next1 = cfg.Vedges(_test, null).First().Target; var next2 = cfg.Vedges(_test, null).Second().Target; (cflow.Contains(next1) || cflow.Contains(next2)).AssertTrue(); _conv = cflow.Contains(next1) ? next2 : next1; _bodyHead = cflow.Contains(next1) ? next1 : next2; var botIsContinue = jumpsToHead.Count() == 1; if (botIsContinue) { var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); var lastJumpToBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); _continue = jumpsToBot.Count() > 1 ? _bot : null; _bodyBot = jumpsToBot.Count() > 1 ? lastJumpToBot : _bot; } else { _continue = _head; _bodyBot = _bot; } } // case #2. while-true else if (!hcond && !bcond) { _head.Residue.AssertEmpty(); var es = _head.BalancedCode.AssertSingle(); var ass = es.AssertCast <Assign>(); (ass.Rhs.AssertCast <Const>().Value.AssertCast <int>() == 1).AssertTrue(); _loop.IsWhileDo = true; _loop.Test = new Const(true); _test = _head; // todo. here we use a hack, assuming that there's exactly 1 jump from the loop's body var alienEdges = cfg.AlienEdges(cflow); var breaks = alienEdges.Except(cfg.Vedges(null, _head), cfg.Vedges(null, _bot)); _conv = breaks.Select(e => e.Target).Distinct().AssertSingle(); _bodyHead = cfg.Vedges(_head, null).AssertSingle().Target; var botIsContinue = jumpsToHead.Count() == 1; if (botIsContinue) { var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); var lastJumpToBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); _continue = (botIsContinue && jumpsToBot.Count() > 1) ? _bot : null; _bodyBot = (botIsContinue && jumpsToBot.Count() > 1) ? lastJumpToBot : _bot; } else { _continue = _head; _bodyBot = _bot; } } // case #3: do-while else if (!hcond && bcond) { _loop.IsDoWhile = true; _test = _bot; _loop.Test = _test.Residue.AssertSingle(); parent.Hir.AddElements(_test.BalancedCode); var next1 = cfg.Vedges(_test, null).First().Target; var next2 = cfg.Vedges(_test, null).Second().Target; (cflow.Contains(next1) || cflow.Contains(next2)).AssertTrue(); _conv = cflow.Contains(next1) ? next2 : next1; _bodyHead = cflow.Contains(next1) ? next1 : next2; var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); _continue = jumpsToBot.Count() == 1 ? null : _bot; _bodyBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); } // case #4: mysterious loop else { // todo. we don't process case #4, since that's just too much for me now throw AssertionHelper.Fail(); } // here we need to pass a hint to DoDecompileScopesForLoopLocals if (_continue != null) { if (_continue == _test) { _loop.Iter.Add(null); } else { _loop.Iter.SetElements(_continue.AssertThat(cfb => cfb.Residue.IsEmpty()).BalancedCode); } } _body = this.InferBody(out _offsprings); _loop.Body = BlockScope.Decompile(this, _body).Hir; }
public static LoopScope Decompile(BlockScope parent, ControlFlowBlock head) { return(new LoopScope(parent, head)); }
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) return; Instruction first = null; // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); CFGState entry; if (!ctx.StatesMap.TryGetValue(key.EntryState, out entry)) { Debug.Assert(key.Type == BlockKeyType.Explicit); key.Type = BlockKeyType.Explicit; } if (key.Type == BlockKeyType.Incremental) { // Incremental CFGState exit; if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) { // Create new exit state // Update one of the entry states to be exit state exit = entry; int updateId = ctx.Random.NextInt32(3); uint targetValue = ctx.Random.NextUInt32(); exit.UpdateExplicit(updateId, targetValue); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(false, updateId, getId); var incr = entry.GetIncrementalUpdate(updateId, targetValue); body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); ctx.StatesMap[key.ExitState] = exit; } else { // Scan for updated state var headerIndex = targetIndex; for (int stateId = 0; stateId < 4; stateId++) { if (entry.Get(stateId) == exit.Get(stateId)) continue; uint targetValue = exit.Get(stateId); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(false, stateId, getId); var incr = entry.GetIncrementalUpdate(stateId, targetValue); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); } first = body.Instructions[headerIndex]; } } else { // Explicit CFGState exit; if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) { // Create new exit state from random seed var seed = ctx.Random.NextUInt32(); exit = new CFGState(seed); body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)seed)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxCtor)); ctx.StatesMap[key.ExitState] = exit; } else { // Scan for updated state var headerIndex = targetIndex; for (int stateId = 0; stateId < 4; stateId++) { uint targetValue = exit.Get(stateId); int getId = ctx.Random.NextInt32(3); var fl = CFGState.EncodeFlag(true, stateId, getId); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)targetValue)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext)); body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop)); } first = body.Instructions[headerIndex]; } } ctx.Graph.Body.ReplaceReference(block.Header, first); }
private InitialDecompilation(ControlFlowBlock block, ReadOnlyCollection <IILOp> cil, Symbols symbols) { _block = block; _cil = cil; _symbols = symbols; }
public static void DoPrimaryDecompilation(ControlFlowBlock block, ReadOnlyCollection <IILOp> cil, Symbols symbols) { new InitialDecompilation(block, cil, symbols).DoPrimaryDecompilation(); }