Skip to content

Commit

Permalink
Support ILInlining for in parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
siegfriedpammer committed Jul 20, 2024
1 parent fd1de09 commit 03a20f3
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 10 deletions.
19 changes: 19 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
17 changes: 13 additions & 4 deletions ICSharpCode.Decompiler/CSharp/CallBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1179,14 +1179,20 @@ private void CastArguments(IList<TranslatedExpression> arguments, IList<IParamet
}
else
{
IParameter parameter = expectedParameters[i];
IType parameterType;
if (expectedParameters[i].Type.Kind == TypeKind.Dynamic)
if (parameter.Type.Kind == TypeKind.Dynamic)
{
parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object);
}
else
{
parameterType = expectedParameters[i].Type;
parameterType = parameter.Type;
}

if (parameter.ReferenceKind == ReferenceKind.In && parameterType is ByReferenceType brt && arguments[i].Type is not ByReferenceType)
{
parameterType = brt.ElementType;
}

arguments[i] = arguments[i].ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: false);
Expand Down Expand Up @@ -1295,7 +1301,10 @@ OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetD
resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition;
if (lookup.IsAccessible(ctor, allowProtectedAccess))
{
or.AddCandidate(ctor);
Log.Indent();
OverloadResolutionErrors errors = or.AddCandidate(ctor);
Log.Unindent();
or.LogCandidateAddingResult(" Candidate", ctor, errors);
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4174,7 +4174,7 @@ TranslatedExpression TranslateDynamicArgument(ILInstruction argument, CSharpArgu
}
if (info.HasFlag(CSharpArgumentInfoFlags.IsOut))
{
translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out);
translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out, argument is AddressOf);
}
if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name))
{
Expand All @@ -4184,10 +4184,14 @@ TranslatedExpression TranslateDynamicArgument(ILInstruction argument, CSharpArgu
return translatedExpression;
}

internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind)
internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind, bool isAddressOf)
{
if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr))
return input;
if (isAddressOf && kind is ReferenceKind.In or ReferenceKind.RefReadOnly)
{
return input.UnwrapChild(dirExpr.Expression);
}
dirExpr.FieldDirection = kind switch {
ReferenceKind.Ref => FieldDirection.Ref,
ReferenceKind.Out => FieldDirection.Out,
Expand Down Expand Up @@ -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);
}
Expand Down
17 changes: 14 additions & 3 deletions ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -289,7 +289,7 @@ static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstructio
/// </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)
static bool IsGeneratedTemporaryForAddressOf(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression, InliningOptions options)
{
Debug.Assert(loadInst.Variable == v);
if (!options.HasFlag(InliningOptions.AllowInliningOfLdloca))
Expand All @@ -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))
{
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Gets whether the instruction, when converted into C#, turns into an l-value that can
/// be used to mutate a value-type.
Expand Down

0 comments on commit 03a20f3

Please sign in to comment.