Exemplo n.º 1
0
        internal static bool DoInline(ILVariable v, StLoc originalStore, LdLoc loadInst, InliningOptions options, ILTransformContext context)
        {
            if ((options & InliningOptions.Aggressive) == 0 && originalStore.ILStackWasEmpty)
            {
                return(false);
            }
            context.Step($"Introduce named argument '{v.Name}'", originalStore);
            var call = (CallInstruction)loadInst.Parent;

            if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs)
            {
                // create namedArgBlock:
                namedArgBlock = new Block(BlockKind.CallWithNamedArgs);
                call.ReplaceWith(namedArgBlock);
                namedArgBlock.FinalInstruction = call;
                if (call.IsInstanceCall)
                {
                    IType thisVarType = call.Method.DeclaringType;
                    if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref)
                    {
                        thisVarType = new ByReferenceType(thisVarType);
                    }
                    var function   = call.Ancestors.OfType <ILFunction>().First();
                    var thisArgVar = function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg");
                    namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0]));
                    call.Arguments[0] = new LdLoc(thisArgVar);
                }
            }
            v.Kind = VariableKind.NamedArgument;
            namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, originalStore);
            return(true);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
        /// </summary>
        public static bool InlineOneIfPossible(Block block, int pos, InliningOptions options, ILTransformContext context)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            StLoc stloc = block.Instructions[pos] as StLoc;

            if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal)
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            // ensure the variable is accessed only a single time
            if (v.StoreCount != 1)
            {
                return(false);
            }
            if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
            {
                return(false);
            }
            // TODO: inlining of small integer types might be semantically incorrect,
            // but we can't avoid it this easily without breaking lots of tests.
            //if (v.Type.IsSmallIntegerType())
            //	return false; // stloc might perform implicit truncation
            return(InlineOne(stloc, options, context));
        }
Exemplo n.º 3
0
        public static void Propagate(StLoc store, ILTransformContext context)
        {
            Debug.Assert(store.Variable.IsSingleDefinition);
            Block block = (Block)store.Parent;
            int   i     = store.ChildIndex;

            DoPropagate(store.Variable, store.Value, block, ref i, context);
        }
 static bool IsClosureInit(ILTransformContext context, StLoc inst, out ITypeDefinition closureType)
 {
     if (inst.Value is NewObj newObj) {
         closureType = newObj.Method.DeclaringTypeDefinition;
         return closureType != null && IsPotentialClosure(context, newObj);
     }
     closureType = null;
     return false;
 }
Exemplo n.º 5
0
        protected internal override void VisitStLoc(StLoc inst)
        {
            base.VisitStLoc(inst);

            if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc)
            {
                context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
                inst.ReplaceWith(inst.Value);
            }
        }
Exemplo n.º 6
0
 bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
 {
     if (inst.Value is NewObj newObj)
     {
         closureType = newObj.Method.DeclaringTypeDefinition;
         return(closureType != null && IsPotentialClosure(this.context, newObj));
     }
     closureType = null;
     return(false);
 }
Exemplo n.º 7
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     base.VisitStLoc(inst);
     // Sometimes display class references are copied into other local variables.
     // We remove the assignment and store the relationship between the display class and the variable in the
     // displayClasses dictionary.
     if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass))
     {
         displayClasses[inst.Variable] = displayClass;
         instructionsToRemove.Add(inst);
     }
Exemplo n.º 8
0
 bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, out StLoc obj)
 {
     obj = null;
     if (entryPoint.Instructions.Count == 0 || entryPoint.IncomingEdgeCount != 1)
     {
         return(false);
     }
     if (!MatchCall(entryPoint.Instructions[0] as Call, "Enter", flag, out obj))
     {
         return(false);
     }
     return(true);
 }
Exemplo n.º 9
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     base.VisitStLoc(inst);
     if (inst.Variable == targetLoad.Variable)
     {
         orphanedVariableInits.Add(inst);
     }
     if (MatchesTargetOrCopyLoad(inst.Value))
     {
         targetAndCopies.Add(inst.Variable);
         orphanedVariableInits.Add(inst);
     }
 }
Exemplo n.º 10
0
 /// <summary>
 /// Determines whether storeInst.Variable is only assigned once and used only inside <paramref name="usingContainer"/>.
 /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions.
 /// (This only applies to value types.)
 /// </summary>
 bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer)
 {
     if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
     {
         return(false);
     }
     if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la)))
     {
         return(false);
     }
     if (storeInst.Variable.StoreInstructions.OfType <ILInstruction>().Any(st => st != storeInst))
     {
         return(false);
     }
     return(true);
 }
Exemplo n.º 11
0
        bool MatchCall(Call call, string methodName, ILVariable flag, out StLoc obj)
        {
            obj = null;
            const string ThreadingMonitor = "System.Threading.Monitor";

            if (call == null || call.Method.Name != methodName || call.Method.DeclaringType.FullName != ThreadingMonitor ||
                call.Method.TypeArguments.Count != 0 || call.Arguments.Count != 2)
            {
                return(false);
            }
            if (!call.Arguments[1].MatchLdLoca(flag) || !(call.Arguments[0] is StLoc val))
            {
                return(false);
            }
            obj = val;
            return(true);
        }
Exemplo n.º 12
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     base.VisitStLoc(inst);
     if (targetLoad is ILInstruction instruction && instruction.MatchLdThis())
     {
         return;
     }
     if (inst.Variable == targetLoad.Variable)
     {
         orphanedVariableInits.Add(inst);
     }
     if (MatchesTargetOrCopyLoad(inst.Value))
     {
         targetAndCopies.Add(inst.Variable);
         orphanedVariableInits.Add(inst);
     }
 }
Exemplo n.º 13
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     DisplayClass displayClass;
     if (inst.Parent is Block parentBlock && inst.Variable.IsSingleDefinition) {
         if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0) {
             // traverse pre-order, so that we do not have to deal with more special cases afterwards
             base.VisitStLoc(inst);
             if (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction) {
                 context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
                 inst.ReplaceWith(inst.Value);
             }
             return;
         }
         if (displayClasses.TryGetValue(inst.Variable, out displayClass) && displayClass.Initializer == inst) {
             // inline contents of object initializer block
             if (inst.Value is Block initBlock && initBlock.Kind == BlockKind.ObjectInitializer) {
                 context.Step($"Remove initializer of {inst.Variable.Name}", inst);
                 for (int i = 1; i < initBlock.Instructions.Count; i++) {
                     var stobj = (StObj)initBlock.Instructions[i];
                     var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition];
                     parentBlock.Instructions.Insert(inst.ChildIndex + i, new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj));
                 }
             }
             context.Step($"Remove initializer of {inst.Variable.Name}", inst);
             parentBlock.Instructions.Remove(inst);
             return;
         }
         if (inst.Value is LdLoc || inst.Value is LdObj) {
             // in some cases (e.g. if inlining fails), there can be a reference to a display class in a stack slot,
             // in that case it is necessary to resolve the reference and iff it can be propagated, replace all loads
             // of the single-definition.
             var referencedDisplayClass = ResolveVariableToPropagate(inst.Value);
             if (referencedDisplayClass != null && displayClasses.TryGetValue(referencedDisplayClass, out _)) {
                 context.Step($"Propagate reference to {referencedDisplayClass.Name} in {inst.Variable}", inst);
                 foreach (var ld in inst.Variable.LoadInstructions.ToArray()) {
                     ld.ReplaceWith(new LdLoc(referencedDisplayClass).WithILRange(ld));
                 }
                 parentBlock.Instructions.Remove(inst);
                 return;
             }
         }
     }
     base.VisitStLoc(inst);
 }
Exemplo n.º 14
0
 // This transform is required because ILInlining only works with stloc/ldloc
 internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
 {
     if (inst.Target.MatchLdLoca(out ILVariable v) &&
         TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) &&
         inst.UnalignedPrefix == 0 &&
         !inst.IsVolatile)
     {
         context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
         ILInstruction replacement = new StLoc(v, inst.Value).WithILRange(inst);
         if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown &&
             inst.SlotInfo != Block.InstructionSlot)
         {
             replacement = new Conv(replacement, inst.Type.ToPrimitiveType(),
                                    checkForOverflow: false, Sign.None);
         }
         inst.ReplaceWith(replacement);
         return(true);
     }
     return(false);
 }
Exemplo n.º 15
0
        protected internal override void VisitStLoc(StLoc inst)
        {
            base.VisitStLoc(inst);

            if (inst.Parent is Block && inst.Variable.IsSingleDefinition)
            {
                if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc)
                {
                    context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
                    inst.ReplaceWith(inst.Value);
                    return;
                }
                if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass))
                {
                    context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst);
                    displayClasses.Add(inst.Variable, displayClass);
                    instructionsToRemove.Add(inst);
                    return;
                }
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
        /// </summary>
        public static bool InlineOneIfPossible(Block block, int pos, bool aggressive, ILTransformContext context)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            StLoc stloc = block.Instructions[pos] as StLoc;

            if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal)
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            // ensure the variable is accessed only a single time
            if (v.StoreCount != 1)
            {
                return(false);
            }
            if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
            {
                return(false);
            }
            return(InlineOne(stloc, aggressive, context));
        }
Exemplo n.º 17
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        public static bool InlineOne(StLoc stloc, InliningOptions options, ILTransformContext context)
        {
            ILVariable v     = stloc.Variable;
            Block      block = (Block)stloc.Parent;
            int        pos   = stloc.ChildIndex;

            if (DoInline(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos + 1), options, context))
            {
                // Assign the ranges of the stloc instruction:
                stloc.Value.AddILRange(stloc);
                // Remove the stloc instruction:
                Debug.Assert(block.Instructions[pos] == stloc);
                block.Instructions.RemoveAt(pos);
                return(true);
            }
            else if (v.LoadCount == 0 && v.AddressCount == 0)
            {
                // The variable is never loaded
                if (SemanticHelper.IsPure(stloc.Value.Flags))
                {
                    // Remove completely if the instruction has no effects
                    // (except for reading locals)
                    context.Step("Remove dead store without side effects", stloc);
                    block.Instructions.RemoveAt(pos);
                    return(true);
                }
                else if (v.Kind == VariableKind.StackSlot)
                {
                    context.Step("Remove dead store, but keep expression", stloc);
                    // Assign the ranges of the stloc instruction:
                    stloc.Value.AddILRange(stloc);
                    // Remove the stloc, but keep the inner expression
                    stloc.ReplaceWith(stloc.Value);
                    return(true);
                }
            }
            return(false);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Block finallyHead (incoming: 2) {
        ///		[body of finally]
        ///     stloc V_4(ldloc V_1)
        ///     if (comp(ldloc V_4 == ldnull)) br afterFinally
        ///     br typeCheckBlock
        /// }
        ///
        /// Block typeCheckBlock (incoming: 1) {
        ///     stloc S_110(isinst System.Exception(ldloc V_4))
        ///     if (comp(ldloc S_110 != ldnull)) br captureBlock
        ///     br throwBlock
        /// }
        ///
        /// Block throwBlock (incoming: 1) {
        ///     throw(ldloc V_4)
        /// }
        ///
        /// Block captureBlock (incoming: 1) {
        ///     callvirt Throw(call Capture(ldloc S_110))
        ///     br afterFinally
        /// }
        ///
        /// Block afterFinally (incoming: 2) {
        ///     stloc V_1(ldnull)
        ///     [after finally]
        /// }
        /// </summary>
        static bool MatchExceptionCaptureBlock(StLoc tempStore, out Block endOfFinally, out Block afterFinally, out List <Block> blocksToRemove)
        {
            afterFinally   = null;
            endOfFinally   = (Block)tempStore.Parent;
            blocksToRemove = new List <Block>();
            int count = endOfFinally.Instructions.Count;

            if (tempStore.ChildIndex != count - 3)
            {
                return(false);
            }
            if (!(endOfFinally.Instructions[count - 2] is IfInstruction ifInst))
            {
                return(false);
            }
            if (!endOfFinally.Instructions.Last().MatchBranch(out var typeCheckBlock))
            {
                return(false);
            }
            if (!ifInst.TrueInst.MatchBranch(out afterFinally))
            {
                return(false);
            }
            // match typeCheckBlock
            if (typeCheckBlock.Instructions.Count != 3)
            {
                return(false);
            }
            if (!typeCheckBlock.Instructions[0].MatchStLoc(out var castStore, out var cast) ||
                !cast.MatchIsInst(out var arg, out var type) || !type.IsKnownType(KnownTypeCode.Exception) || !arg.MatchLdLoc(tempStore.Variable))
            {
                return(false);
            }
            if (!typeCheckBlock.Instructions[1].MatchIfInstruction(out var cond, out var jumpToCaptureBlock))
            {
                return(false);
            }
            if (!cond.MatchCompNotEqualsNull(out arg) || !arg.MatchLdLoc(castStore))
            {
                return(false);
            }
            if (!typeCheckBlock.Instructions[2].MatchBranch(out var throwBlock))
            {
                return(false);
            }
            if (!jumpToCaptureBlock.MatchBranch(out var captureBlock))
            {
                return(false);
            }
            // match throwBlock
            if (throwBlock.Instructions.Count != 1 || !throwBlock.Instructions[0].MatchThrow(out arg) || !arg.MatchLdLoc(tempStore.Variable))
            {
                return(false);
            }
            // match captureBlock
            if (captureBlock.Instructions.Count != 2)
            {
                return(false);
            }
            if (!captureBlock.Instructions[1].MatchBranch(afterFinally))
            {
                return(false);
            }
            if (!(captureBlock.Instructions[0] is CallVirt callVirt) || callVirt.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw" || callVirt.Arguments.Count != 1)
            {
                return(false);
            }
            if (!(callVirt.Arguments[0] is Call call) || call.Method.FullName != "System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture" || call.Arguments.Count != 1)
            {
                return(false);
            }
            if (!call.Arguments[0].MatchLdLoc(castStore))
            {
                return(false);
            }
            blocksToRemove.Add(typeCheckBlock);
            blocksToRemove.Add(throwBlock);
            blocksToRemove.Add(captureBlock);
            return(true);
        }
 /// <summary>
 /// if (logic.not(ldloc V_1)) Block IL_004a {
 ///     stloc V_2(dynamic.getmember B(target))
 /// }
 /// </summary>
 static bool MatchLhsCacheIfInstruction(ILInstruction ifInst, ILVariable flagVar, out StLoc cacheStore)
 {
     cacheStore = null;
     if (!ifInst.MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     if (!MatchFlagEqualsZero(condition, flagVar))
     {
         return(false);
     }
     cacheStore = Block.Unwrap(trueInst) as StLoc;
     return(cacheStore != null);
 }
Exemplo n.º 20
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     base.VisitStLoc(inst);
     TransformAssignment.HandleCompoundAssign(inst, context);
 }
Exemplo n.º 21
0
 protected internal override void VisitStLoc(StLoc inst)
 {
     inst.Value.AcceptVisitor(this);
     HandleStore(inst.Variable);
 }
Exemplo n.º 22
0
        bool CreatePinnedRegion(Block block)
        {
            // After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block
            // can be a write to a pinned local.
            var stLoc = block.Instructions.SecondToLastOrDefault() as StLoc;

            if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal)
            {
                return(false);
            }
            // stLoc is a store to a pinned local.
            if (IsNullOrZero(stLoc.Value))
            {
                return(false);                // ignore unpin instructions
            }
            // stLoc is a store that starts a new pinned region

            // Collect the blocks to be moved into the region:
            BlockContainer sourceContainer = (BlockContainer)block.Parent;

            int[]         reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count];
            Queue <Block> workList             = new Queue <Block>();
            Block         entryBlock           = ((Branch)block.Instructions.Last()).TargetBlock;

            if (entryBlock.Parent != sourceContainer)
            {
                // we didn't find a single block to be added to the pinned region
                return(false);
            }
            reachedEdgesPerBlock[entryBlock.ChildIndex]++;
            workList.Enqueue(entryBlock);
            while (workList.Count > 0)
            {
                Block workItem  = workList.Dequeue();
                StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc;
                int   instructionCount;
                if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value))
                {
                    // found unpin instruction: only consider branches prior to that instruction
                    instructionCount = workStLoc.ChildIndex;
                }
                else
                {
                    instructionCount = workItem.Instructions.Count;
                }
                for (int i = 0; i < instructionCount; i++)
                {
                    foreach (var branch in workItem.Instructions[i].Descendants.OfType <Branch>())
                    {
                        if (branch.TargetBlock.Parent == sourceContainer)
                        {
                            if (branch.TargetBlock == block)
                            {
                                // pin instruction is within a loop, and can loop around without an unpin instruction
                                // This should never happen for C#-compiled code, but may happen with C++/CLI code.
                                return(false);
                            }
                            if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0)
                            {
                                // detected first edge to that block: add block as work item
                                workList.Enqueue(branch.TargetBlock);
                            }
                        }
                    }
                }
            }

            // Validate that all uses of a block consistently are inside or outside the pinned region.
            // (we cannot do this anymore after we start moving blocks around)
            for (int i = 0; i < sourceContainer.Blocks.Count; i++)
            {
                if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount)
                {
                    return(false);
                }
            }

            context.Step("CreatePinnedRegion", block);
            BlockContainer body = new BlockContainer();

            for (int i = 0; i < sourceContainer.Blocks.Count; i++)
            {
                if (reachedEdgesPerBlock[i] > 0)
                {
                    var    innerBlock = sourceContainer.Blocks[i];
                    Branch br         = innerBlock.Instructions.LastOrDefault() as Branch;
                    if (br != null && br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0)
                    {
                        // branch that leaves body.
                        // Should have an instruction that resets the pin; delete that instruction:
                        StLoc innerStLoc = innerBlock.Instructions.SecondToLastOrDefault() as StLoc;
                        if (innerStLoc != null && innerStLoc.Variable == stLoc.Variable && IsNullOrZero(innerStLoc.Value))
                        {
                            innerBlock.Instructions.RemoveAt(innerBlock.Instructions.Count - 2);
                        }
                    }

                    body.Blocks.Add(innerBlock);                     // move block into body
                    sourceContainer.Blocks[i] = new Block();         // replace with dummy block
                    // we'll delete the dummy block later
                }
            }

            stLoc.ReplaceWith(new PinnedRegion(stLoc.Variable, stLoc.Value, body));
            block.Instructions.RemoveAt(block.Instructions.Count - 1);             // remove branch into body
            ProcessPinnedRegion((PinnedRegion)block.Instructions.Last());
            return(true);
        }
Exemplo n.º 23
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.Dynamic)
            {
                return;
            }

            this.context = context;

            Dictionary <IField, CallSiteInfo> callsites          = new Dictionary <IField, CallSiteInfo>();
            HashSet <BlockContainer>          modifiedContainers = new HashSet <BlockContainer>();

            foreach (var block in function.Descendants.OfType <Block>())
            {
                if (block.Instructions.Count < 2)
                {
                    continue;
                }
                // Check if, we deal with a callsite cache field null check:
                // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c
                // br IL_002b
                if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst))
                {
                    continue;
                }
                if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit))
                {
                    continue;
                }
                if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches))
                {
                    continue;
                }
                if (!ifInst.TrueInst.MatchBranch(out var trueBlock))
                {
                    continue;
                }
                Block callSiteInitBlock, targetBlockAfterInit;
                if (invertBranches)
                {
                    callSiteInitBlock    = branchAfterInit.TargetBlock;
                    targetBlockAfterInit = trueBlock;
                }
                else
                {
                    callSiteInitBlock    = trueBlock;
                    targetBlockAfterInit = branchAfterInit.TargetBlock;
                }
                if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit))
                {
                    continue;
                }
                if (targetBlockAfterInit != blockAfterInit)
                {
                    continue;
                }
                callSiteInfo.DelegateType          = callSiteDelegate;
                callSiteInfo.ConditionalJumpToInit = ifInst;
                callSiteInfo.Inverted        = invertBranches;
                callSiteInfo.BranchAfterInit = branchAfterInit;
                callsites.Add(callSiteCacheField, callSiteInfo);
            }

            var storesToRemove = new List <StLoc>();

            foreach (var invokeCall in function.Descendants.OfType <CallVirt>())
            {
                if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0)
                {
                    continue;
                }
                var firstArgument = invokeCall.Arguments[0];
                if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition)
                {
                    firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value;
                }
                if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField))
                {
                    continue;
                }
                if (!cacheFieldLoad.MatchLdsFld(out var cacheField))
                {
                    continue;
                }
                if (!callsites.TryGetValue(cacheField, out var callsite))
                {
                    continue;
                }
                context.Stepper.Step("Transform callsite for " + callsite.MemberName);
                var           deadArguments = new List <ILInstruction>();
                ILInstruction replacement   = MakeDynamicInstruction(callsite, invokeCall, deadArguments);
                if (replacement == null)
                {
                    continue;
                }
                invokeCall.ReplaceWith(replacement);
                Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block);
                var block = ((Block)callsite.ConditionalJumpToInit.Parent);
                if (callsite.Inverted)
                {
                    block.Instructions.Remove(callsite.ConditionalJumpToInit);
                    callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst);
                }
                else
                {
                    block.Instructions.Remove(callsite.ConditionalJumpToInit);
                }
                foreach (var arg in deadArguments)
                {
                    if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0)
                    {
                        StLoc stLoc = (StLoc)temporary.StoreInstructions[0];
                        if (stLoc.Parent is Block storeParentBlock)
                        {
                            var value = stLoc.Value;
                            if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField))
                            {
                                storesToRemove.Add(stLoc);
                            }
                            if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy))
                            {
                                storesToRemove.Add(stLoc);
                            }
                        }
                    }
                }
                modifiedContainers.Add((BlockContainer)block.Parent);
            }

            foreach (var inst in storesToRemove)
            {
                Block parentBlock = (Block)inst.Parent;
                parentBlock.Instructions.RemoveAt(inst.ChildIndex);
            }

            foreach (var container in modifiedContainers)
            {
                container.SortBlocks(deleteUnreachableBlocks: true);
            }
        }
Exemplo n.º 24
0
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            // Check if the using resource matches the GetEnumerator pattern.
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            // The using body must be a BlockContainer.
            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            // The using-variable is the enumerator.
            var enumeratorVar = inst.Variable;
            // If there's another BlockContainer nested in this container and it only has one child block, unwrap it.
            // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            // Detect whether we're dealing with a while loop with multiple embedded statements.
            var loop = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            // The loop condition must be a call to enumerator.MoveNext()
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            // Check enumerator variable references.
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar)
            {
                return(null);
            }
            // Detect which foreach-variable transformation is necessary/possible.
            var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(),
                                                                out var singleGetter, out var foreachVariable);

            if (transformation == RequiredGetCurrentTransformation.NoForeach)
            {
                return(null);
            }
            // The existing foreach variable, if found, can only be used in the loop container.
            if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            // Extract in-expression
            var collectionExpr = m.Get <Expression>("collection").Single();

            // Special case: foreach (var item in this) is decompiled as foreach (var item in base)
            // but a base reference is not valid in this context.
            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            // Handle explicit casts:
            // This is the case if an explicit type different from the collection-item-type was used.
            // For example: foreach (ClassA item in nonGenericEnumerable)
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            // Handle the required foreach-variable transformation:
            switch (transformation)
            {
            case RequiredGetCurrentTransformation.UseExistingVariable:
                foreachVariable.Type = type;
                foreachVariable.Kind = VariableKind.ForeachLocal;
                foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), foreachVariable);
                break;

            case RequiredGetCurrentTransformation.UninlineAndUseExistingVariable:
                // Unwrap stloc chain.
                var nestedStores = new Stack <ILVariable>();
                var currentInst  = instToReplace;                        // instToReplace is the innermost value of the stloc chain.
                while (currentInst.Parent is StLoc stloc)
                {
                    // Exclude nested stores to foreachVariable
                    // we'll insert one store at the beginning of the block.
                    if (stloc.Variable != foreachVariable && stloc.Parent is StLoc)
                    {
                        nestedStores.Push(stloc.Variable);
                    }
                    currentInst = stloc;
                }
                // Rebuild the nested store instructions:
                ILInstruction reorderedStores = new LdLoc(foreachVariable);
                while (nestedStores.Count > 0)
                {
                    reorderedStores = new StLoc(nestedStores.Pop(), reorderedStores);
                }
                currentInst.ReplaceWith(reorderedStores);
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                // Adjust variable type, kind and name.
                goto case RequiredGetCurrentTransformation.UseExistingVariable;

            case RequiredGetCurrentTransformation.IntroduceNewVariable:
                foreachVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(foreachVariable));
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                break;
            }
            // Convert the modified body to C# AST:
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
            foreachBody.Statements.First().Detach();
            // Construct the foreach loop.
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && foreachVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type),
                VariableName      = foreachVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
            // If there was an optional return statement, return it as well.
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }