Beispiel #1
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// 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>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, options);

            if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument)
            {
                var loadInst = r.LoadInst;
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, r, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                if (r.Type == FindResultType.NamedArgument)
                {
                    NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context);
                    // Now that the argument is evaluated early, we can inline as usual
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    Debug.Assert(((LdLoca)loadInst).Variable == v);
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            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))
                {
                    // 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.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 (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1)
            {
                // Possibly a switch-container, allow inlining into the switch instruction:
                return(NoContinue(FindLoadInNext(container.EntryPoint.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 blockcontainer.
            }
Beispiel #3
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// 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>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst);

            if (r == FindResult.Found)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0)
            {
                return(NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst,
                                                       options, context));
            }
            return(false);
        }
Beispiel #4
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, out ILInstruction loadInst)
        {
            loadInst = null;
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                loadInst = expr;
                return(FindResult.Found);
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                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, out loadInst)));

                // 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, out loadInst));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1)
            {
                // Possibly a switch-container, allow inlining into the switch instruction:
                return(NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst)));
                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the blockcontainer.
            }
Beispiel #5
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
            }
        }