Browse Source

Peephole detection of the ternary operator (?:)

pull/70/head
David Srbecký 15 years ago
parent
commit
05b0b427d6
  1. 6
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 132
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 1
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  4. 8
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

6
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -285,6 +285,12 @@ namespace Decompiler
new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)
} }
}; };
case ILCode.TernaryOp:
return new Ast.ConditionalExpression() {
Condition = MakeBranchCondition(byteCode.Arguments[0]),
TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]),
FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]),
};
} }
List<Ast.Expression> args = TransformExpressionArguments(byteCode); List<Ast.Expression> args = TransformExpressionArguments(byteCode);

132
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -12,7 +12,7 @@ namespace Decompiler.ControlFlow
public enum ILAstOptimizationStep public enum ILAstOptimizationStep
{ {
SplitToMovableBlocks, SplitToMovableBlocks,
ShortCircuits, PeepholeOptimizations,
FindLoops, FindLoops,
FindConditions, FindConditions,
FlattenNestedMovableBlocks, FlattenNestedMovableBlocks,
@ -34,8 +34,11 @@ namespace Decompiler.ControlFlow
SplitToBasicBlocks(block); SplitToBasicBlocks(block);
} }
if (abortBeforeStep == ILAstOptimizationStep.ShortCircuits) return; if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return;
OptimizeShortCircuits(method); AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
PeepholeOptimizations(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
@ -132,29 +135,21 @@ namespace Decompiler.ControlFlow
} }
} }
foreach (ILBasicBlock bb in basicBlocks) {
if (bb.Body.Count > 0 &&
bb.Body.Last() is ILExpression &&
((ILExpression)bb.Body.Last()).Code == ILCode.Br)
{
Debug.Assert(bb.FallthoughGoto == null);
bb.FallthoughGoto = (ILExpression)bb.Body.Last();
bb.Body.RemoveAt(bb.Body.Count - 1);
}
}
block.Body = basicBlocks; block.Body = basicBlocks;
return; return;
} }
void OptimizeShortCircuits(ILBlock method)
{
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
bool modified;
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
} else {
i++;
}
}
} while(modified);
}
}
Dictionary<ILLabel, int> labelGlobalRefCount; Dictionary<ILLabel, int> labelGlobalRefCount;
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock; Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock;
@ -175,11 +170,34 @@ namespace Decompiler.ControlFlow
} }
} }
void PeepholeOptimizations(ILBlock block)
{
bool modified;
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
continue;
}
if (TrySimplifyTernaryOperator(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
continue;
}
i++;
}
} while(modified);
}
bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel) bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel)
{ {
if (bb.Body.Count == 1) { if (bb.Body.Count == 1) {
branchExpr = bb.Body[0] as ILExpression; branchExpr = bb.Body[0] as ILExpression;
if (branchExpr != null && branchExpr.Operand is ILLabel && branchExpr.Arguments.Count > 0) { if (branchExpr != null &&
branchExpr.Operand is ILLabel &&
branchExpr.Arguments.Count > 0 &&
branchExpr.Prefixes == null)
{
trueLabel = (ILLabel)branchExpr.Operand; trueLabel = (ILLabel)branchExpr.Operand;
falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand; falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand;
return true; return true;
@ -188,6 +206,71 @@ namespace Decompiler.ControlFlow
return false; return false;
} }
bool IsStloc(ILBasicBlock bb, ref ILVariable locVar, ref ILExpression val, ref ILLabel fallLabel)
{
if (bb.Body.Count == 1) {
ILExpression expr = bb.Body[0] as ILExpression;
if (expr != null &&
expr.Code == ILCode.Stloc &&
expr.Prefixes == null)
{
locVar = (ILVariable)expr.Operand;
val = expr.Arguments[0];
fallLabel = (ILLabel)bb.FallthoughGoto.Operand;
return true;
}
}
return false;
}
// scope is modified if successful
bool TrySimplifyTernaryOperator(List<ILNode> scope, ILBasicBlock head)
{
Debug.Assert(scope.Contains(head));
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
ILVariable trueLocVar = null;
ILExpression trueExpr = null;
ILLabel trueFall = null;
ILVariable falseLocVar = null;
ILExpression falseExpr = null;
ILLabel falseFall = null;
if(IsConditionalBranch(head, ref branchExpr, ref trueLabel, ref falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
IsStloc(labelToBasicBlock[trueLabel], ref trueLocVar, ref trueExpr, ref trueFall) &&
IsStloc(labelToBasicBlock[falseLabel], ref falseLocVar, ref falseExpr, ref falseFall) &&
trueLocVar == falseLocVar &&
trueFall == falseFall)
{
// Create the ternary expression
head.Body = new List<ILNode>() {
new ILExpression(ILCode.Stloc, trueLocVar,
new ILExpression(ILCode.TernaryOp, null,
new ILExpression(branchExpr.Code, null, branchExpr.Arguments.ToArray()),
trueExpr,
falseExpr
)
)
};
head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall);
// Remove the old basic blocks
scope.Remove(labelToBasicBlock[trueLabel]);
scope.Remove(labelToBasicBlock[falseLabel]);
labelToBasicBlock.Remove(trueLabel);
labelToBasicBlock.Remove(falseLabel);
labelGlobalRefCount.Remove(trueLabel);
labelGlobalRefCount.Remove(falseLabel);
return true;
}
return false;
}
// scope is modified if successful // scope is modified if successful
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head) bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head)
{ {
@ -228,7 +311,8 @@ namespace Decompiler.ControlFlow
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
// Remove the inlined branch from scope // Remove the inlined branch from scope
labelGlobalRefCount[nextBasicBlock.EntryLabel] = 0; labelGlobalRefCount.Remove(nextBasicBlock.EntryLabel);
labelToBasicBlock.Remove(nextBasicBlock.EntryLabel);
if (!scope.Remove(nextBasicBlock)) if (!scope.Remove(nextBasicBlock))
throw new Exception("Element not found"); throw new Exception("Element not found");

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -259,6 +259,7 @@ namespace Decompiler
BrLogicAnd, BrLogicAnd,
BrLogicOr, BrLogicOr,
InitArray, // Array Initializer InitArray, // Array Initializer
TernaryOp, // ?:
Pattern // used for ILAst pattern nodes Pattern // used for ILAst pattern nodes
} }

8
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -119,6 +119,14 @@ namespace Decompiler
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
} }
return null; return null;
case ILCode.TernaryOp:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
}
return TypeWithMoreInformation(
InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren),
InferTypeForExpression(expr.Arguments[2], expectedType, forceInferChildren)
);
#endregion #endregion
#region Variable load/store #region Variable load/store
case ILCode.Stloc: case ILCode.Stloc:

Loading…
Cancel
Save