|
|
@ -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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |