Example #1
0
        /// <summary>
        ///   if (...) br trueBlock;
        /// ->
        ///   if (...) { trueBlock... }
        ///
        /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1)
        /// </summary>
        private bool InlineTrueBranch(Block block, IfInstruction ifInst)
        {
            if (!CanInline(ifInst.TrueInst))
            {
                if (block.Instructions.SecondToLastOrDefault() == ifInst && ifInst.FalseInst.MatchNop())
                {
                    var exitInst = block.Instructions.Last();
                    if (DetectExitPoints.CompatibleExitInstruction(ifInst.TrueInst, exitInst))
                    {
                        // if (...) exitInst; exitInst;
                        context.Step("Use empty block as then-branch", ifInst.TrueInst);
                        ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst);
                        // false, because we didn't inline a real block
                        // this will cause HandleIfInstruction() to attempt to inline the exitInst.
                        return(false);
                    }
                }
                return(false);
            }

            context.Step("Inline block as then-branch", ifInst.TrueInst);
            // The targetBlock was already processed, and is ready to embed
            var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;

            targetBlock.AddRef();              // Peformance: avoid temporarily disconnecting targetBlock
            targetBlock.Remove();
            ifInst.TrueInst = targetBlock;
            targetBlock.ReleaseRef();

            return(true);
        }
Example #2
0
 private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst)
 {
     Debug.Assert(sw.DefaultBody is Nop);
     // First, move blocks into the switch section
     foreach (var section in sw.Sections)
     {
         if (IsUsableBranchToChild(cfgNode, section.Body))
         {
             // case ...: goto targetBlock;
             var targetBlock = ((Branch)section.Body).TargetBlock;
             targetBlock.Remove();
             section.Body = targetBlock;
         }
     }
     // Move the code following the switch into the default section
     if (IsUsableBranchToChild(cfgNode, exitInst))
     {
         // switch(...){} goto targetBlock;
         // ---> switch(..) { default: { targetBlock } }
         var targetBlock = ((Branch)exitInst).TargetBlock;
         targetBlock.Remove();
         sw.DefaultBody = targetBlock;
         if (IsBranchOrLeave(targetBlock.Instructions.Last()))
         {
             exitInst = block.Instructions[block.Instructions.Count - 1] = targetBlock.Instructions.Last();
             targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
         }
         else
         {
             exitInst = null;
             block.Instructions.RemoveAt(block.Instructions.Count - 1);
         }
     }
     // Remove compatible exitInsts from switch sections:
     foreach (var section in sw.Sections)
     {
         Block sectionBlock = section.Body as Block;
         if (sectionBlock != null && exitInst == null && IsBranchOrLeave(sectionBlock.Instructions.Last()))
         {
             exitInst = sectionBlock.Instructions.Last();
             sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1);
             block.Instructions.Add(exitInst);
         }
         else if (sectionBlock != null && DetectExitPoints.CompatibleExitInstruction(exitInst, sectionBlock.Instructions.Last()))
         {
             sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1);
         }
     }
     sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body.ILRange.Start));
 }
Example #3
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);
            }
        }
Example #4
0
        /// <summary>
        /// Looks for common exits in the inlined then and else branches of an if instruction
        /// and performs inversions and simplifications to merge them provided they don't
        /// isolate a higher priority block exit
        /// </summary>
        private void MergeCommonBranches(Block block, IfInstruction ifInst)
        {
            var thenExits = new List <ILInstruction>();

            AddExits(ifInst.TrueInst, 0, thenExits);
            if (thenExits.Count == 0)
            {
                return;
            }

            // if there are any exits from the then branch, then the else is redundant and shouldn't exist
            Debug.Assert(IsEmpty(ifInst.FalseInst));
            Debug.Assert(ifInst.Parent == block);
            var elseExits      = new List <ILInstruction>();
            int falseInstIndex = block.Instructions.IndexOf(ifInst) + 1;

            AddExits(block, falseInstIndex, elseExits);

            var commonExits = elseExits.Where(e1 => thenExits.Any(e2 => DetectExitPoints.CompatibleExitInstruction(e1, e2)));

            // find the common exit with the highest block exit priority
            ILInstruction commonExit = null;

            foreach (var exit in commonExits)
            {
                if (commonExit == null || CompareBlockExitPriority(exit, commonExit) > 0)
                {
                    commonExit = exit;
                }
            }

            if (commonExit == null)
            {
                return;
            }

            // if the current block exit has higher priority than the exits to merge,
            // determine if this merge will isolate the current block exit
            // that is, no sequence of inversions can restore it to the block exit position
            var blockExit = block.Instructions.Last();

            if (CompareBlockExitPriority(blockExit, commonExit, true) > 0 && !WillShortCircuit(block, ifInst, commonExit))
            {
                return;
            }

            // could improve performance by directly implementing the || short-circuit when WillShortCircuit
            // currently the same general sequence of transformations introduces both operators

            context.StepStartGroup("Merge common branches " + commonExit, ifInst);
            ProduceExit(ifInst.TrueInst, 0, commonExit);
            ProduceExit(block, falseInstIndex, commonExit);

            // if (...) { ...; blockExit; } ...; blockExit;
            // -> if (...) { ...; blockExit; } else { ... } blockExit;
            if (ifInst != block.Instructions.SecondToLastOrDefault())
            {
                context.Step("Embed else-block for goto removal", ifInst);
                Debug.Assert(IsEmpty(ifInst.FalseInst));
                ifInst.FalseInst = ExtractBlock(block, block.Instructions.IndexOf(ifInst) + 1, block.Instructions.Count - 1);
            }

            // if (...) { ...; goto blockExit; } blockExit;
            // -> if (...) { ... } blockExit;
            // OR
            // if (...) { ...; goto blockExit; } else { ... } blockExit;
            // -> if (...) { ... } else { ... } blockExit;
            context.Step("Remove redundant 'goto blockExit;' in then-branch", ifInst);
            if (!(ifInst.TrueInst is Block trueBlock) || trueBlock.Instructions.Count == 1)
            {
                ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst);
            }