Browse Source

Refactoring and cleanup of SimplifyCascadingIfStatements and other switch transforms.

pull/887/head
Siegfried Pammer 8 years ago
parent
commit
63b626716f
  1. 103
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

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

@ -115,36 +115,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!firstBlockJump.MatchBranch(out var firstBlock)) if (!firstBlockJump.MatchBranch(out var firstBlock))
return false; return false;
List<(string, Block)> values = new List<(string, Block)>(); List<(string, Block)> values = new List<(string, Block)>();
// match null check: this is used by the old C# compiler.
if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) {
values.Add((null, firstBlock));
// Roslyn: match call to operator ==(string, string)
} else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) {
ILInstruction switchValue = null;
// Roslyn: match call to operator ==(string, string)
if (MatchStringEqualityComparison(condition, out var switchValueVar, out string value)) {
values.Add((value, firstBlock)); values.Add((value, firstBlock));
} else return false;
// switchValueVar must be assigned only once and must be of type string.
if (!switchValueVar.IsSingleDefinition || !switchValueVar.Type.IsKnownType(KnownTypeCode.String))
return false;
// if instruction must be preceeded by a stloc to the switchValueVar
if (instructions[i - 1].MatchStLoc(switchValueVar, out var switchValue)) { }
// in case of legacy code there are two stlocs:
// stloc switchValueVar(ldloc switchValue)
// stloc otherSwitchValueVar(ldloc switchValueVar)
// if (comp(ldloc switchValueVar == ldnull)) br nullCase
else if (i > 1 && instructions[i - 2].MatchStLoc(switchValueVar, out switchValue) &&
instructions[i - 1].MatchStLoc(out var otherSwitchValueVar, out var switchValueCopyInst) &&
switchValueCopyInst.MatchLdLoc(switchValueVar) && otherSwitchValueVar.IsSingleDefinition && switchValueVar.LoadCount == 2)
{
extraLoad = true;
if(!instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) {
switchValue = new LdLoc(switchValueVar);
}
} else {
// match null check with different variable:
// this is used by the old C# compiler.
if (MatchCompEqualsNull(condition, out var otherSwitchValueVar)) {
values.Add((null, firstBlock));
} else {
return false;
}
// in case of optimized legacy code there are two stlocs:
// stloc otherSwitchValueVar(ldloc switchValue)
// stloc switchValueVar(ldloc otherSwitchValueVar)
// if (comp(ldloc otherSwitchValueVar == ldnull)) br nullCase
if (i > 1 && otherSwitchValueVar != null && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue)
&& instructions[i - 1].MatchStLoc(out switchValueVar, out var switchValueCopyInst)
&& switchValueCopyInst.MatchLdLoc(otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) {
extraLoad = true;
} else if (instructions[i - 1].MatchStLoc(out switchValueVar, out switchValue)) {
// unoptimized legacy switch
}
} }
else return false;
// if instruction must be followed by a branch to the next case // if instruction must be followed by a branch to the next case
if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump))
return false; return false;
// extract all cases and add them to the values list. // extract all cases and add them to the values list.
Block currentCaseBlock = nextCaseJump.TargetBlock; Block currentCaseBlock = nextCaseJump.TargetBlock;
Block nextCaseBlock; Block nextCaseBlock;
while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, ref switchValueVar, out string value, out Block block)) != null) {
while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out value, out Block block)) != null) {
values.Add((value, block)); values.Add((value, block));
currentCaseBlock = nextCaseBlock; currentCaseBlock = nextCaseBlock;
} }
@ -169,13 +175,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// 1. block: /// 1. block:
/// if (call op_Equality(ldloc switchVariable, ldstr value)) br caseBlock /// if (call op_Equality(ldloc switchVariable, ldstr value)) br caseBlock
/// br nextBlock /// br nextBlock
/// -or-
/// if (comp(ldloc switchValueVar == ldnull)) br nextBlock
/// br caseBlock
/// 2. block is caseBlock
/// This method matches the above pattern or its inverted form: /// This method matches the above pattern or its inverted form:
/// the call to ==(string, string) is wrapped in logic.not and the branch targets are reversed. /// the call to ==(string, string) is wrapped in logic.not and the branch targets are reversed.
/// Returns the next block that follows in the block-chain. /// Returns the next block that follows in the block-chain.
/// The <paramref name="switchVariable"/> is updated if the value gets copied to a different variable. /// The <paramref name="switchVariable"/> is updated if the value gets copied to a different variable.
/// See comments below for more info. /// See comments below for more info.
/// </summary> /// </summary>
Block MatchCaseBlock(Block currentBlock, ref ILVariable switchVariable, out string value, out Block caseBlock)
Block MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock)
{ {
value = null; value = null;
caseBlock = null; caseBlock = null;
@ -196,19 +206,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!currentBlock.Instructions[1].MatchBranch(out nextBlock)) if (!currentBlock.Instructions[1].MatchBranch(out nextBlock))
return null; return null;
} }
// Sometimes the switch pattern uses one variable at the beginning for null checks
// and another variable for the if-else-if-else-pattern.
// both variables must be only assigned once and be of the type: System.String.
if (!MatchStringEqualityComparison(condition, out var newSwitchVariable, out value))
return null;
if (!newSwitchVariable.IsSingleDefinition)
return null;
// if the used variable differs and both variables are not related, return null:
if (switchVariable != newSwitchVariable && !(IsInitializedBy(switchVariable, newSwitchVariable) || IsInitializedBy(newSwitchVariable, switchVariable)))
return null;
if (!newSwitchVariable.Type.IsKnownType(KnownTypeCode.String))
if (!MatchStringEqualityComparison(condition, out var v, out value)) {
if (MatchCompEqualsNull(condition, out v)) {
value = null;
} else {
return null;
}
}
if (v != switchVariable)
return null; return null;
switchVariable = newSwitchVariable;
return nextBlock; return nextBlock;
} }
@ -482,8 +488,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!bodyBranch.MatchBranch(out Block body)) if (!bodyBranch.MatchBranch(out Block body))
return false; return false;
if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) {
if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) {
if (!MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out string stringValue)) {
if (condition.MatchLogicNot(out condition) && MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out stringValue)) {
if (!target.Instructions[1].MatchBranch(out Block exit)) if (!target.Instructions[1].MatchBranch(out Block exit))
return false; return false;
body = exit; body = exit;
@ -520,9 +526,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue)
bool MatchStringEqualityOrNullComparison(ILInstruction condition, ILVariable variable, out string stringValue)
{ {
return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable;
if (!MatchStringEqualityComparison(condition, out var v, out stringValue)) {
if (!MatchCompEqualsNull(condition, out v))
return false;
stringValue = null;
}
return v == variable;
} }
bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue) bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue)
@ -535,9 +547,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
right = c.Arguments[1]; right = c.Arguments[1];
if (!right.MatchLdStr(out stringValue)) if (!right.MatchLdStr(out stringValue))
return false; return false;
} else if (condition.MatchCompEquals(out left, out right) && right.MatchLdNull()) {
} else return false;
} else {
return false;
}
return left.MatchLdLoc(out variable); return left.MatchLdLoc(out variable);
} }
bool MatchCompEqualsNull(ILInstruction condition, out ILVariable variable)
{
variable = null;
if (!condition.MatchCompEquals(out var left, out var right))
return false;
return right.MatchLdNull() && left.MatchLdLoc(out variable);
}
} }
} }
Loading…
Cancel
Save