mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
13 KiB
353 lines
13 KiB
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// 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. |
|
|
|
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.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && |
|
labelGlobalRefCount[trueLabel] == 1 && |
|
labelGlobalRefCount[falseLabel] == 1 && |
|
((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && |
|
labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && |
|
trueLocVar == falseLocVar && trueFall == falseFall) || |
|
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && |
|
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && |
|
body.Contains(labelToBasicBlock[trueLabel]) && |
|
body.Contains(labelToBasicBlock[falseLabel]) |
|
) |
|
{ |
|
bool isStloc = trueLocVar != null; |
|
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; |
|
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; |
|
bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType); |
|
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 (retTypeIsBoolean && |
|
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 (retTypeIsBoolean && 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 (retTypeIsBoolean && 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.RemoveTail(ILCode.Brtrue, ILCode.Br); |
|
head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); |
|
if (isStloc) |
|
head.Body.Add(new ILExpression(ILCode.Br, trueFall)); |
|
|
|
// 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 >= 3 && |
|
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && |
|
leftExpr.Match(ILCode.Ldloc, out leftVar) && |
|
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && |
|
leftExpr2.MatchLdloc(leftVar) && |
|
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && |
|
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && |
|
v == v2 && |
|
endBBLabel == endBBLabel2 && |
|
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && |
|
body.Contains(rightBB) |
|
) |
|
{ |
|
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); |
|
head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); |
|
head.Body.Add(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.MatchLastAndBr(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[(ILLabel)nextBasicBlock.Body.First()] == 1 && |
|
nextBasicBlock.MatchSingleAndBr(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.RemoveTail(ILCode.Brtrue, ILCode.Br); |
|
head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr)); |
|
head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel)); |
|
|
|
// Remove the inlined branch from scope |
|
body.RemoveOrThrow(nextBasicBlock); |
|
|
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) |
|
{ |
|
Debug.Assert(body.Contains(head)); |
|
|
|
// --- looking for the following pattern --- |
|
// stloc(targetVar, leftVar) |
|
// brtrue(exitLabel, call(op_False, leftVar) |
|
// br(followingBlock) |
|
// |
|
// FollowingBlock: |
|
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) |
|
// br(exitLabel) |
|
// --- |
|
|
|
if (head.Body.Count < 3) |
|
return false; |
|
|
|
// looking for: |
|
// stloc(targetVar, leftVar) |
|
ILVariable targetVar; |
|
ILExpression targetVarInitExpr; |
|
if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) |
|
return false; |
|
|
|
// looking for: |
|
// brtrue(exitLabel, call(op_False, leftVar) |
|
// br(followingBlock) |
|
ILExpression callExpr; |
|
ILLabel exitLabel; |
|
ILLabel followingBlock; |
|
if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) |
|
return false; |
|
|
|
MethodDefinition opFalse; |
|
ILExpression leftVar; |
|
if (!callExpr.Match(ILCode.Call, out opFalse, out leftVar)) |
|
return false; |
|
|
|
if (!leftVar.MatchLdloc(targetVarInitExpr.Operand as ILVariable)) |
|
return false; |
|
|
|
ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; |
|
|
|
// FollowingBlock: |
|
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) |
|
// br(exitLabel) |
|
ILVariable _targetVar; |
|
ILExpression opBitwiseCallExpr; |
|
ILLabel _exitLabel; |
|
if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) |
|
return false; |
|
|
|
if (_targetVar != targetVar) |
|
return false; |
|
|
|
MethodDefinition opBitwise; |
|
ILExpression leftVarExpression; |
|
ILExpression rightExpression; |
|
if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) |
|
return false; |
|
|
|
if (!leftVar.MatchLdloc(leftVarExpression.Operand as ILVariable)) |
|
return false; |
|
|
|
// insert: |
|
// stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) |
|
// br(exitLabel) |
|
ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr, leftVar, rightExpression); |
|
shortCircuitExpr.Operand = opBitwise; |
|
|
|
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); |
|
head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); |
|
head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); |
|
body.Remove(followingBasicBlock); |
|
|
|
return true; |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos) |
|
{ |
|
ILLabel nextLabel; |
|
ILBasicBlock nextBB; |
|
if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && |
|
head.Body.Last().Match(ILCode.Br, out nextLabel) && |
|
labelGlobalRefCount[nextLabel] == 1 && |
|
labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && |
|
body.Contains(nextBB) && |
|
nextBB.Body.First() == nextLabel && |
|
!nextBB.Body.OfType<ILTryCatchBlock>().Any() |
|
) |
|
{ |
|
head.Body.RemoveTail(ILCode.Br); |
|
nextBB.Body.RemoveAt(0); // Remove label |
|
head.Body.AddRange(nextBB.Body); |
|
|
|
body.RemoveOrThrow(nextBB); |
|
return true; |
|
} |
|
return false; |
|
} |
|
} |
|
}
|
|
|