diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 695af22a4..a896ca11a 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/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()) { 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; } - + + /// + /// Returns true if the last two instructions before the branch are storing the value 'true' into an unused variable. + /// + 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); + } } }