Browse Source

Mark stack slot for aggressive removal in InfeasiblePathTransform

This helps with pattern matching in short circuiting operators.
pull/2461/head
Daniel Grunwald 4 years ago
parent
commit
c26d9ad6f1
  1. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 5
      ICSharpCode.Decompiler/IL/ILVariable.cs
  3. 55
      ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs
  4. 1
      ICSharpCode.Decompiler/IL/Transforms/RemoveInfeasiblePathTransform.cs
  5. 4
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -97,10 +97,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -97,10 +97,10 @@ namespace ICSharpCode.Decompiler.CSharp
new DetectExitPoints(),
new LdLocaDupInitObjTransform(),
new EarlyExpressionTransforms(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
// RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...)
// is already collapsed into stloc(V, ...).
new RemoveDeadVariableInit(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
new ControlFlowSimplification(), //split variables may enable new branch to leave inlining
new DynamicCallSiteTransform(),
new SwitchDetection(),

5
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -372,6 +372,11 @@ namespace ICSharpCode.Decompiler.IL @@ -372,6 +372,11 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IField? StateMachineField;
/// <summary>
/// If enabled, remove dead stores to this variable as if the "Remove dead code" option is enabled.
/// </summary>
internal bool RemoveIfRedundant;
public ILVariable(VariableKind kind, IType type, int? index = null)
{
if (type == null)

55
ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs

@ -42,41 +42,44 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -42,41 +42,44 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler.
// In yield return + async, the C# compiler tends to store null/default(T) to variables
// when the variable goes out of scope.
if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores)
bool removeDeadStores = function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores;
var variableQueue = new Queue<ILVariable>(function.Variables);
while (variableQueue.Count > 0)
{
var variableQueue = new Queue<ILVariable>(function.Variables);
while (variableQueue.Count > 0)
var v = variableQueue.Dequeue();
if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot)
continue;
if (!(v.RemoveIfRedundant || removeDeadStores))
continue;
// Skip variables that are captured in a mcs yield state-machine
// loads of these will only be visible after DelegateConstruction step.
if (function.StateMachineCompiledWithMono && v.StateMachineField != null)
continue;
if (v.LoadCount != 0 || v.AddressCount != 0)
continue;
foreach (var stloc in v.StoreInstructions.OfType<StLoc>().ToArray())
{
var v = variableQueue.Dequeue();
if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot)
continue;
// Skip variables that are captured in a mcs yield state-machine
// loads of these will only be visible after DelegateConstruction step.
if (function.StateMachineCompiledWithMono && v.StateMachineField != null)
continue;
if (v.LoadCount != 0 || v.AddressCount != 0)
continue;
foreach (var stloc in v.StoreInstructions.OfType<StLoc>().ToArray())
if (stloc.Parent is Block block)
{
if (stloc.Parent is Block block)
context.Step($"Dead store to {v.Name}", stloc);
if (SemanticHelper.IsPure(stloc.Value.Flags))
{
block.Instructions.Remove(stloc);
}
else
{
if (SemanticHelper.IsPure(stloc.Value.Flags))
{
block.Instructions.Remove(stloc);
}
else
{
stloc.ReplaceWith(stloc.Value);
}
if (stloc.Value is LdLoc ldloc)
{
variableQueue.Enqueue(ldloc.Variable);
}
stloc.ReplaceWith(stloc.Value);
}
if (stloc.Value is LdLoc ldloc)
{
variableQueue.Enqueue(ldloc.Variable);
}
}
}
}
// Try to infer IType of stack slots that are of StackType.Ref:
foreach (var v in function.Variables)
{

1
ICSharpCode.Decompiler/IL/Transforms/RemoveInfeasiblePathTransform.cs

@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
context.Step("RemoveInfeasiblePath", br);
br.TargetBlock = targetBlock;
s.RemoveIfRedundant = true;
return true;
}

4
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case VariableKind.StackSlot:
// stack slots: are already split by construction,
// except for the locals-turned-stackslots in async functions
if (v.Function.IsAsync)
// or stack slots handled by the infeasible path transform
if (v.Function.IsAsync || v.RemoveIfRedundant)
goto case VariableKind.Local;
else
return false;
@ -270,6 +271,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -270,6 +271,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.HasGeneratedName = inst.Variable.HasGeneratedName;
v.StateMachineField = inst.Variable.StateMachineField;
v.HasInitialValue = false; // we'll set HasInitialValue when we encounter an uninit load
v.RemoveIfRedundant = inst.Variable.RemoveIfRedundant;
newVariables.Add(representative, v);
inst.Variable.Function.Variables.Add(v);
}

Loading…
Cancel
Save