|
|
|
@ -19,6 +19,7 @@
@@ -19,6 +19,7 @@
|
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Linq; |
|
|
|
|
|
|
|
|
|
using ICSharpCode.Decompiler.IL.ControlFlow; |
|
|
|
|
using ICSharpCode.Decompiler.TypeSystem; |
|
|
|
|
using ICSharpCode.Decompiler.Util; |
|
|
|
@ -44,6 +45,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -44,6 +45,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
|
|
|
|
|
foreach (var block in function.Descendants.OfType<Block>()) { |
|
|
|
|
bool changed = false; |
|
|
|
|
if (block.IncomingEdgeCount == 0) |
|
|
|
|
continue; |
|
|
|
|
for (int i = block.Instructions.Count - 1; i >= 0; i--) { |
|
|
|
|
if (SimplifyCascadingIfStatements(block.Instructions, ref i)) { |
|
|
|
|
changed = true; |
|
|
|
@ -782,58 +785,114 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -782,58 +785,114 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
|
|
|
|
|
bool MatchRoslynSwitchOnString(InstructionCollection<ILInstruction> instructions, ref int i) |
|
|
|
|
{ |
|
|
|
|
if (i < 1) return false; |
|
|
|
|
if (i >= instructions.Count - 1) return false; |
|
|
|
|
// stloc switchValueVar(switchValue)
|
|
|
|
|
// if (comp(ldloc switchValueVar == ldnull)) br nullCase
|
|
|
|
|
// br nextBlock
|
|
|
|
|
InstructionCollection<ILInstruction> switchBlockInstructions = instructions; |
|
|
|
|
int switchBlockInstructionsOffset = i; |
|
|
|
|
Block nullValueCaseBlock = null; |
|
|
|
|
if (instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump) |
|
|
|
|
&& condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull()) |
|
|
|
|
{ |
|
|
|
|
var nextBlockJump = instructions[i + 1] as Branch; |
|
|
|
|
if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) |
|
|
|
|
return false; |
|
|
|
|
if (!exitBlockJump.MatchBranch(out nullValueCaseBlock)) |
|
|
|
|
return false; |
|
|
|
|
switchBlockInstructions = nextBlockJump.TargetBlock.Instructions; |
|
|
|
|
switchBlockInstructionsOffset = 0; |
|
|
|
|
} |
|
|
|
|
// stloc switchValueVar(call ComputeStringHash(switchValue))
|
|
|
|
|
// switch (ldloc switchValueVar) {
|
|
|
|
|
// case [211455823..211455824): br caseBlock1
|
|
|
|
|
// ... more cases ...
|
|
|
|
|
// case [long.MinValue..-365098645),...,[1697255802..long.MaxValue]: br defaultBlock
|
|
|
|
|
// }
|
|
|
|
|
if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var switchValueVar) && |
|
|
|
|
MatchComputeStringHashCall(instructions[i - 1], switchValueVar, out LdLoc switchValueLoad))) |
|
|
|
|
if (!(switchBlockInstructionsOffset + 1 < switchBlockInstructions.Count && switchBlockInstructions[switchBlockInstructionsOffset + 1] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var switchValueVar) && |
|
|
|
|
MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
var stringValues = new List<(int, string, Block)>(); |
|
|
|
|
int index = 0; |
|
|
|
|
SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); |
|
|
|
|
Block exitOrDefaultBlock = null; |
|
|
|
|
foreach (var section in switchInst.Sections) { |
|
|
|
|
if (section == defaultSection) continue; |
|
|
|
|
// extract target block
|
|
|
|
|
if (!section.Body.MatchBranch(out Block target)) |
|
|
|
|
return false; |
|
|
|
|
if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out Block body, out string stringValue)) |
|
|
|
|
if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out Block body, out Block currentExitBlock, out string stringValue)) |
|
|
|
|
return false; |
|
|
|
|
if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) |
|
|
|
|
return false; |
|
|
|
|
exitOrDefaultBlock = currentExitBlock; |
|
|
|
|
stringValues.Add((index++, stringValue, body)); |
|
|
|
|
} |
|
|
|
|
ILInstruction switchValueInst = switchValueLoad; |
|
|
|
|
// stloc switchValueLoadVariable(switchValue)
|
|
|
|
|
// stloc switchValueVar(call ComputeStringHash(ldloc switchValueLoadVariable))
|
|
|
|
|
// switch (ldloc switchValueVar) {
|
|
|
|
|
bool keepAssignmentBefore; |
|
|
|
|
// if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it.
|
|
|
|
|
if (i > 1 && instructions[i - 2].MatchStLoc(switchValueLoad.Variable, out var switchValueTmp) && |
|
|
|
|
switchValueLoad.Variable.IsSingleDefinition && switchValueLoad.Variable.LoadCount == switchInst.Sections.Count) |
|
|
|
|
{ |
|
|
|
|
switchValueInst = switchValueTmp; |
|
|
|
|
keepAssignmentBefore = false; |
|
|
|
|
} else { |
|
|
|
|
keepAssignmentBefore = true; |
|
|
|
|
|
|
|
|
|
if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) { |
|
|
|
|
stringValues.Add((index++, null, nullValueCaseBlock)); |
|
|
|
|
} |
|
|
|
|
var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); |
|
|
|
|
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray())); |
|
|
|
|
newSwitch.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); |
|
|
|
|
newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); |
|
|
|
|
instructions[i].ReplaceWith(newSwitch); |
|
|
|
|
if (keepAssignmentBefore) { |
|
|
|
|
newSwitch.AddILRange(instructions[i - 1]); |
|
|
|
|
instructions.RemoveAt(i - 1); |
|
|
|
|
i--; |
|
|
|
|
|
|
|
|
|
ILInstruction switchValueInst = switchValueLoad; |
|
|
|
|
if (instructions == switchBlockInstructions) { |
|
|
|
|
// stloc switchValueLoadVariable(switchValue)
|
|
|
|
|
// stloc switchValueVar(call ComputeStringHash(ldloc switchValueLoadVariable))
|
|
|
|
|
// switch (ldloc switchValueVar) {
|
|
|
|
|
bool keepAssignmentBefore; |
|
|
|
|
// if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it.
|
|
|
|
|
if (i >= 1 && instructions[i - 1].MatchStLoc(switchValueLoad.Variable, out var switchValueTmp) && |
|
|
|
|
switchValueLoad.Variable.IsSingleDefinition && switchValueLoad.Variable.LoadCount == switchInst.Sections.Count) { |
|
|
|
|
switchValueInst = switchValueTmp; |
|
|
|
|
keepAssignmentBefore = false; |
|
|
|
|
} else { |
|
|
|
|
keepAssignmentBefore = true; |
|
|
|
|
} |
|
|
|
|
// replace stloc switchValueVar(call ComputeStringHash(...)) with new switch instruction
|
|
|
|
|
var newSwitch = ReplaceWithSwitchInstruction(i); |
|
|
|
|
// remove old switch instruction
|
|
|
|
|
newSwitch.AddILRange(instructions[i + 1]); |
|
|
|
|
instructions.RemoveAt(i + 1); |
|
|
|
|
// remove extra assignment
|
|
|
|
|
if (!keepAssignmentBefore) { |
|
|
|
|
newSwitch.AddILRange(instructions[i - 1]); |
|
|
|
|
instructions.RemoveRange(i - 1, 1); |
|
|
|
|
i -= 1; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
newSwitch.AddILRange(instructions[i - 2]); |
|
|
|
|
instructions.RemoveRange(i - 2, 2); |
|
|
|
|
i -= 2; |
|
|
|
|
bool keepAssignmentBefore; |
|
|
|
|
// if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it.
|
|
|
|
|
if (i >= 2 && instructions[i - 2].MatchStLoc(out var temporary, out var temporaryValue) && instructions[i - 1].MatchStLoc(switchValueLoad.Variable, out var tempLoad) && tempLoad.MatchLdLoc(temporary)) { |
|
|
|
|
switchValueInst = temporaryValue; |
|
|
|
|
keepAssignmentBefore = false; |
|
|
|
|
} else { |
|
|
|
|
keepAssignmentBefore = true; |
|
|
|
|
} |
|
|
|
|
// replace null check with new switch instruction
|
|
|
|
|
var newSwitch = ReplaceWithSwitchInstruction(i); |
|
|
|
|
newSwitch.AddILRange(switchInst); |
|
|
|
|
// remove jump instruction to switch block
|
|
|
|
|
newSwitch.AddILRange(instructions[i + 1]); |
|
|
|
|
instructions.RemoveAt(i + 1); |
|
|
|
|
// remove extra assignment
|
|
|
|
|
if (!keepAssignmentBefore) { |
|
|
|
|
newSwitch.AddILRange(instructions[i - 2]); |
|
|
|
|
instructions.RemoveRange(i - 2, 2); |
|
|
|
|
i -= 2; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
SwitchInstruction ReplaceWithSwitchInstruction(int offset) |
|
|
|
|
{ |
|
|
|
|
var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); |
|
|
|
|
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray())); |
|
|
|
|
newSwitch.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new LongSet(section.Item1), Body = new Branch(section.Item3) })); |
|
|
|
|
newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); |
|
|
|
|
instructions[offset].ReplaceWith(newSwitch); |
|
|
|
|
return newSwitch; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -841,9 +900,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -841,9 +900,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
/// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body
|
|
|
|
|
/// br exit
|
|
|
|
|
/// </summary>
|
|
|
|
|
bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out Block body, out string stringValue) |
|
|
|
|
bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out Block body, out Block defaultOrExitBlock, out string stringValue) |
|
|
|
|
{ |
|
|
|
|
body = null; |
|
|
|
|
defaultOrExitBlock = null; |
|
|
|
|
stringValue = null; |
|
|
|
|
if (target.Instructions.Count != 2) |
|
|
|
|
return false; |
|
|
|
@ -851,11 +911,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -851,11 +911,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|
|
|
|
return false; |
|
|
|
|
if (MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { |
|
|
|
|
var exitBranch = target.Instructions[1]; |
|
|
|
|
if (!(exitBranch.MatchBranch(out _) || exitBranch.MatchLeave(out _))) |
|
|
|
|
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _))) |
|
|
|
|
return false; |
|
|
|
|
return bodyBranch.MatchBranch(out body) && body != null; |
|
|
|
|
} else if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { |
|
|
|
|
if (!(bodyBranch.MatchBranch(out _) || bodyBranch.MatchLeave(out _))) |
|
|
|
|
if (!(bodyBranch.MatchBranch(out defaultOrExitBlock) || bodyBranch.MatchLeave(out _))) |
|
|
|
|
return false; |
|
|
|
|
return target.Instructions[1].MatchBranch(out body) && body != null; |
|
|
|
|
} else { |
|
|
|
|