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
new DetectExitPoints(), new DetectExitPoints(),
new LdLocaDupInitObjTransform(), new LdLocaDupInitObjTransform(),
new EarlyExpressionTransforms(), 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, ...) // RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...)
// is already collapsed into stloc(V, ...). // is already collapsed into stloc(V, ...).
new RemoveDeadVariableInit(), 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 ControlFlowSimplification(), //split variables may enable new branch to leave inlining
new DynamicCallSiteTransform(), new DynamicCallSiteTransform(),
new SwitchDetection(), new SwitchDetection(),

5
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -372,6 +372,11 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public IField? StateMachineField; 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) public ILVariable(VariableKind kind, IType type, int? index = null)
{ {
if (type == null) if (type == null)

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

@ -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. // 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 // In yield return + async, the C# compiler tends to store null/default(T) to variables
// when the variable goes out of scope. // 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); var v = variableQueue.Dequeue();
while (variableQueue.Count > 0) 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 (stloc.Parent is Block block)
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) 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)) stloc.ReplaceWith(stloc.Value);
{ }
block.Instructions.Remove(stloc); if (stloc.Value is LdLoc ldloc)
} {
else 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: // Try to infer IType of stack slots that are of StackType.Ref:
foreach (var v in function.Variables) foreach (var v in function.Variables)
{ {

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

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

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

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

Loading…
Cancel
Save