From 03a20f3494b7eec7871c1da09e74b7b6e576bfea Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 20 Jul 2024 17:51:45 +0200 Subject: [PATCH] Support ILInlining for in parameters --- .../TestCases/Pretty/ValueTypes.cs | 19 +++++++++++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 17 +++++++++++++---- .../CSharp/ExpressionBuilder.cs | 10 +++++++--- .../IL/Transforms/ILInlining.cs | 17 ++++++++++++++--- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs index c40e55f4c0..87f54d52f7 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs @@ -291,5 +291,24 @@ public static void InliningDefaultValue() public static void Test(object x) { } + +#if CS120 + public static void AcceptIn(in S o) + { + } + + public static void AcceptRefReadOnly(ref readonly S o) + { + } + + private static void Use(in S param) + { + AcceptIn(new S(5)); + S o = new S(10); + AcceptRefReadOnly(in o); + AcceptIn(in param); + AcceptRefReadOnly(in param); + } +#endif } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index c389a0c796..042ca5bc8d 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -867,7 +867,7 @@ private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetai if (parameter.ReferenceKind != ReferenceKind.None) { - arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind); + arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind, callArguments[i] is AddressOf); } arguments.Add(arg); @@ -1179,14 +1179,20 @@ private void CastArguments(IList arguments, IList FieldDirection.Ref, ReferenceKind.Out => FieldDirection.Out, @@ -4454,7 +4458,7 @@ protected internal override TranslatedExpression VisitCallIndirect(CallIndirect var arg = Translate(argInst, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true); if (paramRefKind != ReferenceKind.None) { - arg = ChangeDirectionExpressionTo(arg, paramRefKind); + arg = ChangeDirectionExpressionTo(arg, paramRefKind, argInst is AddressOf); } invocation.Arguments.Add(arg); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index fc15ebc3ae..416972039a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -244,7 +244,7 @@ static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstructio var loadInst = r.LoadInst; if (loadInst.OpCode == OpCode.LdLoca) { - if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression, options)) + if (!IsGeneratedTemporaryForAddressOf((LdLoca)loadInst, v, inlinedExpression, options)) return false; } else @@ -289,7 +289,7 @@ static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstructio /// /// The load instruction (a descendant within 'next') /// The variable being inlined. - static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options) + static bool IsGeneratedTemporaryForAddressOf(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options) { Debug.Assert(loadInst.Variable == v); if (!options.HasFlag(InliningOptions.AllowInliningOfLdloca)) @@ -301,7 +301,7 @@ static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstr // 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, out var constrainedTo)) + if (IsUsedAsThisPointerInCall(loadInst, out var method, out var constrainedTo) || IsPassedToInParameter(loadInst, out method)) { if (options.HasFlag(InliningOptions.Aggressive)) { @@ -415,6 +415,17 @@ static bool IsUsedAsThisPointerInFieldRead(LdLoca ldloca) return inst != ldloca && inst.Parent is LdObj; } + static bool IsPassedToInParameter(LdLoca ldloca, out IMethod method) + { + method = null; + if (ldloca.Parent is not CallInstruction call) + { + return false; + } + method = call.Method; + return call.GetParameter(ldloca.ChildIndex)?.ReferenceKind is ReferenceKind.In; + } + /// /// Gets whether the instruction, when converted into C#, turns into an l-value that can /// be used to mutate a value-type.