mirror of https://github.com/icsharpcode/ILSpy.git
28 changed files with 1125 additions and 155 deletions
@ -0,0 +1,248 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty |
||||||
|
{ |
||||||
|
public class PatternMatching |
||||||
|
{ |
||||||
|
public void SimpleTypePattern(object x) |
||||||
|
{ |
||||||
|
if (x is string value) |
||||||
|
{ |
||||||
|
Console.WriteLine(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternWithShortcircuit(object x) |
||||||
|
{ |
||||||
|
Use(F() && x is string text && text.Contains("a")); |
||||||
|
if (F() && x is string text2 && text2.Contains("a")) |
||||||
|
{ |
||||||
|
Console.WriteLine(text2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternWithShortcircuitAnd(object x) |
||||||
|
{ |
||||||
|
if (x is string text && text.Contains("a")) |
||||||
|
{ |
||||||
|
Console.WriteLine(text); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternWithShortcircuitOr(object x) |
||||||
|
{ |
||||||
|
if (!(x is string text) || text.Contains("a")) |
||||||
|
{ |
||||||
|
Console.WriteLine(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine(text); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternWithShortcircuitOr2(object x) |
||||||
|
{ |
||||||
|
if (F() || !(x is string value)) |
||||||
|
{ |
||||||
|
Console.WriteLine(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesCondition(object x) |
||||||
|
{ |
||||||
|
if (x is int num) |
||||||
|
{ |
||||||
|
Console.WriteLine("Integer: " + num); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesCondition2() |
||||||
|
{ |
||||||
|
if (GetObject() is int num) |
||||||
|
{ |
||||||
|
Console.WriteLine("Integer: " + num); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesWithShortcircuitAnd(object x) |
||||||
|
{ |
||||||
|
if (x is int num && num.GetHashCode() > 0) |
||||||
|
{ |
||||||
|
Console.WriteLine("Positive integer: " + num); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesWithShortcircuitOr(object x) |
||||||
|
{ |
||||||
|
if (!(x is int value) || value.GetHashCode() > 0) |
||||||
|
{ |
||||||
|
Console.WriteLine(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#if ROSLYN3 || OPT
|
||||||
|
// Roslyn 2.x generates a complex infeasible path in debug builds, which RemoveInfeasiblePathTransform
|
||||||
|
// currently cannot handle. Because this would increase the complexity of that transform, we ignore
|
||||||
|
// this case.
|
||||||
|
public void TypePatternValueTypesWithShortcircuitOr2(object x) |
||||||
|
{ |
||||||
|
if (F() || !(x is int value)) |
||||||
|
{ |
||||||
|
Console.WriteLine(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine(value); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif
|
||||||
|
|
||||||
|
public void TypePatternGenerics<T>(object x) |
||||||
|
{ |
||||||
|
if (x is T val) |
||||||
|
{ |
||||||
|
Console.WriteLine(val.GetType().FullName); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("not a " + typeof(T).FullName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternGenericRefType<T>(object x) where T : class |
||||||
|
{ |
||||||
|
if (x is T val) |
||||||
|
{ |
||||||
|
Console.WriteLine(val.GetType().FullName); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("not a " + typeof(T).FullName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternGenericValType<T>(object x) where T : struct |
||||||
|
{ |
||||||
|
if (x is T val) |
||||||
|
{ |
||||||
|
Console.WriteLine(val.GetType().FullName); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("not a " + typeof(T).FullName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesWithShortcircuitAndMultiUse(object x) |
||||||
|
{ |
||||||
|
if (x is int num && num.GetHashCode() > 0 && num % 2 == 0) |
||||||
|
{ |
||||||
|
Console.WriteLine("Positive integer: " + num); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesWithShortcircuitAndMultiUse2(object x) |
||||||
|
{ |
||||||
|
if ((x is int num && num.GetHashCode() > 0 && num % 2 == 0) || F()) |
||||||
|
{ |
||||||
|
Console.WriteLine("true"); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypesWithShortcircuitAndMultiUse3(object x) |
||||||
|
{ |
||||||
|
if (F() || (x is int num && num.GetHashCode() > 0 && num % 2 == 0)) |
||||||
|
{ |
||||||
|
Console.WriteLine("true"); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Console.WriteLine("else"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TypePatternValueTypes() |
||||||
|
{ |
||||||
|
Use(F() && GetObject() is int num && num.GetHashCode() > 0 && num % 2 == 0); |
||||||
|
} |
||||||
|
|
||||||
|
public static void NotTypePatternVariableUsedOutsideTrueBranch(object x) |
||||||
|
{ |
||||||
|
string text = x as string; |
||||||
|
if (text != null && text.Length > 5) |
||||||
|
{ |
||||||
|
Console.WriteLine("pattern matches"); |
||||||
|
} |
||||||
|
if (text != null && text.Length > 10) |
||||||
|
{ |
||||||
|
Console.WriteLine("other use!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void NotTypePatternBecauseVarIsNotDefAssignedInCaseOfFallthrough(object x) |
||||||
|
{ |
||||||
|
#if OPT
|
||||||
|
string obj = x as string; |
||||||
|
if (obj == null) |
||||||
|
{ |
||||||
|
Console.WriteLine("pattern doesn't match"); |
||||||
|
} |
||||||
|
Console.WriteLine(obj == null); |
||||||
|
#else
|
||||||
|
string text = x as string; |
||||||
|
if (text == null) |
||||||
|
{ |
||||||
|
Console.WriteLine("pattern doesn't match"); |
||||||
|
} |
||||||
|
Console.WriteLine(text == null); |
||||||
|
#endif
|
||||||
|
} |
||||||
|
|
||||||
|
private bool F() |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private object GetObject() |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
private void Use(bool x) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,350 @@ |
|||||||
|
// Copyright (c) 2021 Daniel Grunwald, Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#nullable enable |
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.IL.ControlFlow; |
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
using ICSharpCode.Decompiler.Util; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.IL.Transforms |
||||||
|
{ |
||||||
|
class PatternMatchingTransform : IILTransform |
||||||
|
{ |
||||||
|
|
||||||
|
void IILTransform.Run(ILFunction function, ILTransformContext context) |
||||||
|
{ |
||||||
|
if (!context.Settings.PatternMatching) |
||||||
|
return; |
||||||
|
foreach (var container in function.Descendants.OfType<BlockContainer>()) |
||||||
|
{ |
||||||
|
ControlFlowGraph? cfg = null; |
||||||
|
foreach (var block in container.Blocks) |
||||||
|
{ |
||||||
|
if (PatternMatchValueTypes(block, container, context, ref cfg)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (PatternMatchRefTypes(block, container, context, ref cfg)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Block {
|
||||||
|
/// ...
|
||||||
|
/// stloc V(isinst T(testedOperand))
|
||||||
|
/// if (comp.o(ldloc V == ldnull)) br falseBlock
|
||||||
|
/// br trueBlock
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// All other uses of V are in blocks dominated by trueBlock.
|
||||||
|
/// =>
|
||||||
|
/// Block {
|
||||||
|
/// ...
|
||||||
|
/// if (match.type[T].notnull(V = testedOperand)) br trueBlock
|
||||||
|
/// br falseBlock
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - or -
|
||||||
|
///
|
||||||
|
/// Block {
|
||||||
|
/// stloc s(isinst T(testedOperand))
|
||||||
|
/// stloc v(ldloc s)
|
||||||
|
/// if (logic.not(comp.o(ldloc s != ldnull))) br falseBlock
|
||||||
|
/// br trueBlock
|
||||||
|
/// }
|
||||||
|
/// =>
|
||||||
|
/// Block {
|
||||||
|
/// ...
|
||||||
|
/// if (match.type[T].notnull(V = testedOperand)) br trueBlock
|
||||||
|
/// br falseBlock
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// All other uses of V are in blocks dominated by trueBlock.
|
||||||
|
private bool PatternMatchRefTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) |
||||||
|
{ |
||||||
|
if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) |
||||||
|
return false; |
||||||
|
int pos = block.Instructions.Count - 3; |
||||||
|
if (condition.MatchLdLoc(out var conditionVar)) |
||||||
|
{ |
||||||
|
// stloc conditionVar(comp.o(ldloc s == ldnull))
|
||||||
|
// if (logic.not(ldloc conditionVar)) br trueBlock
|
||||||
|
if (pos < 0) |
||||||
|
return false; |
||||||
|
if (!(conditionVar.IsSingleDefinition && conditionVar.LoadCount == 1 |
||||||
|
&& conditionVar.Kind == VariableKind.StackSlot)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!block.Instructions[pos].MatchStLoc(conditionVar, out condition)) |
||||||
|
return false; |
||||||
|
pos--; |
||||||
|
} |
||||||
|
if (condition.MatchCompEqualsNull(out var loadInNullCheck)) |
||||||
|
{ |
||||||
|
ExtensionMethods.Swap(ref trueInst, ref falseInst); |
||||||
|
} |
||||||
|
else if (condition.MatchCompNotEqualsNull(out loadInNullCheck)) |
||||||
|
{ |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!loadInNullCheck.MatchLdLoc(out var s)) |
||||||
|
return false; |
||||||
|
if (!s.IsSingleDefinition) |
||||||
|
return false; |
||||||
|
if (s.Kind is not (VariableKind.Local or VariableKind.StackSlot)) |
||||||
|
return false; |
||||||
|
if (pos < 0) |
||||||
|
return false; |
||||||
|
// stloc V(isinst T(testedOperand))
|
||||||
|
ILInstruction storeToV = block.Instructions[pos]; |
||||||
|
if (!storeToV.MatchStLoc(out var v, out var value)) |
||||||
|
return false; |
||||||
|
if (value.MatchLdLoc(s)) |
||||||
|
{ |
||||||
|
// stloc v(ldloc s)
|
||||||
|
pos--; |
||||||
|
if (!block.Instructions[pos].MatchStLoc(s, out value)) |
||||||
|
return false; |
||||||
|
if (v.Kind is not (VariableKind.Local or VariableKind.StackSlot)) |
||||||
|
return false; |
||||||
|
if (s.LoadCount != 2) |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (v != s) |
||||||
|
return false; |
||||||
|
} |
||||||
|
IType? unboxType; |
||||||
|
if (value is UnboxAny unboxAny) |
||||||
|
{ |
||||||
|
// stloc S(unbox.any T(isinst T(testedOperand)))
|
||||||
|
unboxType = unboxAny.Type; |
||||||
|
value = unboxAny.Argument; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
unboxType = null; |
||||||
|
} |
||||||
|
if (value is not IsInst { Argument: var testedOperand, Type: var type }) |
||||||
|
return false; |
||||||
|
if (type.IsReferenceType != true) |
||||||
|
return false; |
||||||
|
if (!(unboxType == null || type.Equals(unboxType))) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (!v.Type.Equals(type)) |
||||||
|
return false; |
||||||
|
if (!CheckAllUsesDominatedBy(v, container, trueInst, storeToV, loadInNullCheck, context, ref cfg)) |
||||||
|
return false; |
||||||
|
context.Step($"Type pattern matching {v.Name}", block); |
||||||
|
// if (match.type[T].notnull(V = testedOperand)) br trueBlock
|
||||||
|
|
||||||
|
var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; |
||||||
|
|
||||||
|
ifInst.Condition = new MatchInstruction(v, testedOperand) { |
||||||
|
CheckNotNull = true, |
||||||
|
CheckType = true |
||||||
|
}.WithILRange(ifInst.Condition); |
||||||
|
ifInst.TrueInst = trueInst; |
||||||
|
block.Instructions[block.Instructions.Count - 1] = falseInst; |
||||||
|
block.Instructions.RemoveRange(pos, ifInst.ChildIndex - pos); |
||||||
|
v.Kind = VariableKind.PatternLocal; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst, |
||||||
|
ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg) |
||||||
|
{ |
||||||
|
var targetBlock = trueInst as Block; |
||||||
|
if (targetBlock == null && !trueInst.MatchBranch(out targetBlock)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (targetBlock.Parent != container) |
||||||
|
return false; |
||||||
|
if (targetBlock.IncomingEdgeCount != 1) |
||||||
|
return false; |
||||||
|
cfg ??= new ControlFlowGraph(container, context.CancellationToken); |
||||||
|
var targetBlockNode = cfg.GetNode(targetBlock); |
||||||
|
var uses = v.LoadInstructions.Concat<ILInstruction>(v.AddressInstructions) |
||||||
|
.Concat(v.StoreInstructions.Cast<ILInstruction>()); |
||||||
|
foreach (var use in uses) |
||||||
|
{ |
||||||
|
if (use == storeToV || use == loadInNullCheck) |
||||||
|
continue; |
||||||
|
Block? found = null; |
||||||
|
for (ILInstruction? current = use; current != null; current = current.Parent) |
||||||
|
{ |
||||||
|
if (current.Parent == container) |
||||||
|
{ |
||||||
|
found = (Block)current; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (found == null) |
||||||
|
return false; |
||||||
|
var node = cfg.GetNode(found); |
||||||
|
if (!targetBlockNode.Dominates(node)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Block {
|
||||||
|
/// ...
|
||||||
|
/// [stloc temp(ldloc testedOperand)]
|
||||||
|
/// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock
|
||||||
|
/// br unboxBlock
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Block unboxBlock (incoming: 1) {
|
||||||
|
/// stloc V(unbox.any T(ldloc temp))
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// =>
|
||||||
|
/// Block {
|
||||||
|
/// ...
|
||||||
|
/// if (match.type[T].notnull(V = testedOperand)) br unboxBlock
|
||||||
|
/// br falseBlock
|
||||||
|
/// }
|
||||||
|
private bool PatternMatchValueTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) |
||||||
|
{ |
||||||
|
if (!MatchIsInstBlock(block, out var type, out var testedOperand, |
||||||
|
out var unboxBlock, out var falseBlock)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
StLoc? tempStore = block.Instructions.ElementAtOrDefault(block.Instructions.Count - 3) as StLoc; |
||||||
|
if (tempStore == null || !tempStore.Value.MatchLdLoc(testedOperand.Variable)) |
||||||
|
{ |
||||||
|
tempStore = null; |
||||||
|
} |
||||||
|
if (!MatchUnboxBlock(unboxBlock, type, out var unboxOperand, out var v, out var storeToV)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (unboxOperand == testedOperand.Variable) |
||||||
|
{ |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
else if (unboxOperand == tempStore?.Variable) |
||||||
|
{ |
||||||
|
if (!(tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!CheckAllUsesDominatedBy(v, container, unboxBlock, storeToV, null, context, ref cfg)) |
||||||
|
return false; |
||||||
|
context.Step($"PatternMatching with {v.Name}", block); |
||||||
|
var ifInst = (IfInstruction)block.Instructions.SecondToLastOrDefault()!; |
||||||
|
ifInst.Condition = new MatchInstruction(v, testedOperand) { |
||||||
|
CheckNotNull = true, |
||||||
|
CheckType = true |
||||||
|
}; |
||||||
|
((Branch)ifInst.TrueInst).TargetBlock = unboxBlock; |
||||||
|
((Branch)block.Instructions.Last()).TargetBlock = falseBlock; |
||||||
|
unboxBlock.Instructions.RemoveAt(0); |
||||||
|
if (unboxOperand == tempStore?.Variable) |
||||||
|
{ |
||||||
|
block.Instructions.Remove(tempStore); |
||||||
|
} |
||||||
|
// HACK: condition detection uses StartILOffset of blocks to decide which branch of if-else
|
||||||
|
// should become the then-branch. Change the unboxBlock StartILOffset from an offset inside
|
||||||
|
// the pattern matching machinery to an offset belonging to an instruction in the then-block.
|
||||||
|
unboxBlock.SetILRange(unboxBlock.Instructions[0]); |
||||||
|
v.Kind = VariableKind.PatternLocal; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// ...
|
||||||
|
/// if (comp.o(isinst T(ldloc testedOperand) == ldnull)) br falseBlock
|
||||||
|
/// br unboxBlock
|
||||||
|
private bool MatchIsInstBlock(Block block, |
||||||
|
[NotNullWhen(true)] out IType? type, |
||||||
|
[NotNullWhen(true)] out LdLoc? testedOperand, |
||||||
|
[NotNullWhen(true)] out Block? unboxBlock, |
||||||
|
[NotNullWhen(true)] out Block? falseBlock) |
||||||
|
{ |
||||||
|
type = null; |
||||||
|
testedOperand = null; |
||||||
|
unboxBlock = null; |
||||||
|
falseBlock = null; |
||||||
|
if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) |
||||||
|
return false; |
||||||
|
if (condition.MatchCompEqualsNull(out var arg)) |
||||||
|
{ |
||||||
|
ExtensionMethods.Swap(ref trueInst, ref falseInst); |
||||||
|
} |
||||||
|
else if (condition.MatchCompNotEqualsNull(out arg)) |
||||||
|
{ |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!arg.MatchIsInst(out arg, out type)) |
||||||
|
return false; |
||||||
|
testedOperand = arg as LdLoc; |
||||||
|
if (testedOperand == null) |
||||||
|
return false; |
||||||
|
return trueInst.MatchBranch(out unboxBlock) && falseInst.MatchBranch(out falseBlock) |
||||||
|
&& unboxBlock.Parent == block.Parent && falseBlock.Parent == block.Parent; |
||||||
|
} |
||||||
|
|
||||||
|
/// Block unboxBlock (incoming: 1) {
|
||||||
|
/// stloc V(unbox.any T(ldloc testedOperand))
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
private bool MatchUnboxBlock(Block unboxBlock, IType type, [NotNullWhen(true)] out ILVariable? testedOperand, |
||||||
|
[NotNullWhen(true)] out ILVariable? v, [NotNullWhen(true)] out ILInstruction? storeToV) |
||||||
|
{ |
||||||
|
v = null; |
||||||
|
storeToV = null; |
||||||
|
testedOperand = null; |
||||||
|
if (unboxBlock.IncomingEdgeCount != 1) |
||||||
|
return false; |
||||||
|
storeToV = unboxBlock.Instructions[0]; |
||||||
|
if (!storeToV.MatchStLoc(out v, out var value)) |
||||||
|
return false; |
||||||
|
if (!(value.MatchUnboxAny(out var arg, out var t) && t.Equals(type) && arg.MatchLdLoc(out testedOperand))) |
||||||
|
return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,117 @@ |
|||||||
|
// Copyright (c) 2021 Daniel Grunwald, Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#nullable enable |
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.IL.Transforms |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Block IL_0018 (incoming: *) {
|
||||||
|
/// stloc s(ldc.i4 1)
|
||||||
|
/// br IL_0019
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Block IL_0019 (incoming: > 1) {
|
||||||
|
/// if (logic.not(ldloc s)) br IL_0027
|
||||||
|
/// br IL_001d
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// replace br IL_0019 with br IL_0027
|
||||||
|
/// </summary>
|
||||||
|
class RemoveInfeasiblePathTransform : IILTransform |
||||||
|
{ |
||||||
|
void IILTransform.Run(ILFunction function, ILTransformContext context) |
||||||
|
{ |
||||||
|
foreach (var container in function.Descendants.OfType<BlockContainer>()) |
||||||
|
{ |
||||||
|
bool changed = false; |
||||||
|
foreach (var block in container.Blocks) |
||||||
|
{ |
||||||
|
changed |= DoTransform(block, context); |
||||||
|
} |
||||||
|
|
||||||
|
if (changed) |
||||||
|
{ |
||||||
|
container.SortBlocks(deleteUnreachableBlocks: true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private bool DoTransform(Block block, ILTransformContext context) |
||||||
|
{ |
||||||
|
if (!MatchBlock1(block, out var s, out int value, out var br)) |
||||||
|
return false; |
||||||
|
if (!MatchBlock2(br.TargetBlock, s, value, out var exitInst)) |
||||||
|
return false; |
||||||
|
context.Step("RemoveInfeasiblePath", br); |
||||||
|
br.ReplaceWith(exitInst.Clone()); |
||||||
|
s.RemoveIfRedundant = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Block IL_0018 (incoming: *) {
|
||||||
|
// stloc s(ldc.i4 1)
|
||||||
|
// br IL_0019
|
||||||
|
// }
|
||||||
|
private bool MatchBlock1(Block block, [NotNullWhen(true)] out ILVariable? variable, out int constantValue, [NotNullWhen(true)] out Branch? branch) |
||||||
|
{ |
||||||
|
variable = null; |
||||||
|
constantValue = 0; |
||||||
|
branch = null; |
||||||
|
if (block.Instructions.Count != 2) |
||||||
|
return false; |
||||||
|
if (block.Instructions[0] is not StLoc |
||||||
|
{ |
||||||
|
Variable: { Kind: VariableKind.StackSlot } s, |
||||||
|
Value: LdcI4 { Value: 0 or 1 } valueInst |
||||||
|
}) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (block.Instructions[1] is not Branch br) |
||||||
|
return false; |
||||||
|
variable = s; |
||||||
|
constantValue = valueInst.Value; |
||||||
|
branch = br; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Block IL_0019 (incoming: > 1) {
|
||||||
|
// if (logic.not(ldloc s)) br IL_0027
|
||||||
|
// br IL_001d
|
||||||
|
// }
|
||||||
|
bool MatchBlock2(Block block, ILVariable s, int constantValue, [NotNullWhen(true)] out ILInstruction? exitInst) |
||||||
|
{ |
||||||
|
exitInst = null; |
||||||
|
if (block.Instructions.Count != 2) |
||||||
|
return false; |
||||||
|
if (block.IncomingEdgeCount <= 1) |
||||||
|
return false; |
||||||
|
if (!block.MatchIfAtEndOfBlock(out var load, out var trueInst, out var falseInst)) |
||||||
|
return false; |
||||||
|
if (!load.MatchLdLoc(s)) |
||||||
|
return false; |
||||||
|
exitInst = constantValue != 0 ? trueInst : falseInst; |
||||||
|
return exitInst is Branch or Leave { Value: Nop }; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue