Esempio n. 1
0
 /// <summary>
 /// Ensures that every write to a pinned local is followed by a branch instruction.
 /// This ensures the 'pinning region' does not involve any half blocks, which makes it easier to extract.
 /// </summary>
 void SplitBlocksAtWritesToPinnedLocals(BlockContainer container)
 {
     for (int i = 0; i < container.Blocks.Count; i++)
     {
         var block = container.Blocks[i];
         for (int j = 0; j < block.Instructions.Count - 1; j++)
         {
             var inst = block.Instructions[j];
             if (inst.MatchStLoc(out ILVariable v, out var value) && v.Kind == VariableKind.PinnedLocal)
             {
                 if (block.Instructions[j + 1].OpCode != OpCode.Branch)
                 {
                     // split block after j:
                     context.Step("Split block after pinned local write", inst);
                     var newBlock = new Block();
                     for (int k = j + 1; k < block.Instructions.Count; k++)
                     {
                         newBlock.Instructions.Add(block.Instructions[k]);
                     }
                     newBlock.AddILRange(newBlock.Instructions[0]);
                     block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count);
                     block.Instructions.Add(new Branch(newBlock));
                     container.Blocks.Insert(i + 1, newBlock);
                 }
                 // in case of re-pinning (e.g. C++/CLI assignment to pin_ptr variable),
                 // it's possible for the new value to be dependent on the old.
                 if (v.IsUsedWithin(value))
                 {
                     // In this case, we need to un-inline the uses of the pinned local
                     // so that they are split off into the block prior to the pinned local write
                     var temp = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type);
                     block.Instructions.Insert(j++, new StLoc(temp, new LdLoc(v)));
                     foreach (var descendant in value.Descendants)
                     {
                         if (descendant.MatchLdLoc(v))
                         {
                             descendant.ReplaceWith(new LdLoc(temp).WithILRange(descendant));
                         }
                     }
                 }
                 if (j > 0)
                 {
                     // split block before j:
                     context.Step("Split block before pinned local write", inst);
                     var newBlock = new Block();
                     newBlock.Instructions.Add(block.Instructions[j]);
                     newBlock.Instructions.Add(block.Instructions[j + 1]);
                     newBlock.AddILRange(newBlock.Instructions[0]);
                     Debug.Assert(block.Instructions.Count == j + 2);
                     block.Instructions.RemoveRange(j, 2);
                     block.Instructions.Insert(j, new Branch(newBlock));
                     container.Blocks.Insert(i + 1, newBlock);
                 }
             }
         }
     }
 }
Esempio n. 2
0
 /// <summary>
 /// Ensures that every write to a pinned local is followed by a branch instruction.
 /// This ensures the 'pinning region' does not involve any half blocks, which makes it easier to extract.
 /// </summary>
 void SplitBlocksAtWritesToPinnedLocals(BlockContainer container)
 {
     for (int i = 0; i < container.Blocks.Count; i++)
     {
         var block = container.Blocks[i];
         for (int j = 0; j < block.Instructions.Count - 1; j++)
         {
             var        inst = block.Instructions[j];
             ILVariable v;
             if (inst.MatchStLoc(out v) && v.Kind == VariableKind.PinnedLocal)
             {
                 if (block.Instructions[j + 1].OpCode != OpCode.Branch)
                 {
                     // split block after j:
                     context.Step("Split block after pinned local write", inst);
                     var newBlock = new Block();
                     for (int k = j + 1; k < block.Instructions.Count; k++)
                     {
                         newBlock.Instructions.Add(block.Instructions[k]);
                     }
                     newBlock.AddILRange(newBlock.Instructions[0]);
                     block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count);
                     block.Instructions.Add(new Branch(newBlock));
                     container.Blocks.Insert(i + 1, newBlock);
                 }
                 if (j > 0)
                 {
                     // split block before j:
                     context.Step("Split block before pinned local write", inst);
                     var newBlock = new Block();
                     newBlock.Instructions.Add(block.Instructions[j]);
                     newBlock.Instructions.Add(block.Instructions[j + 1]);
                     Debug.Assert(block.Instructions.Count == j + 2);
                     block.Instructions.RemoveRange(j, 2);
                     block.Instructions.Insert(j, new Branch(newBlock));
                     container.Blocks.Insert(i + 1, newBlock);
                 }
             }
         }
     }
 }
Esempio n. 3
0
        static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context)
        {
            Debug.Assert(container == block.Parent);
            // Ensure the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection.
            if (block.Instructions.Count > 1 && block.Instructions[block.Instructions.Count - 2].HasFlag(InstructionFlags.MayBranch))
            {
                return(false);
            }
            Branch br = block.Instructions.Last() as Branch;

            // Check whether we can combine the target block with this block
            if (br == null || br.TargetBlock.Parent != container || br.TargetBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            if (br.TargetBlock == block)
            {
                return(false);                // don't inline block into itself
            }
            context.Step("CombineBlockWithNextBlock", br);
            var targetBlock = br.TargetBlock;

            if (targetBlock.StartILOffset < block.StartILOffset && IsDeadTrueStore(block))
            {
                // The C# compiler generates a dead store for the condition of while (true) loops.
                block.Instructions.RemoveRange(block.Instructions.Count - 3, 2);
            }

            if (block.ILRangeIsEmpty)
            {
                block.AddILRange(targetBlock);
            }

            block.Instructions.Remove(br);
            block.Instructions.AddRange(targetBlock.Instructions);
            targetBlock.Instructions.Clear();             // mark targetBlock for deletion
            return(true);
        }
Esempio n. 4
0
        bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody)
        {
            // for loops have exactly two incoming edges at the entry point.
            if (loop.EntryPoint.IncomingEdgeCount != 2)
            {
                return(false);
            }
            // try to find an increment block:
            // consists of simple statements only.
            var incrementBlock = GetIncrementBlock(loop, whileLoopBody);

            if (incrementBlock != null)
            {
                // we found a possible increment block, just make sure, that there are at least three blocks:
                // - condition block
                // - loop body
                // - increment block
                if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3)
                {
                    return(false);
                }
                context.Step("Transform to for loop", loop);
                // move the block to the end of the loop:
                loop.Blocks.MoveElementToEnd(incrementBlock);
                loop.Kind = ContainerKind.For;
            }
            else
            {
                // we need to move the increment statements into its own block:
                // last must be a branch entry-point
                var last         = whileLoopBody.Instructions.LastOrDefault();
                var secondToLast = whileLoopBody.Instructions.SecondToLastOrDefault();
                if (last == null || secondToLast == null)
                {
                    return(false);
                }
                if (!last.MatchBranch(loop.EntryPoint))
                {
                    return(false);
                }
                // we only deal with 'numeric' increments
                if (!MatchIncrement(secondToLast, out var incrementVariable))
                {
                    return(false);
                }
                // the increment variable must be local/stack variable
                if (incrementVariable.Kind == VariableKind.Parameter)
                {
                    return(false);
                }
                // split conditions:
                var conditions = new List <ILInstruction>();
                SplitConditions(whileCondition.Condition, conditions);
                IfInstruction forCondition       = null;
                int           numberOfConditions = 0;
                foreach (var condition in conditions)
                {
                    // the increment variable must be used in the condition
                    if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable)))
                    {
                        break;
                    }
                    // condition should not contain an assignment
                    if (condition.Descendants.Any(IsAssignment))
                    {
                        break;
                    }
                    if (forCondition == null)
                    {
                        forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst);
                    }
                    else
                    {
                        forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition);
                    }
                    numberOfConditions++;
                }
                if (numberOfConditions == 0)
                {
                    return(false);
                }
                context.Step("Transform to for loop", loop);
                // split condition block:
                whileCondition.ReplaceWith(forCondition);
                ExpressionTransforms.RunOnSingleStatement(forCondition, context);
                for (int i = conditions.Count - 1; i >= numberOfConditions; i--)
                {
                    IfInstruction inst;
                    whileLoopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop)));
                    ExpressionTransforms.RunOnSingleStatement(inst, context);
                }
                // create a new increment block and add it at the end:
                int secondToLastIndex = secondToLast.ChildIndex;
                var newIncremenBlock  = new Block();
                loop.Blocks.Add(newIncremenBlock);
                // move the increment instruction:
                newIncremenBlock.Instructions.Add(secondToLast);
                newIncremenBlock.Instructions.Add(last);
                newIncremenBlock.AddILRange(secondToLast.ILRange);
                whileLoopBody.Instructions.RemoveRange(secondToLastIndex, 2);
                whileLoopBody.Instructions.Add(new Branch(newIncremenBlock));
                // complete transform.
                loop.Kind = ContainerKind.For;
            }
            return(true);
        }
Esempio n. 5
0
        private void DetectSwitchBody(Block block, SwitchInstruction switchInst)
        {
            Debug.Assert(block.Instructions.Last() == switchInst);
            ControlFlowNode h = context.ControlFlowNode;             // CFG node for our switch head

            Debug.Assert(h.UserData == block);
            Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited));

            isSwitch    = true;
            loopContext = new SwitchDetection.LoopContext(context.ControlFlowGraph, h);

            var nodesInSwitch = new List <ControlFlowNode>();

            nodesInSwitch.Add(h);
            h.Visited = true;
            ExtendLoop(h, nodesInSwitch, out var exitPoint);
            if (exitPoint != null && h.Dominates(exitPoint) && exitPoint.Predecessors.Count == 1 && !HasReachableExit(exitPoint))
            {
                // If the exit point is reachable from just one single "break;",
                // it's better to move the code into the switch.
                // (unlike loops which should not be nested unless necessary,
                //  nesting switches makes it clearer in which cases a piece of code is reachable)
                nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren));
                foreach (var node in nodesInSwitch)
                {
                    node.Visited = true;
                }
                exitPoint = null;
            }

            context.Step("Create BlockContainer for switch", switchInst);
            // 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)
            nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber));
            Debug.Assert(nodesInSwitch[0] == h);
            foreach (var node in nodesInSwitch)
            {
                node.Visited = false;                 // reset visited flag so that we can find outer loops
                Debug.Assert(h.Dominates(node), "The switch body must be dominated by the switch head");
            }

            BlockContainer switchContainer = new BlockContainer(ContainerKind.Switch);
            Block          newEntryPoint   = new Block();

            newEntryPoint.AddILRange(switchInst);
            switchContainer.Blocks.Add(newEntryPoint);
            newEntryPoint.Instructions.Add(switchInst);
            block.Instructions[block.Instructions.Count - 1] = switchContainer;

            Block exitTargetBlock = (Block)exitPoint?.UserData;

            if (exitTargetBlock != null)
            {
                block.Instructions.Add(new Branch(exitTargetBlock));
            }

            switchContainer.AddILRange(newEntryPoint);
            MoveBlocksIntoContainer(nodesInSwitch, switchContainer);

            // Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
            foreach (var branch in switchContainer.Descendants.OfType <Branch>())
            {
                if (branch.TargetBlock == exitTargetBlock)
                {
                    branch.ReplaceWith(new Leave(switchContainer).WithILRange(branch));
                }
            }

            isSwitch = false;
        }