Beispiel #1
0
        /// <summary>
        /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
        /// </summary>
        /// <param name="loadInst">The load instruction (a descendant within 'next')</param>
        /// <param name="v">The variable being inlined.</param>
        static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options)
        {
            Debug.Assert(loadInst.Variable == v);
            // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
            // that the method is operating on a copy.
            // Thus, we have to ensure we're operating on an r-value.
            // Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
            // of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
            if (IsUsedAsThisPointerInCall(loadInst, out var method))
            {
                if (options.HasFlag(InliningOptions.Aggressive))
                {
                    // Inlining might be required in ctor initializers (see #2714).
                    // expressionBuilder.VisitAddressOf will handle creating the copy for us.
                    return(true);
                }

                switch (ClassifyExpression(inlinedExpression))
                {
                case ExpressionClassification.RValue:
                    // For struct method calls on rvalues, the C# compiler always generates temporaries.
                    return(true);

                case ExpressionClassification.MutableLValue:
                    // For struct method calls on mutable lvalues, the C# compiler never generates temporaries.
                    return(false);

                case ExpressionClassification.ReadonlyLValue:
                    // For struct method calls on readonly lvalues, the C# compiler
                    // only generates a temporary if it isn't a "readonly struct"
                    return(MethodRequiresCopyForReadonlyLValue(method));

                default:
                    throw new InvalidOperationException("invalid expression classification");
                }
            }
            else if (IsUsedAsThisPointerInFieldRead(loadInst))
            {
                // mcs generated temporaries for field reads on rvalues (#1555)
                return(ClassifyExpression(inlinedExpression) == ExpressionClassification.RValue);
            }
            else
            {
                return(false);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options)
        {
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                if (expr.SlotInfo == StObj.TargetSlot && !((StObj)expr.Parent).CanInlineIntoTargetSlot(expressionBeingMoved))
                {
                    if ((options & InliningOptions.AllowChangingOrderOfEvaluationForExceptions) != 0)
                    {
                        // Intentionally change code semantics so that we can avoid a ref local
                        if (expressionBeingMoved is LdFlda ldflda)
                        {
                            ldflda.DelayExceptions = true;
                        }
                        else if (expressionBeingMoved is LdElema ldelema)
                        {
                            ldelema.DelayExceptions = true;
                        }
                    }
                    else
                    {
                        // special case: the StObj.TargetSlot does not accept some kinds of expressions
                        return(FindResult.Stop);
                    }
                }
                return(FindResult.Found(expr));
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ControlFlow when block.Parent is BlockContainer:
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (options.HasFlag(InliningOptions.FindDeconstruction) && expr is DeconstructInstruction di)
            {
                return(FindResult.Deconstruction(di));
            }
            foreach (var child in expr.Children)
            {
                if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved))
                {
                    return(FindResult.Stop);
                }

                // Recursively try to find the load instruction
                FindResult r = FindLoadInNext(child, v, expressionBeingMoved, options);
                if (r.Type != FindResultType.Continue)
                {
                    if (r.Type == FindResultType.Stop && (options & InliningOptions.IntroduceNamedArguments) != 0 && expr is CallInstruction call)
                    {
                        return(NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, expressionBeingMoved));
                    }
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(FindResult.Continue);                // continue searching
            }
            else
            {
                return(FindResult.Stop);                // abort, inlining not possible
            }
        }