Пример #1
0
        /// <summary>
        /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
        /// </summary>
        /// <param name="next">The next top-level expression</param>
        /// <param name="loadInst">The load within 'next'</param>
        /// <param name="inlinedExpression">The expression being inlined</param>
        static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v)
        {
            if (findResult.Type == FindResultType.NamedArgument)
            {
                var originalStore = (StLoc)inlinedExpression.Parent;
                return(!originalStore.ILStackWasEmpty);
            }
            Debug.Assert(findResult.Type == FindResultType.Found);

            var loadInst = findResult.LoadInst;

            Debug.Assert(loadInst.IsDescendantOf(next));

            // decide based on the source expression being inlined
            switch (inlinedExpression.OpCode)
            {
            case OpCode.DefaultValue:
            case OpCode.StObj:
            case OpCode.NumericCompoundAssign:
            case OpCode.UserDefinedCompoundAssign:
            case OpCode.Await:
                return(true);

            case OpCode.LdLoc:
                if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null)
                {
                    // Roslyn likes to put the result of fetching a state machine field into a temporary variable,
                    // so inline more aggressively in such cases.
                    return(true);
                }
                break;
            }

            var parent = loadInst.Parent;

            if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _))
            {
                // inline into nullable ctor call in lifted operator
                parent = parent.Parent;
            }
            if (parent is ILiftableInstruction liftable && liftable.IsLifted)
            {
                return(true);                // inline into lifted operators
            }
            // decide based on the new parent into which we are inlining:
            switch (parent.OpCode)
            {
            case OpCode.NullCoalescingInstruction:
                if (NullableType.IsNullable(v.Type))
                {
                    return(true);                            // inline nullables into ?? operator
                }
                break;

            case OpCode.NullableUnwrap:
                return(true);                        // inline into ?. operator

            case OpCode.UserDefinedLogicOperator:
            case OpCode.DynamicLogicOperatorInstruction:
                return(true);                        // inline into (left slot of) user-defined && or || operator

            case OpCode.DynamicGetMemberInstruction:
            case OpCode.DynamicGetIndexInstruction:
            case OpCode.LdObj:
                if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
                {
                    return(true);                            // inline into dynamic compound assignments
                }
                break;

            case OpCode.ArrayToPointer:
            case OpCode.LocAllocSpan:
                return(true);                        // inline size-expressions into localloc.span
            }
            // decide based on the top-level target instruction into which we are inlining:
            switch (next.OpCode)
            {
            case OpCode.Leave:
            case OpCode.YieldReturn:
                return(parent == next);

            case OpCode.IfInstruction:
                while (parent.MatchLogicNot(out _))
                {
                    parent = parent.Parent;
                }
                return(parent == next);

            case OpCode.BlockContainer:
                if (((BlockContainer)next).EntryPoint.Instructions[0] is SwitchInstruction switchInst)
                {
                    next = switchInst;
                    goto case OpCode.SwitchInstruction;
                }
                else
                {
                    return(false);
                }

            case OpCode.SwitchInstruction:
                if (parent == next)
                {
                    return(true);
                }
                if (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next)
                {
                    return(true);
                }
                if (parent is StringToInt stringToInt && stringToInt.Parent == next)
                {
                    return(true);
                }
                return(false);

            default:
                return(false);
            }
        }
        /// <summary>
        /// Matches Roslyn C# switch on nullable.
        /// </summary>
        bool MatchRoslynSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
        {
            newSwitch = null;
            // match first block:
            // if (logic.not(call get_HasValue(target))) br nullCaseBlock
            // br switchBlock
            if (!instructions[i].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
            {
                return(false);
            }
            if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction target) || !SemanticHelper.IsPure(target.Flags))
            {
                return(false);
            }
            // match second block: switchBlock
            // note: I have seen cases where switchVar is inlined into the switch.
            // stloc switchVar(call GetValueOrDefault(ldloca tmp))
            // switch (ldloc switchVar) {
            //  case [0..1): br caseBlock1
            // ... more cases ...
            //  case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
            // }
            if (switchBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            SwitchInstruction switchInst;

            switch (switchBlock.Instructions.Count)
            {
            case 2: {
                // this is the normal case described by the pattern above
                if (!switchBlock.Instructions[0].MatchStLoc(out var switchVar, out var getValueOrDefault))
                {
                    return(false);
                }
                if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1)
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                if (!(switchBlock.Instructions[1] is SwitchInstruction si))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            case 1: {
                // this is the special case where `call GetValueOrDefault(ldloca tmp)` is inlined into the switch.
                if (!(switchBlock.Instructions[0] is SwitchInstruction si))
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(si.Value, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            default: {
                return(false);
            }
            }
            ILInstruction switchValue;

            if (target.MatchLdLoca(out var v))
            {
                switchValue = new LdLoc(v).WithILRange(target);
            }
            else
            {
                switchValue = new LdObj(target, ((CallInstruction)getHasValue).Method.DeclaringType);
            }
            newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue);
            return(true);
        }
 /// <summary>
 /// Matches legacy C# switch on nullable.
 /// </summary>
 bool MatchSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
 {
     newSwitch = null;
     // match first block:
     // stloc tmp(ldloca switchValueVar)
     // stloc switchVariable(call GetValueOrDefault(ldloc tmp))
     // if (logic.not(call get_HasValue(ldloc tmp))) br nullCaseBlock
     // br switchBlock
     if (i < 2)
     {
         return(false);
     }
     if (!instructions[i - 2].MatchStLoc(out var tmp, out var ldloca) ||
         !instructions[i - 1].MatchStLoc(out var switchVariable, out var getValueOrDefault) ||
         !instructions[i].MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     if (!tmp.IsSingleDefinition || tmp.LoadCount != 2)
     {
         return(false);
     }
     if (!switchVariable.IsSingleDefinition || switchVariable.LoadCount != 1)
     {
         return(false);
     }
     if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
     {
         return(false);
     }
     if (!ldloca.MatchLdLoca(out var switchValueVar))
     {
         return(false);
     }
     if (!condition.MatchLogicNot(out var getHasValue))
     {
         return(false);
     }
     if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction getValueOrDefaultArg))
     {
         return(false);
     }
     if (!NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction getHasValueArg))
     {
         return(false);
     }
     if (!(getHasValueArg.MatchLdLoc(tmp) && getValueOrDefaultArg.MatchLdLoc(tmp)))
     {
         return(false);
     }
     // match second block: switchBlock
     // switch (ldloc switchVariable) {
     //  case [0..1): br caseBlock1
     //  ... more cases ...
     //  case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
     // }
     if (switchBlock.Instructions.Count != 1 || switchBlock.IncomingEdgeCount != 1)
     {
         return(false);
     }
     if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst))
     {
         return(false);
     }
     newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, new LdLoc(switchValueVar));
     return(true);
 }
Пример #4
0
        bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck)
        {
            numObjVarLoadsInCheck = 2;
            CallVirt callVirt;

            if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT))
            {
                if (checkInst.MatchIfInstruction(out var condition, out var disposeInst))
                {
                    if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar))
                    {
                        return(false);
                    }
                    if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
                    {
                        return(false);
                    }
                    callVirt = disposeBlock.Instructions[0] as CallVirt;
                }
                else if (checkInst.MatchNullableRewrap(out disposeInst))
                {
                    callVirt = disposeInst as CallVirt;
                }
                else
                {
                    return(false);
                }
                if (callVirt == null)
                {
                    return(false);
                }
                if (callVirt.Method.FullName != "System.IDisposable.Dispose")
                {
                    return(false);
                }
                if (callVirt.Method.Parameters.Count > 0)
                {
                    return(false);
                }
                if (callVirt.Arguments.Count != 1)
                {
                    return(false);
                }
                var firstArg = callVirt.Arguments.FirstOrDefault();
                if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable)))
                {
                    if (!firstArg.MatchAddressOf(out var innerArg2))
                    {
                        return(false);
                    }
                    return(NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) ||
                           (innerArg2 is NullableUnwrap unwrap &&
                            unwrap.Argument.MatchLdLoc(objVar)));
                }
                else
                {
                    if (!(innerArg1.MatchBox(out firstArg, out var boxType) && boxType.IsKnownType(KnownTypeCode.NullableOfT) &&
                          NullableType.GetUnderlyingType(boxType).Equals(NullableType.GetUnderlyingType(objVar.Type))))
                    {
                        return(false);
                    }
                    return(firstArg.MatchLdLoc(objVar));
                }
            }