bool DoTransformStackAllocInitializer(Block body, int pos)
        {
            if (pos >= body.Instructions.Count - 2)
            {
                return(false);
            }
            ILInstruction inst = body.Instructions[pos];

            if (inst.MatchStLoc(out var v, out var locallocExpr) && locallocExpr.MatchLocAlloc(out var lengthInst))
            {
                if (lengthInst.MatchLdcI(out var lengthInBytes) && HandleCpblkInitializer(body, pos + 1, v, lengthInBytes, out var blob, out var elementType))
                {
                    context.Step("HandleCpblkInitializer", inst);
                    var block     = new Block(BlockKind.StackAllocInitializer);
                    var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType));
                    block.Instructions.Add(new StLoc(tempStore, locallocExpr));

                    while (blob.RemainingBytes > 0)
                    {
                        block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType));
                    }

                    block.FinalInstruction = new LdLoc(tempStore);
                    body.Instructions[pos] = new StLoc(v, block);
                    body.Instructions.RemoveAt(pos + 1);
                    ILInlining.InlineIfPossible(body, pos, context);
                    ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
                    return(true);
                }
                if (HandleSequentialLocAllocInitializer(body, pos + 1, v, locallocExpr, out elementType, out StObj[] values, out int instructionsToRemove))
                {
                    context.Step("HandleSequentialLocAllocInitializer", inst);
                    var block     = new Block(BlockKind.StackAllocInitializer);
                    var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType));
                    block.Instructions.Add(new StLoc(tempStore, locallocExpr));
                    block.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType)));
                    block.FinalInstruction = new LdLoc(tempStore);
                    body.Instructions[pos] = new StLoc(v, block);
                    body.Instructions.RemoveRange(pos + 1, instructionsToRemove);
                    ILInlining.InlineIfPossible(body, pos, context);
                    ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
                    return(true);
                }
            }
        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);
        }
        bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody)
        {
            // ConditionDetection favours leave inside if and branch at end of block
            // while-loop:
            // if (!loop-condition) leave loop-container
            // ...
            condition = null;
            loopBody  = loop.EntryPoint;
            if (!(loopBody.Instructions[0] is IfInstruction ifInstruction))
            {
                return(false);
            }

            if (!ifInstruction.FalseInst.MatchNop())
            {
                return(false);
            }

            if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
            {
                return(false);
            }

            condition = ifInstruction;
            if (!ifInstruction.TrueInst.MatchLeave(loop))
            {
                // sometimes the loop-body is nested within the if
                // if (loop-condition) { loop-body }
                // leave loop-container

                if (loopBody.Instructions.Count != 2 || !loop.EntryPoint.Instructions.Last().MatchLeave(loop))
                {
                    return(false);
                }

                if (!ifInstruction.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    ((Block)ifInstruction.TrueInst).Instructions.Add(new Leave(loop));
                }

                ConditionDetection.InvertIf(loopBody, ifInstruction, context);
            }

            context.Step("Transform to while (condition) loop: " + loop.EntryPoint.Label, loop);
            loop.Kind = ContainerKind.While;
            //invert comparison
            ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition);
            ifInstruction.FalseInst = ifInstruction.TrueInst;
            //move the rest of the body into a new block
            loopBody = ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count);
            loop.Blocks.Insert(1, loopBody);
            if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
            {
                loopBody.Instructions.Add(new Leave(loop));
            }

            ifInstruction.TrueInst = new Branch(loopBody);
            ExpressionTransforms.RunOnSingleStatement(ifInstruction, context);

            // Analyze conditions and decide whether to move some of them out of the condition block:

            /*var conditions = new List<ILInstruction>();
             * SplitConditions(condition.Condition, conditions);
             * // Break apart conditions that could be a MoveNext call followed by a Current accessor call:
             * if (MightBeHeaderOfForEach(loop, conditions)) {
             *      ifInstruction.Condition = conditions[0];
             *      foreach (var cond in conditions.Skip(1).Reverse()) {
             *              IfInstruction inst;
             *              loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop)));
             *              ExpressionTransforms.RunOnSingleStatment(inst, context);
             *      }
             * }*/

            return(true);
        }
        /// <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);
        }