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); } }
/// <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"); } }
/// <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)); }
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); } }
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); }
/// <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); } }
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); } }
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); } }
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); } }
internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca) { return(IsUsedAsThisPointerInCall(ldloca, out _)); }
internal void RemoveAddressInstruction(LdLoca inst) => RemoveInstruction(addressInstructions, inst.IndexInAddressInstructionList, inst);
internal void AddAddressInstruction(LdLoca inst) => inst.IndexInAddressInstructionList = AddInstruction(addressInstructions, inst);
protected internal override void VisitLdLoca(LdLoca inst) { base.VisitLdLoca(inst); HandleLoad(inst); }
protected internal override void VisitLdLoca(LdLoca inst) { EnsureInitialized(inst.Variable); }
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); }