void CachedDelegateInitializationWithLocal(ILBlock block, ref int i)
        {
            // if (logicnot(ldloc(v))) {
            //     stloc(v, newobj(Action::.ctor, ldloc(displayClass), ldftn(method)))
            // } else {
            // }
            // ...(..., ldloc(v), ...)

            ILCondition c = block.Body[i] as ILCondition;

            if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null)
            {
                return;
            }
            if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0))
            {
                return;
            }
            if (!c.Condition.Match(ILCode.LogicNot))
            {
                return;
            }
            ILExpression condition = c.Condition.Arguments.Single() as ILExpression;

            if (condition == null || condition.Code != ILCode.Ldloc)
            {
                return;
            }
            ILVariable   v     = (ILVariable)condition.Operand;
            ILExpression stloc = c.TrueBlock.Body[0] as ILExpression;

            if (!(stloc != null && stloc.Code == ILCode.Stloc && (ILVariable)stloc.Operand == v))
            {
                return;
            }
            ILExpression newObj = stloc.Arguments[0];

            if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2))
            {
                return;
            }
            if (newObj.Arguments[0].Code != ILCode.Ldloc)
            {
                return;
            }
            if (newObj.Arguments[1].Code != ILCode.Ldftn)
            {
                return;
            }
            MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule();             // method is defined in current assembly

            if (!AstServices.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod))
            {
                return;
            }

            ILNode followingNode     = block.Body.ElementAtOrDefault(i + 1);
            int    ldlocOperandCount =
                followingNode
                .EnumerateSelfAndChildrenRecursive()
                .OfType <ILExpression>()
                .Count(
                    e =>
                    e.Code == ILCode.Ldloc &&
                    (ILVariable)e.Operand == v);

            if (followingNode != null && ldlocOperandCount == 1)
            {
                ILInlining inlining = new ILInlining(method);
                if (!(inlining.numLdloc.GetOrDefault(v) == 2 && inlining.numStloc.GetOrDefault(v) == 2 && inlining.numLdloca.GetOrDefault(v) == 0))
                {
                    return;
                }

                // Find the store instruction that initializes the local to null:
                foreach (ILBlock storeBlock in method.EnumerateSelfAndChildrenRecursive().OfType <ILBlock>())
                {
                    for (int j = 0; j < storeBlock.Body.Count; j++)
                    {
                        ILVariable   storedVar;
                        ILExpression storedExpr;
                        if (storeBlock.Body[j].Match(ILCode.Stloc, out storedVar, out storedExpr) && storedVar == v && storedExpr.Match(ILCode.Ldnull))
                        {
                            // Remove the instruction
                            storeBlock.Body.RemoveAt(j);
                            if (storeBlock == block && j < i)
                            {
                                i--;
                            }
                            break;
                        }
                    }
                }

                block.Body[i] = stloc;                 // remove the 'if (v==null)'
                inlining      = new ILInlining(method);
                inlining.InlineIfPossible(block.Body, ref i);
            }
        }
        bool IntroducePostIncrementForVariables(List <ILNode> body, ILExpression expr, int pos)
        {
            // Works for variables and static fields/properties

            // expr = ldloc(i)
            // stloc(i, add(expr, ldc.i4(1)))
            // ->
            // expr = postincrement(1, ldloca(i))
            ILVariable   exprVar;
            ILExpression exprInit;

            if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
            {
                return(false);
            }

            //The next expression
            ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;

            if (nextExpr == null)
            {
                return(false);
            }

            ILCode loadInstruction   = exprInit.Code;
            ILCode storeInstruction  = nextExpr.Code;
            bool   recombineVariable = false;

            // We only recognise local variables, static fields, and static getters with no arguments
            switch (loadInstruction)
            {
            case ILCode.Ldloc:
                //Must be a matching store type
                if (storeInstruction != ILCode.Stloc)
                {
                    return(false);
                }
                ILVariable loadVar  = (ILVariable)exprInit.Operand;
                ILVariable storeVar = (ILVariable)nextExpr.Operand;
                if (loadVar != storeVar)
                {
                    if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable)
                    {
                        recombineVariable = true;
                    }
                    else
                    {
                        return(false);
                    }
                }
                break;

            case ILCode.Ldsfld:
                if (storeInstruction != ILCode.Stsfld)
                {
                    return(false);
                }
                if (exprInit.Operand != nextExpr.Operand)
                {
                    return(false);
                }
                break;

            case ILCode.CallGetter:
                // non-static getters would have the 'this' argument
                if (exprInit.Arguments.Count != 0)
                {
                    return(false);
                }
                if (storeInstruction != ILCode.CallSetter)
                {
                    return(false);
                }
                if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))
                {
                    return(false);
                }
                break;

            default:
                return(false);
            }

            ILExpression addExpr = nextExpr.Arguments[0];

            int    incrementAmount;
            ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);

            if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
            {
                return(false);
            }

            if (recombineVariable)
            {
                // Split local variable, unsplit these two instances
                // replace nextExpr.Operand with exprInit.Operand
                ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar);
            }

            switch (loadInstruction)
            {
            case ILCode.Ldloc:
                exprInit.Code = ILCode.Ldloca;
                break;

            case ILCode.Ldsfld:
                exprInit.Code = ILCode.Ldsflda;
                break;

            case ILCode.CallGetter:
                exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
                break;
            }
            expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
            body.RemoveAt(pos + 1);             // TODO ILRanges
            return(true);
        }
        bool HandleStringFixing(ILVariable pinnedVar, List <ILNode> body, ref int pos, ref ILExpression fixedStmtInitializer)
        {
            // fixed (stloc(pinnedVar, ldloc(text))) {
            //   var1 = var2 = conv.i(ldloc(pinnedVar))
            //   if (logicnot(logicnot(var1))) {
            //     var2 = add(var1, call(RuntimeHelpers::get_OffsetToStringData))
            //   }
            //   stloc(ptrVar, var2)
            //   ...

            if (pos >= body.Count)
            {
                return(false);
            }

            ILVariable   var1, var2;
            ILExpression varAssignment, ptrInitialization;

            if (!(body[pos].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization)))
            {
                return(false);
            }
            if (!(var1.IsGenerated && var2.IsGenerated))
            {
                return(false);
            }
            if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U)
            {
                ptrInitialization = ptrInitialization.Arguments[0];
            }
            if (!ptrInitialization.MatchLdloc(pinnedVar))
            {
                return(false);
            }

            ILCondition ifStmt = body[pos + 1] as ILCondition;

            if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0)))
            {
                return(false);
            }
            if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1))
            {
                return(false);
            }
            ILVariable   assignedVar;
            ILExpression assignedExpr;

            if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add))
            {
                return(false);
            }
            MethodReference calledMethod;

            if (!(assignedExpr.Arguments[0].MatchLdloc(var1)))
            {
                return(false);
            }
            if (!(assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod) || assignedExpr.Arguments[1].Match(ILCode.CallGetter, out calledMethod)))
            {
                return(false);
            }
            if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers"))
            {
                return(false);
            }

            ILVariable pointerVar;

            if (body[pos + 2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2))
            {
                pos += 3;
                fixedStmtInitializer.Operand = pointerVar;
                return(true);
            }
            return(false);
        }