Esempio n. 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, ILInstruction loadInst, ILInstruction inlinedExpression, ILVariable v)
        {
            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
            }
            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;
            }
            // decide based on the target 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:
                return(parent == next || (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next));

            default:
                return(false);
            }
        }
Esempio n. 2
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="v">The variable being eliminated by inlining.</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:
            case OpCode.SwitchInstruction:
                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;
            }
            if (inlinedExpression.ResultType == StackType.Ref)
            {
                // VB likes to use ref locals for compound assignment
                // (the C# compiler uses ref stack slots instead).
                // We want to avoid unnecessary ref locals, so we'll always inline them if possible.
                return(true);
            }

            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:
                if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
                {
                    return(true);                            // inline into dynamic compound assignments
                }
                break;

            case OpCode.DynamicCompoundAssign:
                return(true);

            case OpCode.GetPinnableReference:
            case OpCode.LocAllocSpan:
                return(true);                        // inline size-expressions into localloc.span

            case OpCode.Call:
            case OpCode.CallVirt:
                // Aggressive inline into property/indexer getter calls for compound assignment calls
                // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter)
                if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot)
                {
                    return(true);
                }
                if (((CallInstruction)parent).Method is SyntheticRangeIndexAccessor)
                {
                    return(true);
                }
                break;

            case OpCode.CallIndirect when loadInst.SlotInfo == CallIndirect.FunctionPointerSlot:
                return(true);

            case OpCode.LdElema:
                if (((LdElema)parent).WithSystemIndex)
                {
                    return(true);
                }
                break;

            case OpCode.Leave:
            case OpCode.YieldReturn:
                return(true);

            case OpCode.SwitchInstruction:
            //case OpCode.BinaryNumericInstruction when parent.SlotInfo == SwitchInstruction.ValueSlot:
            case OpCode.StringToInt when parent.SlotInfo == SwitchInstruction.ValueSlot:
                return(true);

            case OpCode.MatchInstruction when((MatchInstruction)parent).IsDeconstructTuple:
                return(true);
            }
            // decide based on the top-level target instruction into which we are inlining:
            switch (next.OpCode)
            {
            case OpCode.IfInstruction:
                while (parent.MatchLogicNot(out _))
                {
                    parent = parent.Parent;
                }
                return(parent == next);

            default:
                return(false);
            }
        }
Esempio n. 3
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="v">The variable being eliminated by inlining.</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:
                if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
                {
                    return(true);                            // inline into dynamic compound assignments
                }
                break;

            case OpCode.DynamicCompoundAssign:
                return(true);

            case OpCode.ArrayToPointer:
            case OpCode.LocAllocSpan:
                return(true);                        // inline size-expressions into localloc.span

            case OpCode.Call:
            case OpCode.CallVirt:
                // Aggressive inline into property/indexer getter calls for compound assignment calls
                // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter)
                if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot)
                {
                    return(true);
                }
                break;
            }
            // 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);
            }
        }