/// <summary>
        /// Group input into a set of blocks that can be later arbitraliby schufled.
        /// The method adds necessary branches to make control flow between blocks
        /// explicit and thus order independent.
        /// </summary>
        void SplitToBasicBlocks(ILBlock block)
        {
            List <ILNode> basicBlocks = new List <ILNode>();

            ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel()
            {
                Name = "Block_" + (nextLabelIndex++)
            };
            ILBasicBlock basicBlock = new ILBasicBlock();

            basicBlocks.Add(basicBlock);
            basicBlock.Body.Add(entryLabel);
            block.EntryGoto = new ILExpression(ILCode.Br, entryLabel);

            if (block.Body.Count > 0)
            {
                if (block.Body[0] != entryLabel)
                {
                    basicBlock.Body.Add(block.Body[0]);
                }

                for (int i = 1; i < block.Body.Count; i++)
                {
                    ILNode lastNode = block.Body[i - 1];
                    ILNode currNode = block.Body[i];

                    // Start a new basic block if necessary
                    if (currNode is ILLabel ||
                        currNode is ILTryCatchBlock ||                     // Counts as label
                        lastNode.IsConditionalControlFlow() ||
                        lastNode.IsUnconditionalControlFlow())
                    {
                        // Try to reuse the label
                        ILLabel label = currNode as ILLabel ?? new ILLabel()
                        {
                            Name = "Block_" + (nextLabelIndex++).ToString()
                        };

                        // Terminate the last block
                        if (!lastNode.IsUnconditionalControlFlow())
                        {
                            // Explicit branch from one block to other
                            basicBlock.Body.Add(new ILExpression(ILCode.Br, label));
                        }

                        // Start the new block
                        basicBlock = new ILBasicBlock();
                        basicBlocks.Add(basicBlock);
                        basicBlock.Body.Add(label);

                        // Add the node to the basic block
                        if (currNode != label)
                        {
                            basicBlock.Body.Add(currNode);
                        }
                    }
                    else
                    {
                        basicBlock.Body.Add(currNode);
                    }
                }
            }

            block.Body = basicBlocks;
            return;
        }
Example #2
0
        public bool SimplifyTernaryOperator(List <ILNode> body, ILBasicBlock head, int pos)
        {
            Debug.Assert(body.Contains(head));

            ILExpression condExpr;
            ILLabel      trueLabel;
            ILLabel      falseLabel;
            ILVariable   trueLocVar = null;
            ILExpression trueExpr;
            ILLabel      trueFall;
            ILVariable   falseLocVar = null;
            ILExpression falseExpr;
            ILLabel      falseFall;
            object       unused;

            if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
                labelGlobalRefCount[trueLabel] == 1 &&
                labelGlobalRefCount[falseLabel] == 1 &&
                ((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
                  labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
                  trueLocVar == falseLocVar && trueFall == falseFall) ||
                 (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) &&
                  labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) &&
                body.Contains(labelToBasicBlock[trueLabel]) &&
                body.Contains(labelToBasicBlock[falseLabel])
                )
            {
                bool          isStloc          = trueLocVar != null;
                ILCode        opCode           = isStloc ? ILCode.Stloc : ILCode.Ret;
                TypeReference retType          = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
                bool          retTypeIsBoolean = TypeAnalysis.IsBoolean(retType);
                int           leftBoolVal;
                int           rightBoolVal;
                ILExpression  newExpr;
                // a ? true:false  is equivalent to  a
                // a ? false:true  is equivalent to  !a
                // a ? true : b    is equivalent to  a || b
                // a ? b : true    is equivalent to  !a || b
                // a ? b : false   is equivalent to  a && b
                // a ? false : b   is equivalent to  !a && b
                if (retTypeIsBoolean &&
                    trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
                    falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
                    ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
                    )
                {
                    // It can be expressed as trivilal expression
                    if (leftBoolVal != 0)
                    {
                        newExpr = condExpr;
                    }
                    else
                    {
                        newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
                    }
                }
                else if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal))
                {
                    // It can be expressed as logical expression
                    if (leftBoolVal != 0)
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
                    }
                    else
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
                    }
                }
                else if (retTypeIsBoolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal))
                {
                    // It can be expressed as logical expression
                    if (rightBoolVal != 0)
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
                    }
                    else
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
                    }
                }
                else
                {
                    // Ternary operator tends to create long complicated return statements
                    if (opCode == ILCode.Ret)
                    {
                        return(false);
                    }

                    // Only simplify generated variables
                    if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated)
                    {
                        return(false);
                    }

                    // Create ternary expression
                    newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
                }

                head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr));
                if (isStloc)
                {
                    head.Body.Add(new ILExpression(ILCode.Br, trueFall));
                }

                // Remove the old basic blocks
                body.RemoveOrThrow(labelToBasicBlock[trueLabel]);
                body.RemoveOrThrow(labelToBasicBlock[falseLabel]);

                return(true);
            }
            return(false);
        }
        List <ILNode> FindConditions(HashSet <ControlFlowNode> scope, ControlFlowNode entryNode)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            HashSet <ControlFlowNode> agenda = new HashSet <ControlFlowNode>();

            agenda.Add(entryNode);
            while (agenda.Any())
            {
                ControlFlowNode node = agenda.First();
                // Attempt for a good order
                while (agenda.Contains(node.ImmediateDominator))
                {
                    node = node.ImmediateDominator;
                }
                agenda.Remove(node);

                // Find a block that represents a simple condition
                if (scope.Contains(node))
                {
                    ILBasicBlock block = (ILBasicBlock)node.UserData;

                    {
                        // Switch
                        ILLabel[]    caseLabels;
                        ILExpression switchArg;
                        ILLabel      fallLabel;
                        if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel))
                        {
                            // Replace the switch code with ILSwitch
                            ILSwitch ilSwitch = new ILSwitch()
                            {
                                Condition = switchArg
                            };
                            block.Body.RemoveTail(ILCode.Switch, ILCode.Br);
                            block.Body.Add(ilSwitch);
                            block.Body.Add(new ILExpression(ILCode.Br, fallLabel));
                            result.Add(block);

                            // Remove the item so that it is not picked up as content
                            scope.RemoveOrThrow(node);

                            // Find the switch offset
                            int addValue = 0;
                            List <ILExpression> subArgs;
                            if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue))
                            {
                                ilSwitch.Condition = subArgs[0];
                            }

                            // Pull in code of cases
                            ControlFlowNode fallTarget = null;
                            labelToCfNode.TryGetValue(fallLabel, out fallTarget);

                            HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>();
                            if (fallTarget != null)
                            {
                                frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget }));
                            }

                            foreach (ILLabel condLabel in caseLabels)
                            {
                                ControlFlowNode condTarget = null;
                                labelToCfNode.TryGetValue(condLabel, out condTarget);
                                if (condTarget != null)
                                {
                                    frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget }));
                                }
                            }

                            for (int i = 0; i < caseLabels.Length; i++)
                            {
                                ILLabel condLabel = caseLabels[i];

                                // Find or create new case block
                                ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel);
                                if (caseBlock == null)
                                {
                                    caseBlock = new ILSwitch.CaseBlock()
                                    {
                                        Values    = new List <int>(),
                                        EntryGoto = new ILExpression(ILCode.Br, condLabel)
                                    };
                                    ilSwitch.CaseBlocks.Add(caseBlock);

                                    ControlFlowNode condTarget = null;
                                    labelToCfNode.TryGetValue(condLabel, out condTarget);
                                    if (condTarget != null && !frontiers.Contains(condTarget))
                                    {
                                        HashSet <ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
                                        scope.ExceptWith(content);
                                        caseBlock.Body.AddRange(FindConditions(content, condTarget));
                                        // Add explicit break which should not be used by default, but the goto removal might decide to use it
                                        caseBlock.Body.Add(new ILBasicBlock()
                                        {
                                            Body =
                                            {
                                                new ILLabel()
                                                {
                                                    Name = "SwitchBreak_" + (nextLabelIndex++)
                                                },
                                                new ILExpression(ILCode.LoopOrSwitchBreak, null)
                                            }
                                        });
                                    }
                                }
                                caseBlock.Values.Add(i + addValue);
                            }

                            // Heuristis to determine if we want to use fallthough as default case
                            if (fallTarget != null && !frontiers.Contains(fallTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, fallTarget);
                                if (content.Any())
                                {
                                    var caseBlock = new ILSwitch.CaseBlock()
                                    {
                                        EntryGoto = new ILExpression(ILCode.Br, fallLabel)
                                    };
                                    ilSwitch.CaseBlocks.Add(caseBlock);
                                    block.Body.RemoveTail(ILCode.Br);

                                    scope.ExceptWith(content);
                                    caseBlock.Body.AddRange(FindConditions(content, fallTarget));
                                    // Add explicit break which should not be used by default, but the goto removal might decide to use it
                                    caseBlock.Body.Add(new ILBasicBlock()
                                    {
                                        Body =
                                        {
                                            new ILLabel()
                                            {
                                                Name = "SwitchBreak_" + (nextLabelIndex++)
                                            },
                                            new ILExpression(ILCode.LoopOrSwitchBreak, null)
                                        }
                                    });
                                }
                            }
                        }

                        // Two-way branch
                        ILExpression condExpr;
                        ILLabel      trueLabel;
                        ILLabel      falseLabel;
                        if (block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
                        {
                            // Swap bodies since that seems to be the usual C# order
                            ILLabel temp = trueLabel;
                            trueLabel  = falseLabel;
                            falseLabel = temp;
                            condExpr   = new ILExpression(ILCode.LogicNot, null, condExpr);

                            // Convert the brtrue to ILCondition
                            ILCondition ilCond = new ILCondition()
                            {
                                Condition = condExpr,
                                TrueBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, trueLabel)
                                },
                                FalseBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, falseLabel)
                                }
                            };
                            block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                            block.Body.Add(ilCond);
                            result.Add(block);

                            // Remove the item immediately so that it is not picked up as content
                            scope.RemoveOrThrow(node);

                            ControlFlowNode trueTarget = null;
                            labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                            ControlFlowNode falseTarget = null;
                            labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                            // Pull in the conditional code
                            HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>();
                            if (trueTarget != null)
                            {
                                frontiers.UnionWith(trueTarget.DominanceFrontier.Except(new [] { trueTarget }));
                            }
                            if (falseTarget != null)
                            {
                                frontiers.UnionWith(falseTarget.DominanceFrontier.Except(new [] { falseTarget }));
                            }

                            if (trueTarget != null && !frontiers.Contains(trueTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);
                                scope.ExceptWith(content);
                                ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget));
                            }
                            if (falseTarget != null && !frontiers.Contains(falseTarget))
                            {
                                HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget);
                                scope.ExceptWith(content);
                                ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget));
                            }
                        }
                    }

                    // Add the node now so that we have good ordering
                    if (scope.Contains(node))
                    {
                        result.Add((ILNode)node.UserData);
                        scope.Remove(node);
                    }
                }

                // Using the dominator tree should ensure we find the the widest loop first
                foreach (var child in node.DominatorTreeChildren)
                {
                    agenda.Add(child);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }

            return(result);
        }
Example #4
0
        public bool SimplifyCustomShortCircuit(List <ILNode> body, ILBasicBlock head, int pos)
        {
            Debug.Assert(body.Contains(head));

            // --- looking for the following pattern ---
            // stloc(targetVar, leftVar)
            // brtrue(exitLabel, call(op_False, leftVar)
            // br(followingBlock)
            //
            // FollowingBlock:
            // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
            // br(exitLabel)
            // ---

            if (head.Body.Count < 3)
            {
                return(false);
            }

            // looking for:
            // stloc(targetVar, leftVar)
            ILVariable   targetVar;
            ILExpression targetVarInitExpr;

            if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr))
            {
                return(false);
            }

            ILVariable leftVar;

            if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar))
            {
                return(false);
            }

            // looking for:
            // brtrue(exitLabel, call(op_False, leftVar)
            // br(followingBlock)
            ILExpression callExpr;
            ILLabel      exitLabel;
            ILLabel      followingBlock;

            if (!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock))
            {
                return(false);
            }

            if (labelGlobalRefCount[followingBlock] > 1)
            {
                return(false);
            }

            MethodReference opFalse;
            ILExpression    opFalseArg;

            if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg))
            {
                return(false);
            }

            // ignore operators other than op_False and op_True
            if (opFalse.Name != "op_False" && opFalse.Name != "op_True")
            {
                return(false);
            }

            if (!opFalseArg.MatchLdloc(leftVar))
            {
                return(false);
            }

            ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock];

            // FollowingBlock:
            // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
            // br(exitLabel)
            ILVariable   _targetVar;
            ILExpression opBitwiseCallExpr;
            ILLabel      _exitLabel;

            if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel))
            {
                return(false);
            }

            if (_targetVar != targetVar || exitLabel != _exitLabel)
            {
                return(false);
            }

            MethodReference opBitwise;
            ILExpression    leftVarExpression;
            ILExpression    rightExpression;

            if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression))
            {
                return(false);
            }

            if (!leftVarExpression.MatchLdloc(leftVar))
            {
                return(false);
            }

            // ignore operators other than op_BitwiseAnd and op_BitwiseOr
            if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr")
            {
                return(false);
            }

            // insert:
            // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression)
            // br(exitLabel)
            ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr;

            if (op == ILCode.LogicAnd && opFalse.Name != "op_False")
            {
                return(false);
            }

            if (op == ILCode.LogicOr && opFalse.Name != "op_True")
            {
                return(false);
            }

            ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression);

            shortCircuitExpr.Operand = opBitwise;

            head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
            head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr));
            head.Body.Add(new ILExpression(ILCode.Br, exitLabel));
            body.Remove(followingBasicBlock);

            return(true);
        }
        List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>();

            agenda.Enqueue(entryPoint);
            while (agenda.Count > 0)
            {
                ControlFlowNode node = agenda.Dequeue();

                // If the node is a loop header
                if (scope.Contains(node) &&
                    node.DominanceFrontier.Contains(node) &&
                    (node != entryPoint || !excludeEntryPoint))
                {
                    HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node);

                    // If the first expression is a loop condition
                    ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
                    ILExpression condExpr;
                    ILLabel      trueLabel;
                    ILLabel      falseLabel;
                    // It has to be just brtrue - any preceding code would introduce goto
                    if (basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
                    {
                        ControlFlowNode trueTarget;
                        labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                        ControlFlowNode falseTarget;
                        labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                        // If one point inside the loop and the other outside
                        if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) ||
                            (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)))
                        {
                            loopContents.RemoveOrThrow(node);
                            scope.RemoveOrThrow(node);

                            // If false means enter the loop
                            if (loopContents.Contains(falseTarget) || falseTarget == node)
                            {
                                // Negate the condition
                                condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
                                ILLabel tmp = trueLabel;
                                trueLabel  = falseLabel;
                                falseLabel = tmp;
                            }

                            ControlFlowNode postLoopTarget;
                            labelToCfNode.TryGetValue(falseLabel, out postLoopTarget);
                            if (postLoopTarget != null)
                            {
                                // Pull more nodes into the loop
                                HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget);
                                var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n));
                                loopContents.UnionWith(pullIn);
                            }

                            // Use loop to implement the brtrue
                            basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                            basicBlock.Body.Add(new ILWhileLoop()
                            {
                                Condition = condExpr,
                                BodyBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, trueLabel),
                                    Body      = FindLoops(loopContents, node, false)
                                }
                            });
                            basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel));
                            result.Add(basicBlock);

                            scope.ExceptWith(loopContents);
                        }
                    }

                    // Fallback method: while(true)
                    if (scope.Contains(node))
                    {
                        result.Add(new ILBasicBlock()
                        {
                            Body = new List <ILNode>()
                            {
                                new ILLabel()
                                {
                                    Name = "Loop_" + (nextLabelIndex++)
                                },
                                new ILWhileLoop()
                                {
                                    BodyBlock = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()),
                                        Body      = FindLoops(loopContents, node, true)
                                    }
                                },
                            },
                        });

                        scope.ExceptWith(loopContents);
                    }
                }

                // Using the dominator tree should ensure we find the the widest loop first
                foreach (var child in node.DominatorTreeChildren)
                {
                    agenda.Enqueue(child);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }
            scope.Clear();

            return(result);
        }