Ejemplo n.º 1
0
        internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
        {
            if (ldloca.ChildIndex != 0)
            {
                return(false);
            }
            if (ldloca.Variable.Type.IsReferenceType ?? false)
            {
                return(false);
            }
            switch (ldloca.Parent.OpCode)
            {
            case OpCode.Call:
            case OpCode.CallVirt:
                var method = ((CallInstruction)ldloca.Parent).Method;
                if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter)
                {
                    // C# doesn't allow calling setters on temporary structs
                    return(false);
                }
                return(!method.IsStatic);

            case OpCode.Await:
                return(true);

            case OpCode.NullableUnwrap:
                return(((NullableUnwrap)ldloca.Parent).RefInput);

            default:
                return(false);
            }
        }
Ejemplo n.º 2
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)
        {
            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))
            {
                return(false);
            }
            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(!(v.Type.GetDefinition()?.IsReadOnly == true));

            default:
                throw new InvalidOperationException("invalid expression classification");
            }
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
 /// </summary>
 /// <param name="next">The next top-level expression</param>
 /// <param name="loadInst">The load instruction (a descendant within 'next')</param>
 /// <param name="v">The variable being inlined.</param>
 static bool IsGeneratedValueTypeTemporary(ILInstruction next, LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression)
 {
     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).
     return(IsUsedAsThisPointerInCall(loadInst) && !IsLValue(inlinedExpression));
 }
Ejemplo n.º 4
0
        static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
        {
            method = null;
            if (ldloca.Variable.Type.IsReferenceType ?? false)
            {
                return(false);
            }
            ILInstruction inst = ldloca;

            while (inst.Parent is LdFlda ldflda)
            {
                inst = ldflda;
            }
            if (inst.ChildIndex != 0)
            {
                return(false);
            }
            switch (inst.Parent.OpCode)
            {
            case OpCode.Call:
            case OpCode.CallVirt:
                method = ((CallInstruction)inst.Parent).Method;
                if (method.IsAccessor)
                {
                    if (method.AccessorKind == MethodSemanticsAttributes.Getter)
                    {
                        // C# doesn't allow property compound assignments on temporary structs
                        return(!(inst.Parent.Parent is CompoundAssignmentInstruction cai &&
                                 cai.TargetKind == CompoundTargetKind.Property &&
                                 cai.Target == inst.Parent));
                    }
                    else
                    {
                        // C# doesn't allow calling setters on temporary structs
                        return(false);
                    }
                }
                return(!method.IsStatic);

            case OpCode.Await:
                method = ((Await)inst.Parent).GetAwaiterMethod;
                return(true);

            case OpCode.NullableUnwrap:
                return(((NullableUnwrap)inst.Parent).RefInput);

            case OpCode.MatchInstruction:
                method = ((MatchInstruction)inst.Parent).Method;
                return(true);

            default:
                return(false);
            }
        }
Ejemplo n.º 5
0
        static bool IsUsedAsThisPointerInFieldRead(LdLoca ldloca)
        {
            if (ldloca.Variable.Type.IsReferenceType ?? false)
            {
                return(false);
            }
            ILInstruction inst = ldloca;

            while (inst.Parent is LdFlda ldflda)
            {
                inst = ldflda;
            }
            return(inst != ldloca && inst.Parent is LdObj);
        }
Ejemplo n.º 6
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);
            }
        }
Ejemplo n.º 7
0
        static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
        {
            method = null;
            if (ldloca.ChildIndex != 0)
            {
                return(false);
            }
            if (ldloca.Variable.Type.IsReferenceType ?? false)
            {
                return(false);
            }
            ILInstruction inst = ldloca;

            while (inst.Parent is LdFlda ldflda)
            {
                inst = ldflda;
            }
            switch (inst.Parent.OpCode)
            {
            case OpCode.Call:
            case OpCode.CallVirt:
                method = ((CallInstruction)inst.Parent).Method;
                if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter)
                {
                    // C# doesn't allow calling setters on temporary structs
                    return(false);
                }
                return(!method.IsStatic);

            case OpCode.Await:
                method = ((Await)inst.Parent).GetAwaiterMethod;
                return(true);

            case OpCode.NullableUnwrap:
                return(((NullableUnwrap)inst.Parent).RefInput);

            default:
                return(false);
            }
        }
Ejemplo n.º 8
0
        internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
        {
            if (ldloca.ChildIndex != 0)
            {
                return(false);
            }
            if (ldloca.Variable.Type.IsReferenceType ?? false)
            {
                return(false);
            }
            switch (ldloca.Parent.OpCode)
            {
            case OpCode.Call:
            case OpCode.CallVirt:
                return(!((CallInstruction)ldloca.Parent).Method.IsStatic);

            case OpCode.Await:
                return(true);

            default:
                return(false);
            }
        }
Ejemplo n.º 9
0
        internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context)
        {
            // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions
            // =>
            // ldobj(...(ldloca V))
            var temp  = inst.Target;
            var range = temp.ILRanges;

            while (temp.MatchLdFlda(out var ldfldaTarget, out _))
            {
                temp  = ldfldaTarget;
                range = range.Concat(temp.ILRanges);
            }
            if (temp.MatchAddressOf(out var addressOfTarget, out _) && addressOfTarget.MatchLdLoc(out var v))
            {
                context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst);
                var replacement = new LdLoca(v).WithILRange(addressOfTarget);
                foreach (var r in range)
                {
                    replacement = replacement.WithILRange(r);
                }
                temp.ReplaceWith(replacement);
            }
        }
Ejemplo n.º 10
0
 internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
 {
     return(IsUsedAsThisPointerInCall(ldloca, out _));
 }
Ejemplo n.º 11
0
 internal void RemoveAddressInstruction(LdLoca inst) => RemoveInstruction(addressInstructions, inst.IndexInAddressInstructionList, inst);
Ejemplo n.º 12
0
 internal void AddAddressInstruction(LdLoca inst) => inst.IndexInAddressInstructionList        = AddInstruction(addressInstructions, inst);
Ejemplo n.º 13
0
 protected internal override void VisitLdLoca(LdLoca inst)
 {
     base.VisitLdLoca(inst);
     HandleLoad(inst);
 }
Ejemplo n.º 14
0
 protected internal override void VisitLdLoca(LdLoca inst)
 {
     EnsureInitialized(inst.Variable);
 }
Ejemplo n.º 15
0
 protected internal override void VisitLdLoca(LdLoca inst)
 {
     // A variable needs to be initialized before we can take it by reference.
     // The exception is if the variable is passed to an out parameter (handled in VisitCall).
     EnsureInitialized(inst.Variable);
 }