Beispiel #1
0
        public override ILInstruction Clone()
        {
            var clone = new SwitchInstruction(value.Clone());

            clone.ILRange = this.ILRange;
            clone.Value   = value.Clone();
            clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone()));
            return(clone);
        }
 public override ILInstruction Clone()
 {
     return(new TryFault(TryBlock.Clone(), faultBlock.Clone())
     {
         ILRange = this.ILRange
     });
 }
 public override ILInstruction Clone()
 {
     return(new TryFinally(TryBlock.Clone(), finallyBlock.Clone())
     {
         ILRange = this.ILRange
     });
 }
 public override ILInstruction Clone()
 {
     return(new CallIndirect(CallingConvention, ReturnType, ParameterTypes,
                             this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
                             )
     {
         ILRange = this.ILRange
     });
 }
Beispiel #5
0
        /// <summary>
        /// Ensures the end point of a block is unreachable by duplicating and appending the [exit] instruction following the end point
        /// </summary>
        /// <param name="inst">The instruction/block of interest</param>
        /// <param name="fallthroughExit">The next instruction to be executed (provided inst does not exit)</param>
        private void EnsureEndPointUnreachable(ILInstruction inst, ILInstruction fallthroughExit)
        {
            if (!(inst is Block block))
            {
                Debug.Assert(inst.HasFlag(InstructionFlags.EndPointUnreachable));
                return;
            }

            if (!block.HasFlag(InstructionFlags.EndPointUnreachable))
            {
                context.Step("Duplicate block exit", fallthroughExit);
                block.Instructions.Add(fallthroughExit.Clone());
            }
        }
        /// <summary>
        /// Reduce Nesting in switch statements by replacing break; in cases with the block exit, and extracting the default case
        /// Does not affect IL order
        /// </summary>
        private bool ReduceSwitchNesting(Block parentBlock, BlockContainer switchContainer, ILInstruction exitInst)
        {
            // break; from outer container cannot be brought inside the switch as the meaning would change
            if (exitInst is Leave leave && !leave.IsLeavingFunction)
            {
                return(false);
            }

            // find the default section, and ensure it has only one incoming edge
            var switchInst     = (SwitchInstruction)switchContainer.EntryPoint.Instructions.Single();
            var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count());

            if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            if (defaultBlock.Parent != switchContainer)
            {
                return(false);
            }

            // tally stats for heuristic from each case block
            int maxStatements = 0, maxDepth = 0;

            foreach (var section in switchInst.Sections)
            {
                if (section != defaultSection && section.Body.MatchBranch(out var caseBlock) && caseBlock.Parent == switchContainer)
                {
                    UpdateStats(caseBlock, ref maxStatements, ref maxDepth);
                }
            }

            if (!ShouldReduceNesting(defaultBlock, maxStatements, maxDepth))
            {
                return(false);
            }

            Debug.Assert(defaultBlock.HasFlag(InstructionFlags.EndPointUnreachable));

            // ensure the default case dominator tree has no exits (branches to other cases)
            var cfg         = new ControlFlowGraph(switchContainer, context.CancellationToken);
            var defaultNode = cfg.GetNode(defaultBlock);
            var defaultTree = TreeTraversal.PreOrder(defaultNode, n => n.DominatorTreeChildren).ToList();

            if (defaultTree.SelectMany(n => n.Successors).Any(n => !defaultNode.Dominates(n)))
            {
                return(false);
            }

            if (defaultTree.Count > 1 && !(parentBlock.Parent is BlockContainer))
            {
                return(false);
            }

            context.Step("Extract default case of switch", switchContainer);

            // replace all break; statements with the exitInst
            var leaveInstructions = switchContainer.Descendants.Where(inst => inst.MatchLeave(switchContainer));

            foreach (var leaveInst in leaveInstructions.ToArray())
            {
                leaveInst.ReplaceWith(exitInst.Clone());
            }

            // replace the default section branch with a break;
            defaultSection.Body.ReplaceWith(new Leave(switchContainer));

            // remove all default blocks from the switch container
            var defaultBlocks = defaultTree.Select(c => (Block)c.UserData).ToList();

            foreach (var block in defaultBlocks)
            {
                switchContainer.Blocks.Remove(block);
            }

            // replace the parent block exit with the default case instructions
            if (parentBlock.Instructions.Last() == exitInst)
            {
                parentBlock.Instructions.RemoveLast();
            }
            // Note: even though we don't check that the switchContainer is near the end of the block,
            // we know this must be the case because we know "exitInst" is a leave/branch and directly
            // follows the switchContainer.
            Debug.Assert(parentBlock.Instructions.Last() == switchContainer);
            parentBlock.Instructions.AddRange(defaultBlock.Instructions);

            // add any additional blocks from the default case to the parent container
            Debug.Assert(defaultBlocks[0] == defaultBlock);
            if (defaultBlocks.Count > 1)
            {
                var parentContainer = (BlockContainer)parentBlock.Parent;
                int insertAt        = parentContainer.Blocks.IndexOf(parentBlock) + 1;
                foreach (var block in defaultBlocks.Skip(1))
                {
                    parentContainer.Blocks.Insert(insertAt++, block);
                }
            }

            return(true);
        }
Beispiel #7
0
 public override ILInstruction Clone()
 {
     return(new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes,
                             this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
                             ).WithILRange(this));
 }
Beispiel #8
0
 public override ILInstruction Clone()
 {
     return(new TryFault(TryBlock.Clone(), faultBlock.Clone()).WithILRange(this));
 }
Beispiel #9
0
 public override ILInstruction Clone()
 {
     return(new TryFinally(TryBlock.Clone(), finallyBlock.Clone()).WithILRange(this));
 }
Beispiel #10
0
 public override ILInstruction Clone()
 {
     return(new CallIndirect(IsInstance, HasExplicitThis, FunctionPointerType,
                             functionPointer.Clone(), this.Arguments.Select(inst => inst.Clone())
                             ).WithILRange(this));
 }
Beispiel #11
0
        /// <summary>
        /// Reduce Nesting in if/else statements by duplicating an exit instruction.
        /// Does not affect IL order
        /// </summary>
        private bool ReduceNesting(Block block, IfInstruction ifInst, ILInstruction exitInst)
        {
            // start tallying stats for heuristics from then and else-if blocks
            int maxStatements = 0, maxDepth = 0;

            UpdateStats(ifInst.TrueInst, ref maxStatements, ref maxDepth);

            // if (cond) { ... } exit;
            if (ifInst.FalseInst.MatchNop())
            {
                // a separate heuristic to ShouldReduceNesting as there is visual balancing to be performed based on number of statments
                if (maxDepth < 2)
                {
                    return(false);
                }

                //   ->
                // if (!cond) exit;
                // ...; exit;
                EnsureEndPointUnreachable(block, exitInst);
                Debug.Assert(ifInst == block.Instructions.SecondToLastOrDefault());

                // use the same exit the block has. If the block already has one (such as a leave from a try), keep it in place
                EnsureEndPointUnreachable(ifInst.TrueInst, block.Instructions.Last());
                ConditionDetection.InvertIf(block, ifInst, context);

                // ensure the exit inst of the if instruction is a keyword
                Debug.Assert(!(ifInst.TrueInst is Block));
                if (!ifInst.TrueInst.Match(exitInst).Success)
                {
                    Debug.Assert(ifInst.TrueInst is Leave);
                    context.Step("Replace leave with keyword exit", ifInst.TrueInst);
                    ifInst.TrueInst.ReplaceWith(exitInst.Clone());
                }
                return(true);
            }

            // else-if trees are considered as a single group from the root IfInstruction
            if (GetElseIfParent(ifInst) != null)
            {
                return(false);
            }

            // find the else block and tally stats for each else-if block
            while (Block.Unwrap(ifInst.FalseInst) is IfInstruction elseIfInst)
            {
                UpdateStats(elseIfInst.TrueInst, ref maxStatements, ref maxDepth);
                ifInst = elseIfInst;
            }

            if (!ShouldReduceNesting(ifInst.FalseInst, maxStatements, maxDepth))
            {
                return(false);
            }

            // extract the else block and insert exit points all the way up the else-if tree
            do
            {
                var elseIfInst = GetElseIfParent(ifInst);

                // if (cond) { ... } else { ... } exit;
                //   ->
                // if (cond) { ...; exit; }
                // ...; exit;
                EnsureEndPointUnreachable(ifInst.TrueInst, exitInst);
                if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable));
                    Debug.Assert(ifInst.Parent == block);
                    int removeAfter = ifInst.ChildIndex + 1;
                    if (removeAfter < block.Instructions.Count)
                    {
                        // Remove all instructions that ended up dead
                        // (this should just be exitInst itself)
                        Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst);
                        Debug.Assert(block.Instructions.Last() == exitInst);
                        block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter);
                    }
                }
                ExtractElseBlock(ifInst);
                ifInst = elseIfInst;
            } while (ifInst != null);

            return(true);
        }