|
|
@ -80,19 +80,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
{ |
|
|
|
{ |
|
|
|
bool modified = false; |
|
|
|
bool modified = false; |
|
|
|
var instructions = block.Instructions; |
|
|
|
var instructions = block.Instructions; |
|
|
|
for (int i = 0; i < instructions.Count;) { |
|
|
|
for (int i = instructions.Count - 1; i >= 0; i--) { |
|
|
|
if (instructions[i] is StLoc inst) { |
|
|
|
if (instructions[i] is StLoc inst) { |
|
|
|
InliningOptions options = InliningOptions.None; |
|
|
|
InliningOptions options = InliningOptions.None; |
|
|
|
if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst, ref ctorCallStart)) |
|
|
|
if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst, ref ctorCallStart)) |
|
|
|
options = InliningOptions.Aggressive; |
|
|
|
options = InliningOptions.Aggressive; |
|
|
|
if (InlineOneIfPossible(block, i, options, context)) { |
|
|
|
if (InlineOneIfPossible(block, i, options, context)) { |
|
|
|
modified = true; |
|
|
|
modified = true; |
|
|
|
i = Math.Max(0, i - 1); |
|
|
|
|
|
|
|
// Go back one step
|
|
|
|
|
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
i++; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return modified; |
|
|
|
return modified; |
|
|
|
} |
|
|
|
} |
|
|
@ -217,20 +214,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context) |
|
|
|
static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst); |
|
|
|
var r = FindLoadInNext(next, v, inlinedExpression, options); |
|
|
|
if (r == FindResult.Found) { |
|
|
|
if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument) { |
|
|
|
|
|
|
|
var loadInst = r.LoadInst; |
|
|
|
if (loadInst.OpCode == OpCode.LdLoca) { |
|
|
|
if (loadInst.OpCode == OpCode.LdLoca) { |
|
|
|
if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression)) |
|
|
|
if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Debug.Assert(loadInst.OpCode == OpCode.LdLoc); |
|
|
|
Debug.Assert(loadInst.OpCode == OpCode.LdLoc); |
|
|
|
bool aggressive = (options & InliningOptions.Aggressive) != 0; |
|
|
|
bool aggressive = (options & InliningOptions.Aggressive) != 0; |
|
|
|
if (!aggressive && v.Kind != VariableKind.StackSlot |
|
|
|
if (!aggressive && v.Kind != VariableKind.StackSlot |
|
|
|
&& !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v)) { |
|
|
|
&& !NonAggressiveInlineInto(next, r, inlinedExpression, v)) { |
|
|
|
return false; |
|
|
|
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); |
|
|
|
context.Step($"Inline variable '{v.Name}'", inlinedExpression); |
|
|
|
// Assign the ranges of the ldloc instruction:
|
|
|
|
// Assign the ranges of the ldloc instruction:
|
|
|
|
inlinedExpression.AddILRange(loadInst); |
|
|
|
inlinedExpression.AddILRange(loadInst); |
|
|
@ -243,9 +246,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
loadInst.ReplaceWith(inlinedExpression); |
|
|
|
loadInst.ReplaceWith(inlinedExpression); |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0) { |
|
|
|
|
|
|
|
return NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst, |
|
|
|
|
|
|
|
options, context); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
@ -253,10 +253,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
|
|
|
|
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="next">The next top-level expression</param>
|
|
|
|
|
|
|
|
/// <param name="loadInst">The load instruction (a descendant within 'next')</param>
|
|
|
|
/// <param name="loadInst">The load instruction (a descendant within 'next')</param>
|
|
|
|
/// <param name="v">The variable being inlined.</param>
|
|
|
|
/// <param name="v">The variable being inlined.</param>
|
|
|
|
static bool IsGeneratedValueTypeTemporary(ILInstruction next, LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression) |
|
|
|
static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Debug.Assert(loadInst.Variable == v); |
|
|
|
Debug.Assert(loadInst.Variable == v); |
|
|
|
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics
|
|
|
|
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics
|
|
|
@ -321,8 +320,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
/// <param name="next">The next top-level expression</param>
|
|
|
|
/// <param name="next">The next top-level expression</param>
|
|
|
|
/// <param name="loadInst">The load within 'next'</param>
|
|
|
|
/// <param name="loadInst">The load within 'next'</param>
|
|
|
|
/// <param name="inlinedExpression">The expression being inlined</param>
|
|
|
|
/// <param name="inlinedExpression">The expression being inlined</param>
|
|
|
|
static bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression, ILVariable v) |
|
|
|
static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
if (findResult.Type == FindResultType.NamedArgument) { |
|
|
|
|
|
|
|
var originalStore = (StLoc)inlinedExpression.Parent; |
|
|
|
|
|
|
|
return !originalStore.ILStackWasEmpty; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Debug.Assert(findResult.Type == FindResultType.Found); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var loadInst = findResult.LoadInst; |
|
|
|
Debug.Assert(loadInst.IsDescendantOf(next)); |
|
|
|
Debug.Assert(loadInst.IsDescendantOf(next)); |
|
|
|
|
|
|
|
|
|
|
|
// decide based on the source expression being inlined
|
|
|
|
// decide based on the source expression being inlined
|
|
|
@ -406,10 +412,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public static bool CanInlineInto(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved) |
|
|
|
public static bool CanInlineInto(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return FindLoadInNext(expr, v, expressionBeingMoved, out _) == FindResult.Found; |
|
|
|
return FindLoadInNext(expr, v, expressionBeingMoved, InliningOptions.None).Type == FindResultType.Found; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal enum FindResult |
|
|
|
internal enum FindResultType |
|
|
|
{ |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Found a load; inlining is possible.
|
|
|
|
/// Found a load; inlining is possible.
|
|
|
@ -432,19 +438,47 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
NamedArgument, |
|
|
|
NamedArgument, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal readonly struct FindResult |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
public readonly FindResultType Type; |
|
|
|
|
|
|
|
public readonly ILInstruction LoadInst; // ldloc or ldloca instruction that loads the variable to be inlined
|
|
|
|
|
|
|
|
public readonly ILInstruction CallArgument; // argument of call that needs to be promoted to a named argument
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private FindResult(FindResultType type, ILInstruction loadInst, ILInstruction callArg) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
this.Type = type; |
|
|
|
|
|
|
|
this.LoadInst = loadInst; |
|
|
|
|
|
|
|
this.CallArgument = callArg; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly FindResult Stop = new FindResult(FindResultType.Stop, null, null); |
|
|
|
|
|
|
|
public static readonly FindResult Continue = new FindResult(FindResultType.Continue, null, null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static FindResult Found(ILInstruction loadInst) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(loadInst.OpCode == OpCode.LdLoc || loadInst.OpCode == OpCode.LdLoca); |
|
|
|
|
|
|
|
return new FindResult(FindResultType.Found, loadInst, null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static FindResult NamedArgument(ILInstruction loadInst, ILInstruction callArg) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(loadInst.OpCode == OpCode.LdLoc || loadInst.OpCode == OpCode.LdLoca); |
|
|
|
|
|
|
|
Debug.Assert(callArg.Parent is CallInstruction); |
|
|
|
|
|
|
|
return new FindResult(FindResultType.NamedArgument, loadInst, callArg); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Finds the position to inline to.
|
|
|
|
/// Finds the position to inline to.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>true = found; false = cannot continue search; null = not found</returns>
|
|
|
|
/// <returns>true = found; false = cannot continue search; null = not found</returns>
|
|
|
|
internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst) |
|
|
|
internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options) |
|
|
|
{ |
|
|
|
{ |
|
|
|
loadInst = null; |
|
|
|
|
|
|
|
if (expr == null) |
|
|
|
if (expr == null) |
|
|
|
return FindResult.Stop; |
|
|
|
return FindResult.Stop; |
|
|
|
if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v)) { |
|
|
|
if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v)) { |
|
|
|
// Match found, we can inline
|
|
|
|
// Match found, we can inline
|
|
|
|
loadInst = expr; |
|
|
|
return FindResult.Found(expr); |
|
|
|
return FindResult.Found; |
|
|
|
|
|
|
|
} else if (expr is Block block) { |
|
|
|
} else if (expr is Block block) { |
|
|
|
// Inlining into inline-blocks?
|
|
|
|
// Inlining into inline-blocks?
|
|
|
|
switch (block.Kind) { |
|
|
|
switch (block.Kind) { |
|
|
@ -455,17 +489,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
// Allow inlining into the first instruction of the block
|
|
|
|
// Allow inlining into the first instruction of the block
|
|
|
|
if (block.Instructions.Count == 0) |
|
|
|
if (block.Instructions.Count == 0) |
|
|
|
return FindResult.Stop; |
|
|
|
return FindResult.Stop; |
|
|
|
return NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, out loadInst)); |
|
|
|
return NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)); |
|
|
|
// If FindLoadInNext() returns null, we still can't continue searching
|
|
|
|
// If FindLoadInNext() returns null, we still can't continue searching
|
|
|
|
// because we can't inline over the remainder of the block.
|
|
|
|
// because we can't inline over the remainder of the block.
|
|
|
|
case BlockKind.CallWithNamedArgs: |
|
|
|
case BlockKind.CallWithNamedArgs: |
|
|
|
return NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst); |
|
|
|
return NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved); |
|
|
|
default: |
|
|
|
default: |
|
|
|
return FindResult.Stop; |
|
|
|
return FindResult.Stop; |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1) { |
|
|
|
} else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1) { |
|
|
|
// Possibly a switch-container, allow inlining into the switch instruction:
|
|
|
|
// Possibly a switch-container, allow inlining into the switch instruction:
|
|
|
|
return NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst)); |
|
|
|
return NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, options)); |
|
|
|
// If FindLoadInNext() returns null, we still can't continue searching
|
|
|
|
// If FindLoadInNext() returns null, we still can't continue searching
|
|
|
|
// because we can't inline over the remainder of the blockcontainer.
|
|
|
|
// because we can't inline over the remainder of the blockcontainer.
|
|
|
|
} else if (expr is NullableRewrap) { |
|
|
|
} else if (expr is NullableRewrap) { |
|
|
@ -479,10 +513,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return FindResult.Stop; |
|
|
|
return FindResult.Stop; |
|
|
|
|
|
|
|
|
|
|
|
// Recursively try to find the load instruction
|
|
|
|
// Recursively try to find the load instruction
|
|
|
|
FindResult r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst); |
|
|
|
FindResult r = FindLoadInNext(child, v, expressionBeingMoved, options); |
|
|
|
if (r != FindResult.Continue) { |
|
|
|
if (r.Type != FindResultType.Continue) { |
|
|
|
if (r == FindResult.Stop && expr is CallInstruction call) |
|
|
|
if (r.Type == FindResultType.Stop && (options & InliningOptions.IntroduceNamedArguments) != 0 && expr is CallInstruction call) |
|
|
|
return NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, out loadInst); |
|
|
|
return NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, expressionBeingMoved); |
|
|
|
return r; |
|
|
|
return r; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -494,7 +528,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
|
|
|
|
|
|
|
|
private static FindResult NoContinue(FindResult findResult) |
|
|
|
private static FindResult NoContinue(FindResult findResult) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (findResult == FindResult.Continue) |
|
|
|
if (findResult.Type == FindResultType.Continue) |
|
|
|
return FindResult.Stop; |
|
|
|
return FindResult.Stop; |
|
|
|
else |
|
|
|
else |
|
|
|
return findResult; |
|
|
|
return findResult; |
|
|
|