mirror of https://github.com/icsharpcode/ILSpy.git
3 changed files with 237 additions and 224 deletions
@ -0,0 +1,233 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
using Mono.Cecil; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.ILAst |
||||||
|
{ |
||||||
|
public class SimpleControlFlow |
||||||
|
{ |
||||||
|
Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>(); |
||||||
|
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>(); |
||||||
|
|
||||||
|
DecompilerContext context; |
||||||
|
TypeSystem typeSystem; |
||||||
|
|
||||||
|
public SimpleControlFlow(DecompilerContext context, ILBlock method) |
||||||
|
{ |
||||||
|
this.context = context; |
||||||
|
this.typeSystem = context.CurrentMethod.Module.TypeSystem; |
||||||
|
|
||||||
|
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { |
||||||
|
labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; |
||||||
|
} |
||||||
|
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) { |
||||||
|
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) { |
||||||
|
labelToBasicBlock[label] = bb; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) |
||||||
|
{ |
||||||
|
Debug.Assert(body.Contains(head)); |
||||||
|
|
||||||
|
ILExpression condExpr; |
||||||
|
ILLabel trueLabel; |
||||||
|
ILLabel falseLabel; |
||||||
|
ILVariable trueLocVar = null; |
||||||
|
ILExpression trueExpr; |
||||||
|
ILLabel trueFall; |
||||||
|
ILVariable falseLocVar = null; |
||||||
|
ILExpression falseExpr; |
||||||
|
ILLabel falseFall; |
||||||
|
object unused; |
||||||
|
|
||||||
|
if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && |
||||||
|
labelGlobalRefCount[trueLabel] == 1 && |
||||||
|
labelGlobalRefCount[falseLabel] == 1 && |
||||||
|
((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && |
||||||
|
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || |
||||||
|
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) && |
||||||
|
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) && |
||||||
|
trueLocVar == falseLocVar && |
||||||
|
trueFall == falseFall && |
||||||
|
body.Contains(labelToBasicBlock[trueLabel]) && |
||||||
|
body.Contains(labelToBasicBlock[falseLabel]) |
||||||
|
) |
||||||
|
{ |
||||||
|
ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; |
||||||
|
TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; |
||||||
|
int leftBoolVal; |
||||||
|
int rightBoolVal; |
||||||
|
ILExpression newExpr; |
||||||
|
// a ? true:false is equivalent to a
|
||||||
|
// a ? false:true is equivalent to !a
|
||||||
|
// a ? true : b is equivalent to a || b
|
||||||
|
// a ? b : true is equivalent to !a || b
|
||||||
|
// a ? b : false is equivalent to a && b
|
||||||
|
// a ? false : b is equivalent to !a && b
|
||||||
|
if (retType == typeSystem.Boolean && |
||||||
|
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && |
||||||
|
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && |
||||||
|
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) |
||||||
|
) |
||||||
|
{ |
||||||
|
// It can be expressed as trivilal expression
|
||||||
|
if (leftBoolVal != 0) { |
||||||
|
newExpr = condExpr; |
||||||
|
} else { |
||||||
|
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); |
||||||
|
} |
||||||
|
} else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { |
||||||
|
// It can be expressed as logical expression
|
||||||
|
if (leftBoolVal != 0) { |
||||||
|
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); |
||||||
|
} else { |
||||||
|
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); |
||||||
|
} |
||||||
|
} else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { |
||||||
|
// It can be expressed as logical expression
|
||||||
|
if (rightBoolVal != 0) { |
||||||
|
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); |
||||||
|
} else { |
||||||
|
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Ternary operator tends to create long complicated return statements
|
||||||
|
if (opCode == ILCode.Ret) |
||||||
|
return false; |
||||||
|
|
||||||
|
// Only simplify generated variables
|
||||||
|
if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) |
||||||
|
return false; |
||||||
|
|
||||||
|
// Create ternary expression
|
||||||
|
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); |
||||||
|
} |
||||||
|
|
||||||
|
head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr); |
||||||
|
head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null; |
||||||
|
|
||||||
|
// Remove the old basic blocks
|
||||||
|
body.RemoveOrThrow(labelToBasicBlock[trueLabel]); |
||||||
|
body.RemoveOrThrow(labelToBasicBlock[falseLabel]); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) |
||||||
|
{ |
||||||
|
// ...
|
||||||
|
// v = ldloc(leftVar)
|
||||||
|
// brtrue(endBBLabel, ldloc(leftVar))
|
||||||
|
// br(rightBBLabel)
|
||||||
|
//
|
||||||
|
// rightBBLabel:
|
||||||
|
// v = rightExpr
|
||||||
|
// br(endBBLabel)
|
||||||
|
// ...
|
||||||
|
// =>
|
||||||
|
// ...
|
||||||
|
// v = NullCoalescing(ldloc(leftVar), rightExpr)
|
||||||
|
// br(endBBLabel)
|
||||||
|
|
||||||
|
ILVariable v, v2; |
||||||
|
ILExpression leftExpr, leftExpr2; |
||||||
|
ILVariable leftVar; |
||||||
|
ILLabel endBBLabel, endBBLabel2; |
||||||
|
ILLabel rightBBLabel; |
||||||
|
ILBasicBlock rightBB; |
||||||
|
ILExpression rightExpr; |
||||||
|
if (head.Body.Count >= 2 && |
||||||
|
head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) && |
||||||
|
leftExpr.Match(ILCode.Ldloc, out leftVar) && |
||||||
|
head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && |
||||||
|
leftExpr2.Match(ILCode.Ldloc, leftVar) && |
||||||
|
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && |
||||||
|
rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && |
||||||
|
v == v2 && |
||||||
|
endBBLabel == endBBLabel2 && |
||||||
|
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && |
||||||
|
body.Contains(rightBB) |
||||||
|
) |
||||||
|
{ |
||||||
|
head.Body[head.Body.Count - 2] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); |
||||||
|
head.Body.RemoveAt(head.Body.Count - 1); |
||||||
|
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); |
||||||
|
|
||||||
|
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) |
||||||
|
{ |
||||||
|
Debug.Assert(body.Contains(head)); |
||||||
|
|
||||||
|
ILExpression condExpr; |
||||||
|
ILLabel trueLabel; |
||||||
|
ILLabel falseLabel; |
||||||
|
if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { |
||||||
|
for (int pass = 0; pass < 2; pass++) { |
||||||
|
|
||||||
|
// On the second pass, swap labels and negate expression of the first branch
|
||||||
|
// It is slightly ugly, but much better then copy-pasting this whole block
|
||||||
|
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; |
||||||
|
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; |
||||||
|
bool negate = (pass == 1); |
||||||
|
|
||||||
|
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; |
||||||
|
ILExpression nextCondExpr; |
||||||
|
ILLabel nextTrueLablel; |
||||||
|
ILLabel nextFalseLabel; |
||||||
|
if (body.Contains(nextBasicBlock) && |
||||||
|
nextBasicBlock != head && |
||||||
|
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && |
||||||
|
nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && |
||||||
|
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) |
||||||
|
{ |
||||||
|
// Create short cicuit branch
|
||||||
|
ILExpression logicExpr; |
||||||
|
if (otherLablel == nextFalseLabel) { |
||||||
|
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); |
||||||
|
} else { |
||||||
|
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); |
||||||
|
} |
||||||
|
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr); |
||||||
|
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); |
||||||
|
|
||||||
|
// Remove the inlined branch from scope
|
||||||
|
body.RemoveOrThrow(nextBasicBlock); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) |
||||||
|
{ |
||||||
|
// Assuming that the inputs are already left associative
|
||||||
|
if (right.Match(code)) { |
||||||
|
// Find the leftmost logical expression
|
||||||
|
ILExpression current = right; |
||||||
|
while(current.Arguments[0].Match(code)) |
||||||
|
current = current.Arguments[0]; |
||||||
|
current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]); |
||||||
|
return right; |
||||||
|
} else { |
||||||
|
return new ILExpression(code, null, left, right); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue