Browse Source

Performance: Cache ChainedConstructorCallILOffset in ILFunction to avoid having to scan the method body in every inlining step. Take a shortcut for static ctors, as there cannot be chained ctor calls in static ctors.

pull/1505/head
Siegfried Pammer 6 years ago
parent
commit
b03aa488aa
  1. 21
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  2. 30
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  3. 3
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

21
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -72,6 +72,27 @@ namespace ICSharpCode.Decompiler.IL
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo; internal DebugInfo.AsyncDebugInfo AsyncDebugInfo;
int ctorCallStart = int.MinValue;
/// <summary>
/// Returns the IL offset of the constructor call, -1 if this is not a constructor or no chained constructor call was found.
/// </summary>
internal int ChainedConstructorCallILOffset {
get {
if (ctorCallStart == int.MinValue) {
if (!this.Method.IsConstructor || this.Method.IsStatic)
ctorCallStart = -1;
else {
ctorCallStart = this.Descendants.FirstOrDefault(d => d is CallInstruction call && !(call is NewObj)
&& call.Method.IsConstructor
&& call.Method.DeclaringType.IsReferenceType == true
&& call.Parent is Block)?.StartILOffset ?? -1;
}
}
return ctorCallStart;
}
}
/// <summary> /// <summary>
/// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T. /// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T.
/// T is the delegate type that matches the signature of this method. /// T is the delegate type that matches the signature of this method.

30
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -39,9 +39,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
int? ctorCallStart = null;
foreach (var block in function.Descendants.OfType<Block>()) { foreach (var block in function.Descendants.OfType<Block>()) {
InlineAllInBlock(function, block, context, ref ctorCallStart); InlineAllInBlock(function, block, context);
} }
function.Variables.RemoveDead(); function.Variables.RemoveDead();
} }
@ -64,27 +63,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
else { else {
var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault(); var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault();
var inst = block.Instructions[pos]; var inst = block.Instructions[pos];
int? ctorCallStart = null; if (IsInConstructorInitializer(function, inst))
if (IsInConstructorInitializer(function, inst, ref ctorCallStart))
options |= InliningOptions.Aggressive; options |= InliningOptions.Aggressive;
} }
return options; return options;
} }
public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context) public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context)
{
int? ctorCallStart = null;
return InlineAllInBlock(function, block, context, ref ctorCallStart);
}
static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context, ref int? ctorCallStart)
{ {
bool modified = false; bool modified = false;
var instructions = block.Instructions; var instructions = block.Instructions;
for (int i = instructions.Count - 1; i >= 0; i--) { 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))
options = InliningOptions.Aggressive; options = InliningOptions.Aggressive;
if (InlineOneIfPossible(block, i, options, context)) { if (InlineOneIfPossible(block, i, options, context)) {
modified = true; modified = true;
@ -95,23 +87,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return modified; return modified;
} }
internal static bool IsInConstructorInitializer(ILFunction function, ILInstruction inst, ref int? ctorCallStart) internal static bool IsInConstructorInitializer(ILFunction function, ILInstruction inst)
{ {
if (ctorCallStart == null) { int ctorCallStart = function.ChainedConstructorCallILOffset;
if (function == null || !function.Method.IsConstructor) if (inst.EndILOffset > ctorCallStart)
ctorCallStart = -1;
else
ctorCallStart = function.Descendants.FirstOrDefault(d => d is CallInstruction call && !(call is NewObj)
&& call.Method.IsConstructor
&& call.Method.DeclaringType.IsReferenceType == true
&& call.Parent is Block)?.StartILOffset ?? -1;
}
if (inst.EndILOffset > ctorCallStart.GetValueOrDefault())
return false; return false;
var topLevelInst = inst.Ancestors.LastOrDefault(instr => instr.Parent is Block); var topLevelInst = inst.Ancestors.LastOrDefault(instr => instr.Parent is Block);
if (topLevelInst == null) if (topLevelInst == null)
return false; return false;
return topLevelInst.EndILOffset <= ctorCallStart.GetValueOrDefault(); return topLevelInst.EndILOffset <= ctorCallStart;
} }
internal static bool IsCatchWhenBlock(Block block) internal static bool IsCatchWhenBlock(Block block)

3
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -439,8 +439,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (elementCount >= length / 3 - 5) if (elementCount >= length / 3 - 5)
return true; return true;
int? unused = null; if (ILInlining.IsCatchWhenBlock(block) || ILInlining.IsInConstructorInitializer(function, block.Instructions[startPos]))
if (ILInlining.IsCatchWhenBlock(block) || ILInlining.IsInConstructorInitializer(function, block.Instructions[startPos], ref unused))
return true; return true;
return false; return false;
} }

Loading…
Cancel
Save