Browse Source

Remove compiler-generated variable in while (true) loops.

pull/887/head
Siegfried Pammer 8 years ago
parent
commit
e33a010cc7
  1. 30
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

30
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
@ -52,7 +53,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
SwitchDetection.SimplifySwitchInstruction(block);
}
SimplifyBranchChains(function, context);
CleanUpEmptyBlocks(function);
CleanUpEmptyBlocks(function, context);
}
void InlineVariableInReturnBlock(Block block, ILTransformContext context)
@ -129,13 +130,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
void CleanUpEmptyBlocks(ILFunction function)
void CleanUpEmptyBlocks(ILFunction function, ILTransformContext context)
{
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
foreach (var block in container.Blocks) {
if (block.Instructions.Count == 0)
continue; // block is already marked for deletion
while (CombineBlockWithNextBlock(container, block)) {
while (CombineBlockWithNextBlock(container, block, context)) {
// repeat combining blocks until it is no longer possible
// (this loop terminates because a block is deleted in every iteration)
}
@ -153,7 +154,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return targetBlock.Instructions[0].MatchReturn(out var value) && value is LdLoc;
}
static bool CombineBlockWithNextBlock(BlockContainer container, Block block)
static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context)
{
Debug.Assert(container == block.Parent);
// Ensure the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection.
@ -165,12 +166,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
if (br.TargetBlock == block)
return false; // don't inline block into itself
context.Step("CombineBlockWithNextBlock", br);
var targetBlock = br.TargetBlock;
if (targetBlock.ILRange.Start < block.ILRange.Start && IsDeadTrueStore(block)) {
// The C# compiler generates a dead store for the condition of while (true) loops.
block.Instructions.RemoveRange(block.Instructions.Count - 3, 2);
}
block.Instructions.Remove(br);
block.Instructions.AddRange(targetBlock.Instructions);
targetBlock.Instructions.Clear(); // mark targetBlock for deletion
return true;
}
/// <summary>
/// Returns true if the last two instructions before the branch are storing the value 'true' into an unused variable.
/// </summary>
private static bool IsDeadTrueStore(Block block)
{
if (block.Instructions.Count < 3) return false;
if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore && block.Instructions[block.Instructions.Count - 3] is StLoc tempStore))
return false;
if (!(deadStore.Variable.LoadCount == 0 && deadStore.Variable.AddressCount == 0))
return false;
if (!(deadStore.Value.MatchLdLoc(tempStore.Variable) && tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1))
return false;
return tempStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean);
}
}
}
Loading…
Cancel
Save