Browse Source

Fix #1526: Roslyn-optimized switch at end of method uses leave instead of br.

pull/1556/head
Siegfried Pammer 7 years ago
parent
commit
f7641037a2
  1. 49
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

49
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -814,7 +814,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad))) MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad)))
return false; return false;
var stringValues = new List<(int, string, Block)>(); var stringValues = new List<(int Index, string Value, ILInstruction TargetBlockOrLeave)>();
int index = 0; int index = 0;
SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count());
Block exitOrDefaultBlock = null; Block exitOrDefaultBlock = null;
@ -823,12 +823,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// extract target block // extract target block
if (!section.Body.MatchBranch(out Block target)) if (!section.Body.MatchBranch(out Block target))
return false; return false;
if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out Block body, out Block currentExitBlock, out string stringValue)) if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock, out string stringValue))
return false; return false;
if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock)
return false; return false;
exitOrDefaultBlock = currentExitBlock; exitOrDefaultBlock = currentExitBlock;
stringValues.Add((index++, stringValue, body)); stringValues.Add((index++, stringValue, targetOrLeave));
} }
if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) { if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) {
@ -888,8 +888,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
SwitchInstruction ReplaceWithSwitchInstruction(int offset) SwitchInstruction ReplaceWithSwitchInstruction(int offset)
{ {
var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); var defaultLabel = new LongSet(new LongInterval(0, index)).Invert();
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray())); var values = new string[stringValues.Count];
newSwitch.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new LongSet(section.Item1), Body = new Branch(section.Item3) })); var sections = new SwitchSection[stringValues.Count];
foreach (var (idx, (label, value, bodyInstruction)) in stringValues.WithIndex()) {
values[idx] = value;
var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction;
sections[idx] = new SwitchSection { Labels = new LongSet(label), Body = body };
}
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, values));
newSwitch.Sections.AddRange(sections);
newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body });
instructions[offset].ReplaceWith(newSwitch); instructions[offset].ReplaceWith(newSwitch);
return newSwitch; return newSwitch;
@ -901,27 +908,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body /// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body
/// br exit /// br exit
/// </summary> /// </summary>
bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out Block body, out Block defaultOrExitBlock, out string stringValue) bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue)
{ {
body = null; bodyOrLeave = null;
defaultOrExitBlock = null; defaultOrExitBlock = null;
stringValue = null; stringValue = null;
if (target.Instructions.Count != 2) if (target.Instructions.Count != 2)
return false; return false;
if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch))
return false; return false;
if (MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { ILInstruction exitBranch;
var exitBranch = target.Instructions[1]; // Handle negated conditions first:
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _))) if (condition.MatchLogicNot(out var expr)) {
return false; exitBranch = bodyBranch;
return bodyBranch.MatchBranch(out body) && body != null; bodyBranch = target.Instructions[1];
} else if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { condition = expr;
if (!(bodyBranch.MatchBranch(out defaultOrExitBlock) || bodyBranch.MatchLeave(out _)))
return false;
return target.Instructions[1].MatchBranch(out body) && body != null;
} else { } else {
exitBranch = target.Instructions[1];
}
if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue))
return false;
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _)))
return false; return false;
if (bodyBranch.MatchLeave(out _)) {
bodyOrLeave = bodyBranch;
return true;
}
if (bodyBranch.MatchBranch(out var bodyBlock)) {
bodyOrLeave = bodyBlock;
return true;
} }
return false;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save