/// <summary> /// Builds structured control flow for the block associated with the control flow node. /// </summary> /// <remarks> /// After a block was processed, it should use structured control flow /// and have just a single 'regular' exit point (last branch instruction in the block) /// </remarks> public void Run(Block block, BlockTransformContext context) { this.context = context; currentContainer = (BlockContainer)block.Parent; // We only embed blocks into this block if they aren't referenced anywhere else, // so those blocks are dominated by this block. // BlockILTransform thus guarantees that the blocks being embedded are already // fully processed. cfgNode = context.ControlFlowNode; Debug.Assert(cfgNode.UserData == block); // Because this transform runs at the beginning of the block transforms, // we know that `block` is still a (non-extended) basic block. // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: if (block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst) { HandleIfInstruction(block, ifInst); } else { InlineExitBranch(block); } }
public void RunTransforms(IEnumerable <IBlockTransform> transforms, BlockTransformContext context) { this.CheckInvariant(ILPhase.Normal); foreach (var transform in transforms) { context.CancellationToken.ThrowIfCancellationRequested(); context.StepStartGroup(transform.GetType().Name); transform.Run(this, context); this.CheckInvariant(ILPhase.Normal); context.StepEndGroup(); } }
/// <summary> /// Builds structured control flow for the block associated with the control flow node. /// </summary> /// <remarks> /// After a block was processed, it should use structured control flow /// and have just a single 'regular' exit point (last branch instruction in the block) /// </remarks> public void Run(Block block, BlockTransformContext context) { this.context = context; this.currentContainer = (BlockContainer)block.Parent; // We only embed blocks into this block if they aren't referenced anywhere else, // so those blocks are dominated by this block. // BlockILTransform thus guarantees that the blocks being embedded are already // fully processed. var cfgNode = context.ControlFlowNode; Debug.Assert(cfgNode.UserData == block); // Because this transform runs at the beginning of the block transforms, // we know that `block` is still a (non-extended) basic block. // Last instruction is one with unreachable endpoint // (guaranteed by combination of BlockContainer and Block invariants) Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable)); ILInstruction exitInst = block.Instructions.Last(); // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) { HandleIfInstruction(cfgNode, block, ifInst, ref exitInst); } else { SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction; if (switchInst != null) { HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst); } } if (IsUsableBranchToChild(cfgNode, exitInst)) { // "...; goto usableblock;" // -> embed target block in this block context.Step("Inline target block of unconditional branch", exitInst); var targetBlock = ((Branch)exitInst).TargetBlock; Debug.Assert(exitInst == block.Instructions.Last()); block.Instructions.RemoveAt(block.Instructions.Count - 1); block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Remove(); } }
/// <summary> /// Check whether 'block' is a loop head; and construct a loop instruction /// (nested BlockContainer) if it is. /// </summary> public void Run(Block block, BlockTransformContext context) { this.context = context; // LoopDetection runs early enough so that block should still // be in the original container at this point. Debug.Assert(block.Parent == context.ControlFlowGraph.Container); this.currentBlockContainer = context.ControlFlowGraph.Container; // Because this is a post-order block transform, we can assume that // any nested loops within this loop have already been constructed. if (block.Instructions.Last() is SwitchInstruction switchInst) { // Switch instructions support "break;" just like loops DetectSwitchBody(block, switchInst); } ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); List <ControlFlowNode> loop = null; foreach (var t in h.Predecessors) { if (h.Dominates(t)) { // h->t is a back edge, and h is a loop header // Add the natural loop of t->h to the loop. // Definitions: // * A back edge is an edge t->h so that h dominates t. // * The natural loop of the back edge is the smallest set of nodes // that includes the back edge and has no predecessors outside the set // except for the predecessor of the header. if (loop == null) { loop = new List <ControlFlowNode>(); loop.Add(h); // Mark loop header as visited so that the pre-order traversal // stops at the loop header. h.Visited = true; } t.TraversePreOrder(n => n.Predecessors, loop.Add); } } if (loop != null) { var headBlock = (Block)h.UserData; context.Step($"Construct loop with head {headBlock.Label}", headBlock); // loop now is the union of all natural loops with loop head h. // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(loop[0] == h); foreach (var node in loop) { node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); } }