/// <summary>
        /// Matches a do-while loop and performs the following transformations:
        /// - combine all compatible conditions into one IfInstruction.
        /// - extract conditions into a condition block, or move the existing condition block to the end.
        /// </summary>
        bool MatchDoWhileLoop(BlockContainer loop)
        {
            (List <IfInstruction> conditions, ILInstruction exit, bool swap, bool split, bool unwrap) = AnalyzeDoWhileConditions(loop);
            // not a do-while loop, exit.
            if (conditions == null || conditions.Count == 0)
            {
                return(false);
            }
            context.Step("Transform to do-while loop: " + loop.EntryPoint.Label, loop);
            Block conditionBlock;
            // first we remove all extracted instructions from the original block.
            var originalBlock = (Block)exit.Parent;

            if (unwrap)
            {
                // we found a condition block nested in a condition that is followed by a return statement:
                // we flip the condition and swap the blocks
                Debug.Assert(originalBlock.Parent is IfInstruction);
                var returnCondition = (IfInstruction)originalBlock.Parent;
                var topLevelBlock   = (Block)returnCondition.Parent;
                Debug.Assert(topLevelBlock.Parent == loop);
                var leaveFunction = topLevelBlock.Instructions[returnCondition.ChildIndex + 1];
                Debug.Assert(leaveFunction.MatchReturn(out _));
                returnCondition.Condition = Comp.LogicNot(returnCondition.Condition);
                returnCondition.TrueInst  = leaveFunction;
                // simplify the condition:
                ExpressionTransforms.RunOnSingleStatement(returnCondition, context);
                topLevelBlock.Instructions.RemoveAt(returnCondition.ChildIndex + 1);
                topLevelBlock.Instructions.AddRange(originalBlock.Instructions);
                originalBlock = topLevelBlock;
                split         = true;
            }
            originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1);
            // we need to split the block:
            if (split)
            {
                // add a new block at the end and add a branch to the new block.
                conditionBlock = new Block();
                loop.Blocks.Add(conditionBlock);
                originalBlock.Instructions.Add(new Branch(conditionBlock));
            }
            else
            {
                // move the condition block to the end.
                conditionBlock = originalBlock;
                loop.Blocks.MoveElementToEnd(originalBlock);
            }
            // combine all conditions and the exit instruction into one IfInstruction:
            IfInstruction condition = null;

            conditionBlock.AddILRange(exit);
            foreach (var inst in conditions)
            {
                conditionBlock.AddILRange(inst);
                if (condition == null)
                {
                    condition = inst;
                    if (swap)
                    {
                        // branches must be swapped and condition negated:
                        condition.Condition = Comp.LogicNot(condition.Condition);
                        condition.FalseInst = condition.TrueInst;
                        condition.TrueInst  = exit;
                    }
                    else
                    {
                        condition.FalseInst = exit;
                    }
                }
                else
                {
                    if (swap)
                    {
                        condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition);
                    }
                    else
                    {
                        condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition);
                    }
                }
            }
            // insert the combined conditions into the condition block:
            conditionBlock.Instructions.Add(condition);
            // simplify the condition:
            ExpressionTransforms.RunOnSingleStatement(condition, context);
            // transform complete
            loop.Kind = ContainerKind.DoWhile;
            return(true);
        }
Ejemplo n.º 2
0
        private void HandleIfInstruction(ControlFlowNode cfgNode, Block block, IfInstruction ifInst, ref ILInstruction exitInst)
        {
            if (ShouldSwapIfTargets(ifInst.TrueInst, exitInst))
            {
                // "if (c) goto lateBlock; goto earlierBlock;"
                // -> "if (!c)" goto earlierBlock; goto lateBlock;
                // This reordering should make the if structure correspond more closely to the original C# source code
                context.Step("Negate if", ifInst);
                block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst;
                ifInst.TrueInst  = exitInst;
                exitInst         = block.Instructions.Last();
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);
            }

            ILInstruction trueExitInst;

            if (IsUsableBranchToChild(cfgNode, ifInst.TrueInst))
            {
                // "if (...) goto targetBlock; exitInst;"
                // -> "if (...) { targetBlock } exitInst;"
                context.Step("Inline block as then-branch", ifInst);
                var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;
                // The targetBlock was already processed, we can embed it into the if statement:
                targetBlock.Remove();
                ifInst.TrueInst = targetBlock;
                ILInstruction nestedCondition, nestedTrueInst;
                while (targetBlock.Instructions.Count > 0 &&
                       targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                {
                    nestedTrueInst = UnpackBlockContainingOnlyBranch(nestedTrueInst);
                    if (DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueInst))
                    {
                        // "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;"
                        // -> "if (... && !nestedCondition) { ... } goto exitPoint;"
                        context.Step("Combine 'if (cond1 && !cond2)' in then-branch", ifInst);
                        ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, Comp.LogicNot(nestedCondition));
                        targetBlock.Instructions.RemoveAt(0);
                        // Update targetBlock label now that we've removed the first instruction
                        if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false)
                        {
                            int offset = targetBlock.Instructions[0].ILRange.Start;
                            targetBlock.ILRange = new Interval(offset, offset);
                        }
                        continue;                         // try to find more nested conditions
                    }
                    if (nestedTrueInst is Block nestedTrueBlock &&
                        DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueBlock.Instructions.Last()) &&
                        targetBlock.HasFlag(InstructionFlags.EndPointUnreachable))
                    {
                        // "if (...) { if (nestedCondition) { trueInst...; goto exitPoint; } falseInst...; } goto exitPoint;"
                        // -> "if (...) { if (!nestedCondition) { falseInst...; } trueInst... } goto exitPoint;"
                        // (only if end-point of 'falseInst...' is unreachable)
                        context.Step("Invert nested condition to reduce number of gotos", ifInst);
                        var nestedIfInst = (IfInstruction)targetBlock.Instructions[0];
                        nestedIfInst.Condition = Comp.LogicNot(nestedCondition);
                        nestedTrueBlock.Instructions.RemoveAt(nestedTrueBlock.Instructions.Count - 1);                         // remove nested goto exitPoint;
                        // remove falseInsts from outer block
                        var falseInsts = targetBlock.Instructions.Skip(1).ToArray();
                        targetBlock.Instructions.RemoveRange(1, targetBlock.Instructions.Count - 1);
                        // add trueInsts to outer block
                        targetBlock.Instructions.AddRange(nestedTrueBlock.Instructions);
                        // add falseInsts to inner block
                        nestedTrueBlock.Instructions.ReplaceList(falseInsts);
                        nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms {
                            context = new StatementTransformContext(context)
                        });
                    }
                    break;
                }

                trueExitInst = targetBlock.Instructions.LastOrDefault();
                if (DetectExitPoints.CompatibleExitInstruction(exitInst, trueExitInst))
                {
                    // "if (...) { ...; goto exitPoint } goto exitPoint;"
                    // -> "if (...) { ... } goto exitPoint;"
                    context.Step("Remove redundant 'goto exitPoint;' in then-branch", ifInst);
                    targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
                    trueExitInst = null;
                    if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                    {
                        // "if (...) { if (nestedCondition) nestedTrueInst; } exitInst;"
                        // --> "if (... && nestedCondition) nestedTrueInst; } exitInst"
                        context.Step("Combine if conditions into logic.and (in then-branch)", ifInst);
                        ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition);
                        ifInst.TrueInst  = nestedTrueInst;
                        trueExitInst     = (nestedTrueInst as Block)?.Instructions.LastOrDefault();
                    }
                }
            }
            else
            {
                trueExitInst = ifInst.TrueInst;
            }
            if (IsUsableBranchToChild(cfgNode, exitInst))
            {
                var targetBlock   = ((Branch)exitInst).TargetBlock;
                var falseExitInst = targetBlock.Instructions.LastOrDefault();
                if (DetectExitPoints.CompatibleExitInstruction(trueExitInst, falseExitInst))
                {
                    // if (...) { ...; goto exitPoint; } goto nextBlock; nextBlock: ...; goto exitPoint;
                    // -> if (...) { ... } else { ... } goto exitPoint;
                    context.Step("Inline block as else-branch", ifInst);
                    targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
                    targetBlock.Remove();
                    ifInst.FalseInst = targetBlock;
                    exitInst         = block.Instructions[block.Instructions.Count - 1] = falseExitInst;
                    Block trueBlock = ifInst.TrueInst as Block;
                    if (trueBlock != null)
                    {
                        Debug.Assert(trueExitInst == trueBlock.Instructions.Last());
                        trueBlock.Instructions.RemoveAt(trueBlock.Instructions.Count - 1);
                    }
                    else
                    {
                        Debug.Assert(trueExitInst == ifInst.TrueInst);
                        ifInst.TrueInst = new Nop {
                            ILRange = ifInst.TrueInst.ILRange
                        };
                    }
                }
            }
            if (IsEmpty(ifInst.TrueInst))
            {
                // prefer empty true-branch to empty-else branch
                context.Step("Swap empty then-branch with else-branch", ifInst);
                var oldTrue = ifInst.TrueInst;
                ifInst.TrueInst  = ifInst.FalseInst;
                ifInst.FalseInst = new Nop {
                    ILRange = oldTrue.ILRange
                };
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);

                // After swapping, it's possible that we can introduce a short-circuit operator:
                Block         trueBlock = ifInst.TrueInst as Block;
                ILInstruction nestedCondition, nestedTrueInst;
                if (trueBlock != null && trueBlock.Instructions.Count == 1 &&
                    trueBlock.FinalInstruction is Nop &&
                    trueBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                {
                    // if (cond) if (nestedCond) nestedTrueInst
                    // ==> if (cond && nestedCond) nestedTrueInst
                    context.Step("Combine if conditions into logic.and (after branch swapping)", ifInst);
                    ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition);
                    ifInst.TrueInst  = nestedTrueInst;
                }
            }
            else if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start)
            {
                // swap true and false branches of if/else construct,
                // to bring them in the same order as the IL code
                context.Step("Swap then-branch with else-branch", ifInst);
                var oldTrue = ifInst.TrueInst;
                ifInst.TrueInst  = ifInst.FalseInst;
                ifInst.FalseInst = oldTrue;
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);
            }
        }
Ejemplo n.º 3
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.EntryPoint.Label, 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.EntryPoint.Label, 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);
                whileLoopBody.Instructions.RemoveRange(secondToLastIndex, 2);
                whileLoopBody.Instructions.Add(new Branch(newIncremenBlock));
                // complete transform.
                loop.Kind = ContainerKind.For;
            }
            return(true);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Matches a do-while loop and performs the following transformations:
        /// - combine all compatible conditions into one IfInstruction.
        /// - extract conditions into a condition block, or move the existing condition block to the end.
        /// </summary>
        bool MatchDoWhileLoop(BlockContainer loop)
        {
            (List <IfInstruction> conditions, ILInstruction exit, bool swap, bool split) = AnalyzeDoWhileConditions(loop);
            // not a do-while loop, exit.
            if (conditions == null || conditions.Count == 0)
            {
                return(false);
            }
            context.Step("Transform to do-while loop", loop);
            Block conditionBlock;
            // first we remove all extracted instructions from the original block.
            var originalBlock = (Block)exit.Parent;

            originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1);
            // we need to split the block:
            if (split)
            {
                // add a new block at the end and add a branch to the new block.
                conditionBlock = new Block();
                loop.Blocks.Add(conditionBlock);
                originalBlock.Instructions.Add(new Branch(conditionBlock));
            }
            else
            {
                // move the condition block to the end.
                conditionBlock = originalBlock;
                loop.Blocks.MoveElementToEnd(originalBlock);
            }
            // combine all conditions and the exit instruction into one IfInstruction:
            IfInstruction condition = null;

            conditionBlock.AddILRange(exit.ILRange);
            foreach (var inst in conditions)
            {
                conditionBlock.AddILRange(inst.ILRange);
                if (condition == null)
                {
                    condition = inst;
                    if (swap)
                    {
                        // branches must be swapped and condition negated:
                        condition.Condition = Comp.LogicNot(condition.Condition);
                        condition.FalseInst = condition.TrueInst;
                        condition.TrueInst  = exit;
                    }
                    else
                    {
                        condition.FalseInst = exit;
                    }
                }
                else
                {
                    if (swap)
                    {
                        condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition);
                    }
                    else
                    {
                        condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition);
                    }
                }
            }
            // insert the combined conditions into the condition block:
            conditionBlock.Instructions.Add(condition);
            // simplify the condition:
            new ExpressionTransforms().Run(conditionBlock, condition.ChildIndex, new StatementTransformContext(new BlockTransformContext(context)));
            // transform complete
            loop.Kind = ContainerKind.DoWhile;
            return(true);
        }