private void RunTest(string sExp, Action<ProcedureBuilder> builder) { var pb = new ProcedureBuilder(this.pb.Program.Architecture); builder(pb); var proc = pb.Procedure; var dg = new DominatorGraph<Block>(proc.ControlGraph, proc.EntryBlock); // Perform the initial transformation var ssa = new SsaTransform(programFlow, proc, dg); // Propagate values and simplify the results. // We hope the the sequence // esp = fp - 4 // mov [esp-4],eax // will become // esp_2 = fp - 4 // mov [fp - 8],eax var vp = new ValuePropagator(ssa.SsaState.Identifiers, proc); vp.Transform(); ssa.RenameFrameAccesses = true; ssa.AddUseInstructions = true; ssa.Transform(); var writer = new StringWriter(); proc.Write(false, writer); var sActual = writer.ToString(); if (sActual != sExp) Debug.Print(sActual); Assert.AreEqual(sExp, sActual); }
private void SetImmediatePostDominators(DominatorGraph <StructureNode> reverseDomGraph) { foreach (StructureNode node in curProc.Nodes) { node.ImmPDom = reverseDomGraph.ImmediateDominator(node); } }
private void RunTest(string sExp, Action<ProcedureBuilder> builder) { var pb = new ProcedureBuilder(this.pb.Program.Architecture); builder(pb); var proc = pb.Procedure; var dg = new DominatorGraph<Block>(proc.ControlGraph, proc.EntryBlock); var project = new Project { Programs = { this.pb.Program } }; var importResolver = new ImportResolver( project, this.pb.Program, new FakeDecompilerEventListener()); var arch = new FakeArchitecture(); var platform = new FakePlatform(null, arch); // Register r1 is assumed to always be implicit when calling // another procedure. var implicitRegs = new HashSet<RegisterStorage> { arch.GetRegister(1) }; Debug.Print("GetRegister(1) {0}", arch.GetRegister(1)); this.pb.Program.Platform = platform; this.pb.Program.Platform = new FakePlatform(null, new FakeArchitecture()); this.pb.Program.SegmentMap = new SegmentMap( Address.Ptr32(0x0000), new ImageSegment( ".text", Address.Ptr32(0), 0x40000, AccessMode.ReadWriteExecute)); // Perform the initial transformation var ssa = new SsaTransform(programFlow, proc, importResolver, dg, implicitRegs); // Propagate values and simplify the results. // We hope the the sequence // esp = fp - 4 // mov [esp-4],eax // will become // esp_2 = fp - 4 // mov [fp - 8],eax var vp = new ValuePropagator(this.pb.Program.Architecture, ssa.SsaState); vp.Transform(); ssa.RenameFrameAccesses = true; ssa.AddUseInstructions = true; ssa.Transform(); var writer = new StringWriter(); proc.Write(false, writer); var sActual = writer.ToString(); if (sActual != sExp) Debug.Print(sActual); Assert.AreEqual(sExp, sActual); }
/// <summary> /// Constructs an SsaTransform, and in the process generates the SsaState for the procedure <paramref>proc</paramref>. /// </summary> /// <param name="proc"></param> /// <param name="gr"></param> public SsaTransform(ProgramDataFlow programFlow, Procedure proc, DominatorGraph<Block> gr) { this.programFlow = programFlow; this.proc = proc; this.SsaState = new SsaState(proc, gr); this.AOrig = CreateA(); Transform(); }
/// <summary> /// Constructs an SsaTransform, and in the process generates the SsaState for the procedure <paramref>proc</paramref>. /// </summary> /// <param name="proc"></param> /// <param name="gr"></param> public SsaTransform(ProgramDataFlow programFlow, Procedure proc, DominatorGraph <Block> gr) { this.programFlow = programFlow; this.proc = proc; this.SsaState = new SsaState(proc, gr); this.AOrig = CreateA(); Transform(); }
private void RunTest(string sExp, Action <ProcedureBuilder> builder) { var pb = new ProcedureBuilder(this.pb.Program.Architecture); builder(pb); var proc = pb.Procedure; var dg = new DominatorGraph <Block>(proc.ControlGraph, proc.EntryBlock); var project = new Project { Programs = { this.pb.Program } }; var importResolver = new ImportResolver( project, this.pb.Program, new FakeDecompilerEventListener()); this.pb.Program.Platform = new FakePlatform(null, new FakeArchitecture()); this.pb.Program.ImageMap = new ImageMap( Address.Ptr32(0x0000), new ImageSegment( ".text", Address.Ptr32(0), 0x40000, AccessMode.ReadWriteExecute)); // Perform the initial transformation var ssa = new SsaTransform(programFlow, proc, importResolver, dg); // Propagate values and simplify the results. // We hope the the sequence // esp = fp - 4 // mov [esp-4],eax // will become // esp_2 = fp - 4 // mov [fp - 8],eax var vp = new ValuePropagator(this.pb.Program.Architecture, ssa.SsaState.Identifiers, proc); vp.Transform(); ssa.RenameFrameAccesses = true; ssa.AddUseInstructions = true; ssa.Transform(); var writer = new StringWriter(); proc.Write(false, writer); var sActual = writer.ToString(); if (sActual != sExp) { Debug.Print(sActual); } Assert.AreEqual(sExp, sActual); }
/// <summary> /// Executes the core of the analysis /// </summary> /// <remarks> /// The algorithm visits nodes in post-order in each iteration. This /// means that all descendants of a node will be visited (and /// hence had the chance to be reduced) before the node itself. /// The algorithm’s behavior when visiting node _n_ /// depends on hether the region at _n_ /// is acyclic (has no loop) or not. For an acyclic region, the /// algorithm tries to match the subgraph /// at _n_to an acyclic schemas (3.2). If there is no match, /// and the region is a switch candidate, then it attempts to /// refine the region at _n_ into a switch region. /// /// If _n_ is cyclic, the algorithm /// compares the region at _n_ to the cyclic schemata. /// If this fails, it refines _n_ into a loop (3.6). /// /// If both matching and refinement do not make progress, the /// current node _n_ is then skipped for the current iteration of /// the algorithm. If there is an iteration in which all /// nodes are skipped, i.e., the algorithm makes no progress, then /// the algorithm employs a last resort refinement (3.7) to /// ensure that progress can be made in the next iteration. /// </remarks> public Region Execute() { var result = BuildRegionGraph(proc); this.regionGraph = result.Item1; this.entry = result.Item2; int iterations = 0; int oldCount; int newCount; do { if (eventListener.IsCanceled()) break; ++iterations; if (iterations > 1000) { eventListener.Warn( eventListener.CreateProcedureNavigator(program, proc), "Structure analysis stopped making progress, quitting. Please report this issue at https://github.com/uxmal/reko"); DumpGraph(); break; } oldCount = regionGraph.Nodes.Count; this.doms = new DominatorGraph<Region>(this.regionGraph, result.Item2); this.unresolvedCycles = new Queue<Tuple<Region, ISet<Region>>>(); this.unresolvedNoncycles = new Queue<Tuple<Region, ISet<Region>>>(); var postOrder = new DfsIterator<Region>(regionGraph).PostOrder(entry).ToList(); bool didReduce = false; foreach (var n in postOrder) { Probe(); didReduce = false; do { if (eventListener.IsCanceled()) break; didReduce = ReduceAcyclic(n); if (!didReduce && IsCyclic(n)) { didReduce = ReduceCyclic(n); } } while (didReduce); } newCount = regionGraph.Nodes.Count; if (newCount == oldCount && newCount > 1) { // Didn't make any progress this round, // try refining unstructured regions ProcessUnresolvedRegions(); } } while (regionGraph.Nodes.Count > 1); return entry; }
// Use for debugging, but don't leave in the unit tests to avoid // useless "spew". private void DumpDominatorFrontier(DominatorGraph <string> pdg) { foreach (var n in graph.Nodes) { Console.Write("{0}:", n); foreach (var df in pdg.DominatorFrontier(n)) { Console.Write(" {0}", df); } Console.WriteLine(); } }
public Region Execute(Procedure proc) { #if NILZ We focus on the novel aspects of our algorithm in this paper and refer readers interested in any structural analysis details elided to standard sources Like vanilla structural analysis, our algorithm visits nodes in post-order in each iteration. Intuitively, this means that all descendants of a node will be visited (and hence had the chance to be reduced) before the node itself. The algorithm’s behavior when visiting node _n_ depends on hether the region at _n_ is acyclic (has no loop) or not. For an acyclic region, the algorithm tries to match the subgraph at _n_to an acyclic schemas (3.2). If there is no match, and the region is a switch candidate, then it attempts to refine the region at _n_ into a switch region (3.4). If _n_ is cyclic, the algorithm compares the region at _n_ to the cyclic schemas (3.5) If this fails, it refines _n_ into a loop (3.6). If both matching and refinement do not make progress, the current node _n_ is then skipped for the current iteration of the algorithm. If there is an iteration in which all nodes are skipped, i.e., the algorithm makes no progress, then the algorithm employs a last resort refinement (3.7) to ensure that progress can be made in the next iteration. #endif var result = BuildRegionGraph(proc); this.regionGraph = result.Item1; var entry = result.Item2; DumpGraph(); newGraph = new DiGraph<Region>(); do { this.Changed = false; this.doms = new DominatorGraph<Region>(this.regionGraph, result.Item2); this.unresolvedCycles = new List<Tuple<Region, ISet<Region>>>(); var postOrder = new DfsIterator<Region>(regionGraph).PostOrder(entry).ToList(); Debug.Print("Iterating...."); DumpGraph(); foreach (var n in postOrder) { entry = Dfs(n); } } while (Changed); return entry; }
public DominatorGraph <StructureNode> AnalyzeGraph() { var graph = new StructureGraphAdapter(curProc.Nodes); var reverseGraph = new ReverseGraph(curProc.Nodes); var infiniteLoops = FindInfiniteLoops(graph, curProc.EntryNode); AddPseudoEdgeFromInfiniteLoopsToExitNode(graph, infiniteLoops, curProc.ExitNode, reverseGraph); var pdg = new DominatorGraph <StructureNode>(reverseGraph, curProc.ExitNode); SetImmediatePostDominators(pdg); RemovePseudoEdgeFromInfiniteLoopsToExitNode(graph, infiniteLoops, curProc.ExitNode); return(pdg); }
protected override void RunTest(Program prog, FileUnitTester fut) { foreach (Procedure proc in prog.Procedures.Values) { DominatorGraph gr = new DominatorGraph(proc); Aliases alias = new Aliases(proc, prog.Architecture); alias.Transform(); SsaTransform sst = new SsaTransform(proc, gr, true); SsaState ssa = sst.SsaState; DeadCode.Eliminate(proc, ssa); ValueNumbering vn = new ValueNumbering(ssa.Identifiers); ssa.Write(fut.TextWriter); proc.Write(false, fut.TextWriter); vn.Write(fut.TextWriter); } }
private SsaTransform BuildSsaTransform(Procedure proc) { if (program.NeedsSsaTransform) { var larw = new LongAddRewriter(proc); larw.Transform(); var alias = new Aliases(proc, flow); alias.Transform(); var doms = new DominatorGraph <Block>(proc.ControlGraph, proc.EntryBlock); var sst = new SsaTransform(flow, proc, importResolver, doms, new HashSet <RegisterStorage>()); return(sst); } else { // We are assuming phi functions are already generated. var sst = new SsaTransform(proc); return(sst); } }
public LoopFinder(DirectedGraph <T> graph, T entry, DominatorGraph <T> doms) { this.graph = graph; this.entry = entry; this.loopNodes = new HashSet <T>(); this.nodeColor = new Dictionary <T, NodeColor>(); Debug.Assert(entry != null); // Find all nodes that can be reached from the back-edge // predecessors by reversed edges and paint them gray. foreach (var p in graph.Predecessors(entry)) { if (doms.DominatesStrictly(entry, p)) { // Back edge! if (!nodeColor.ContainsKey(p)) { // Unvisited back edge! BackwardVisit(p); } } else if (p == entry) { // Self-loop. loopNodes.Add(p); } } // Find all gray nodes that can be visited from the suspected // loop entry and color them black. Black nodes belong // to the loop. NodeColor color; if (nodeColor.TryGetValue(entry, out color) && color == NodeColor.Gray) { ForwardVisit(entry); } }
/// <summary> /// Executes the core of the analysis /// </summary> /// <remarks> /// The algorithm visits nodes in post-order in each iteration. This /// means that all descendants of a node will be visited (and /// hence had the chance to be reduced) before the node itself. /// The algorithm’s behavior when visiting node _n_ /// depends on hether the region at _n_ /// is acyclic (has no loop) or not. For an acyclic region, the /// algorithm tries to match the subgraph /// at _n_to an acyclic schemas (3.2). If there is no match, /// and the region is a switch candidate, then it attempts to /// refine the region at _n_ into a switch region. /// /// If _n_ is cyclic, the algorithm /// compares the region at _n_ to the cyclic schemata. /// If this fails, it refines _n_ into a loop (3.6). /// /// If both matching and refinement do not make progress, the /// current node _n_ is then skipped for the current iteration of /// the algorithm. If there is an iteration in which all /// nodes are skipped, i.e., the algorithm makes no progress, then /// the algorithm employs a last resort refinement (3.7) to /// ensure that progress can be made in the next iteration. /// </remarks> public Region Execute() { if (proc.Name.EndsWith("2150")) //$DEBUG proc.Name.ToString(); var result = BuildRegionGraph(proc); this.regionGraph = result.Item1; this.entry = result.Item2; int oldCount; int newCount; do { //if (proc.Name.EndsWith()) oldCount = regionGraph.Nodes.Count; this.doms = new DominatorGraph<Region>(this.regionGraph, result.Item2); this.unresolvedCycles = new Queue<Tuple<Region, ISet<Region>>>(); this.unresolvedNoncycles = new Queue<Tuple<Region, ISet<Region>>>(); var postOrder = new DfsIterator<Region>(regionGraph).PostOrder(entry).ToList(); bool didReduce = false; foreach (var n in postOrder) { didReduce = false; do { didReduce = ReduceAcyclic(n, false); if (!didReduce && IsCyclic(n)) { didReduce = ReduceCyclic(n); } } while (didReduce); } newCount = regionGraph.Nodes.Count; if (newCount == oldCount && newCount > 1) { // Didn't make any progress this round, // try refining unstructured regions ProcessUnresolvedRegions(); } } while (regionGraph.Nodes.Count > 1); return entry; }
private void RunTest(string sExp, Action <ProcedureBuilder> builder) { var pb = new ProcedureBuilder(this.pb.Program.Architecture); builder(pb); var proc = pb.Procedure; var dg = new DominatorGraph <Block>(proc.ControlGraph, proc.EntryBlock); // Perform the initial transformation var ssa = new SsaTransform(programFlow, proc, dg); // Propagate values and simplify the results. // We hope the the sequence // esp = fp - 4 // mov [esp-4],eax // will become // esp_2 = fp - 4 // mov [fp - 8],eax var vp = new ValuePropagator(ssa.SsaState.Identifiers, proc); vp.Transform(); ssa.RenameFrameAccesses = true; ssa.AddUseInstructions = true; ssa.Transform(); var writer = new StringWriter(); proc.Write(false, writer); var sActual = writer.ToString(); if (sActual != sExp) { Debug.Print(sActual); } Assert.AreEqual(sExp, sActual); }
/// <summary> /// Processes procedures individually, building complex expression trees out /// of the simple, close-to-the-machine code generated by the disassembly. /// </summary> /// <param name="rl"></param> public void BuildExpressionTrees() { int i = 0; foreach (Procedure proc in program.Procedures.Values) { eventListener.ShowProgress("Building complex expressions.", i, program.Procedures.Values.Count); ++i; try { var larw = new LongAddRewriter(proc, program.Architecture); larw.Transform(); Aliases alias = new Aliases(proc, program.Architecture, flow); alias.Transform(); var doms = new DominatorGraph <Block>(proc.ControlGraph, proc.EntryBlock); var sst = new SsaTransform(flow, proc, importResolver, doms); var ssa = sst.SsaState; var cce = new ConditionCodeEliminator(ssa.Identifiers, program.Platform); cce.Transform(); //var cd = new ConstDivisionImplementedByMultiplication(ssa); //cd.Transform(); DeadCode.Eliminate(proc, ssa); var vp = new ValuePropagator(program.Architecture, ssa.Identifiers, proc); vp.Transform(); DeadCode.Eliminate(proc, ssa); // Build expressions. A definition with a single use can be subsumed // into the using expression. var coa = new Coalescer(proc, ssa); coa.Transform(); DeadCode.Eliminate(proc, ssa); var liv = new LinearInductionVariableFinder( proc, ssa.Identifiers, new BlockDominatorGraph(proc.ControlGraph, proc.EntryBlock)); liv.Find(); foreach (KeyValuePair <LinearInductionVariable, LinearInductionVariableContext> de in liv.Contexts) { var str = new StrengthReduction(ssa, de.Key, de.Value); str.ClassifyUses(); str.ModifyUses(); } var opt = new OutParameterTransformer(proc, ssa.Identifiers); opt.Transform(); DeadCode.Eliminate(proc, ssa); // Definitions with multiple uses and variables joined by PHI functions become webs. var web = new WebBuilder(proc, ssa.Identifiers, program.InductionVariables); web.Transform(); ssa.ConvertBack(false); } catch (StatementCorrelatedException stex) { eventListener.Error( eventListener.CreateBlockNavigator(program, stex.Statement.Block), stex, "An error occurred during data flow analysis."); } catch (Exception ex) { eventListener.Error( new NullCodeLocation(proc.Name), ex, "An error occurred during data flow analysis."); } } }
private void CompileTest(DirectedGraphImpl<string> e, string entry) { pdg = new DominatorGraph<string>(e, entry); }
public SsaState(Procedure proc, DominatorGraph <Block> domGraph) { this.Procedure = proc; this.DomGraph = domGraph; this.ids = new SsaIdentifierCollection(); }
private void DumpDominatorFrontier(DominatorGraph<string> pdg) { foreach (var n in graph.Nodes) { Console.Write("{0}:", n); foreach (var df in pdg.DominatorFrontier(n)) { Console.Write(" {0}", df); } Console.WriteLine(); } }
/// <summary> /// Constructs an SsaTransform, and in the process generates the SsaState for the procedure <paramref>proc</paramref>. /// </summary> /// <param name="proc"></param> /// <param name="gr"></param> public SsaTransform(ProgramDataFlow programFlow, Procedure proc, IImportResolver importResolver, DominatorGraph <Block> gr, HashSet <RegisterStorage> implicitRegs) { this.programFlow = programFlow; this.proc = proc; this.importResolver = importResolver; this.implicitRegs = implicitRegs; this.SsaState = new SsaState(proc, gr); this.AOrig = CreateA(); Transform(); }
public SsaState(Procedure proc, DominatorGraph<Block> domGraph) { this.Procedure = proc; this.DomGraph = domGraph; this.ids = new SsaIdentifierCollection(); }
/// <summary> /// Processes procedures individually, building complex expression trees out /// of the simple, close-to-the-machine code generated by the disassembly. /// </summary> /// <param name="rl"></param> public void BuildExpressionTrees() { int i = 0; foreach (Procedure proc in program.Procedures.Values) { eventListener.ShowProgress("Building complex expressions.", i, program.Procedures.Values.Count); ++i; try { var larw = new LongAddRewriter(proc, program.Architecture); larw.Transform(); Aliases alias = new Aliases(proc, program.Architecture, flow); alias.Transform(); var doms = new DominatorGraph<Block>(proc.ControlGraph, proc.EntryBlock); var sst = new SsaTransform(flow, proc, doms); var ssa = sst.SsaState; var cce = new ConditionCodeEliminator(ssa.Identifiers, program.Platform); cce.Transform(); DeadCode.Eliminate(proc, ssa); var vp = new ValuePropagator(program.Architecture, ssa.Identifiers, proc); vp.Transform(); DeadCode.Eliminate(proc, ssa); // Build expressions. A definition with a single use can be subsumed // into the using expression. var coa = new Coalescer(proc, ssa); coa.Transform(); DeadCode.Eliminate(proc, ssa); var liv = new LinearInductionVariableFinder( proc, ssa.Identifiers, new BlockDominatorGraph(proc.ControlGraph, proc.EntryBlock)); liv.Find(); foreach (KeyValuePair<LinearInductionVariable, LinearInductionVariableContext> de in liv.Contexts) { var str = new StrengthReduction(ssa, de.Key, de.Value); str.ClassifyUses(); str.ModifyUses(); } var opt = new OutParameterTransformer(proc, ssa.Identifiers); opt.Transform(); DeadCode.Eliminate(proc, ssa); // Definitions with multiple uses and variables joined by PHI functions become webs. var web = new WebBuilder(proc, ssa.Identifiers, program.InductionVariables); web.Transform(); ssa.ConvertBack(false); } catch (Exception ex) { eventListener.Error(new NullCodeLocation(proc.Name), ex, "An error occurred during data flow analysis."); } } }
private void CompileTest(DirectedGraphImpl <string> e, string entry) { pdg = new DominatorGraph <string>(e, entry); }
private void RunTest(string sExp, Action <ProcedureBuilder> builder) { var pb = new ProcedureBuilder(this.pb.Program.Architecture); builder(pb); var proc = pb.Procedure; var dg = new DominatorGraph <Block>(proc.ControlGraph, proc.EntryBlock); var project = new Project { Programs = { this.pb.Program } }; var listener = new FakeDecompilerEventListener(); var importResolver = new ImportResolver( project, this.pb.Program, listener); var arch = new FakeArchitecture(); var platform = new FakePlatform(null, arch); // Register r1 is assumed to always be implicit when calling // another procedure. var implicitRegs = new HashSet <RegisterStorage> { arch.GetRegister(1) }; Debug.Print("GetRegister(1) {0}", arch.GetRegister(1)); this.pb.Program.Platform = platform; this.pb.Program.Platform = new FakePlatform(null, new FakeArchitecture()); this.pb.Program.SegmentMap = new SegmentMap( Address.Ptr32(0x0000), new ImageSegment( ".text", Address.Ptr32(0), 0x40000, AccessMode.ReadWriteExecute)); // Perform the initial transformation var ssa = new SsaTransform(programFlow, proc, importResolver, dg, implicitRegs); // Propagate values and simplify the results. // We hope the the sequence // esp = fp - 4 // mov [esp-4],eax // will become // esp_2 = fp - 4 // mov [fp - 8],eax var vp = new ValuePropagator(this.pb.Program.Architecture, ssa.SsaState, listener); vp.Transform(); ssa.RenameFrameAccesses = true; ssa.AddUseInstructions = true; ssa.Transform(); var writer = new StringWriter(); proc.Write(false, writer); var sActual = writer.ToString(); if (sActual != sExp) { Debug.Print(sActual); } Assert.AreEqual(sExp, sActual); ssa.SsaState.CheckUses(s => Assert.Fail(s)); }
/// <summary> /// </summary> /// <remarks> /// The algorithm visits nodes in post-order in each iteration. This /// means that all descendants of a node will be visited (and /// hence had the chance to be reduced) before the node itself. /// The algorithm’s behavior when visiting node _n_ /// depends on hether the region at _n_ /// is acyclic (has no loop) or not. For an acyclic region, the /// algorithm tries to match the subgraph /// at _n_to an acyclic schemas (3.2). If there is no match, /// and the region is a switch candidate, then it attempts to /// refine the region at _n_ into a switch region. /// /// If _n_ is cyclic, the algorithm /// compares the region at _n_ to the cyclic schemata. /// If this fails, it refines _n_ into a loop (3.6). /// /// If both matching and refinement do not make progress, the /// current node _n_ is then skipped for the current iteration of /// the algorithm. If there is an iteration in which all /// nodes are skipped, i.e., the algorithm makes no progress, then /// the algorithm employs a last resort refinement (3.7) to /// ensure that progress can be made in the next iteration. /// </remarks> public Region Execute() { var result = BuildRegionGraph(proc); this.regionGraph = result.Item1; var entry = result.Item2; int oldCount; int newCount; DumpGraph(); do { oldCount = regionGraph.Nodes.Count; this.doms = new DominatorGraph<Region>(this.regionGraph, result.Item2); this.postDoms = BuildPostDoms(); this.unresolvedCycles = new Queue<Tuple<Region, ISet<Region>>>(); this.unresolvedNoncycles = new Queue<Tuple<Region, ISet<Region>>>(); this.tailRegions = new Queue<Region>(); var postOrder = new DfsIterator<Region>(regionGraph).PostOrder(entry).ToList(); Debug.Print("== Graph contains {0} nodes ===================================", regionGraph.Nodes.Count); DumpGraph(); bool didReduce = false; foreach (var n in postOrder) { didReduce = false; do { didReduce = ReduceAcyclic(n, false); if (!didReduce && IsCyclic(n)) { didReduce = ReduceCyclic(n); } } while (didReduce); } newCount = regionGraph.Nodes.Count; if (newCount == oldCount) { #if NYI didReduce = false; // Didn't make any progress, try removing tail blocks. foreach (var n in postOrder.Where(n => regionGraph.Nodes.Contains(n))) { didreduce = !IsCyclic(n) && ReduceAcyclic(n, true); if (didreduce) break; } #endif // Didn't make any progress, try to trim away stray gotos. // if (!didReduce) ProcessUnresolvedRegions(); } } while (regionGraph.Nodes.Count > 1); return entry; }