Beispiel #1
0
        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);
                }
            }
        /// stloc V_1(dynamic.isevent (target))
        /// if (logic.not(ldloc V_1)) Block IL_004a {
        ///     stloc V_2(dynamic.getmember B(target))
        /// }
        /// [stloc copyOfValue(value)]
        /// if (logic.not(ldloc V_1)) Block IL_0149 {
        ///     dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2,  value))
        /// } else Block IL_0151 {
        ///     dynamic.invokemember.invokespecial.discard add_B(target, value)
        /// }
        /// =>
        /// if (logic.not(dynamic.isevent (target))) Block IL_0149 {
        ///     dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(dynamic.getmember B(target),  value))
        /// } else Block IL_0151 {
        ///     dynamic.invokemember.invokespecial.discard add_B(target, value)
        /// }
        public void Run(Block block, int pos, StatementTransformContext context)
        {
            if (!(pos + 3 < block.Instructions.Count && block.Instructions[pos].MatchStLoc(out var flagVar, out var inst) && inst is DynamicIsEventInstruction isEvent))
            {
                return;
            }
            if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2))
            {
                return;
            }
            if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore))
            {
                return;
            }
            if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst))
            {
                return;
            }
            int offset = 2;

            if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) &&
                pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 &&
                valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction))
            {
                offset++;
            }
            foreach (var descendant in block.Instructions[pos + offset].Descendants)
            {
                if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse))
                {
                    continue;
                }
                context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]);
                // Collapse duplicate condition
                getMemberVarUse.ReplaceWith(getMemberInst);
                isEventConditionUse.ReplaceWith(isEvent);
                block.Instructions.RemoveRange(pos, 2);
                // Reuse ExpressionTransforms
                ExpressionTransforms.TransformDynamicSetMemberInstruction(setMemberInst, context);
                context.RequestRerun();
                break;
            }
        }
        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  = null;
            if (!(loop.EntryPoint.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))
            {
                return(false);
            }

            context.Step("Transform to while (condition) loop", 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);
             *      }
             * }*/

            // Invert condition and unwrap nested block, if loop ends in a break or return statement preceeded by an IfInstruction.

            /*while (loopBody.Instructions.Last() is Leave leave && loopBody.Instructions.SecondToLastOrDefault() is IfInstruction nestedIf && nestedIf.FalseInst.MatchNop()) {
             *      switch (nestedIf.TrueInst) {
             *              case Block nestedBlock:
             *                      loopBody.Instructions.RemoveAt(leave.ChildIndex);
             *                      loopBody.Instructions.AddRange(nestedBlock.Instructions);
             *                      break;
             *              case Branch br:
             *                      leave.ReplaceWith(nestedIf.TrueInst);
             *                      break;
             *              default:
             *                      return true;
             *      }
             *      nestedIf.Condition = Comp.LogicNot(nestedIf.Condition);
             *      nestedIf.TrueInst = leave;
             *      ExpressionTransforms.RunOnSingleStatment(nestedIf, context);
             *      if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
             *              loopBody.Instructions.Add(new Leave(loop));
             * }*/
            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);
                // 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);
        }
        /// <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);
            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.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:
            ExpressionTransforms.RunOnSingleStatement(condition, context);
            // transform complete
            loop.Kind = ContainerKind.DoWhile;
            return(true);
        }
Beispiel #6
0
        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);
            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);
        }
Beispiel #7
0
        bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody)
        {
            // while-loop:
            // if (loop-condition) br loop-content-block
            // leave loop-container
            // -or-
            // if (loop-condition) block content-block
            // leave loop-container
            condition = null;
            loopBody  = null;
            if (loop.EntryPoint.Instructions.Count != 2)
            {
                return(false);
            }
            if (!(loop.EntryPoint.Instructions[0] is IfInstruction ifInstruction))
            {
                return(false);
            }
            if (!ifInstruction.FalseInst.MatchNop())
            {
                return(false);
            }
            if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
            {
                return(false);
            }
            condition = ifInstruction;
            var trueInst = ifInstruction.TrueInst;

            if (!loop.EntryPoint.Instructions[1].MatchLeave(loop))
            {
                return(false);
            }
            if (trueInst is Block b)
            {
                context.Step("Transform to while (condition) loop", loop);
                // move the loop body to its own block:
                loopBody = b;
                trueInst.ReplaceWith(new Branch(loopBody));
                loop.Blocks.Insert(1, loopBody);
                // sometimes the loop-content-block does not end with a leave/branch
                if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    loopBody.Instructions.Add(new Leave(loop));
                }
            }
            else if (trueInst is Branch br)
            {
                context.Step("Transform to while (condition) loop", loop);
                loopBody = br.TargetBlock;
            }
            else
            {
                return(false);
            }
            // 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);
                }
            }
            // move the branch/leave instruction into the condition block
            ifInstruction.FalseInst = loop.EntryPoint.Instructions[1];
            loop.EntryPoint.Instructions.RemoveAt(1);
            loop.Kind = ContainerKind.While;
            // Invert condition and unwrap nested block, if loop ends in a break or return statement preceeded by an IfInstruction.
            while (loopBody.Instructions.Last() is Leave leave && loopBody.Instructions.SecondToLastOrDefault() is IfInstruction nestedIf && nestedIf.FalseInst.MatchNop())
            {
                switch (nestedIf.TrueInst)
                {
                case Block nestedBlock:
                    loopBody.Instructions.RemoveAt(leave.ChildIndex);
                    loopBody.Instructions.AddRange(nestedBlock.Instructions);
                    break;

                case Branch br:
                    leave.ReplaceWith(nestedIf.TrueInst);
                    break;

                default:
                    return(true);
                }
                nestedIf.Condition = Comp.LogicNot(nestedIf.Condition);
                nestedIf.TrueInst  = leave;
                ExpressionTransforms.RunOnSingleStatment(nestedIf, context);
                if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    loopBody.Instructions.Add(new Leave(loop));
                }
            }
            return(true);
        }