Browse Source

Improve yield-return-decompiler.

pull/734/merge
Daniel Grunwald 9 years ago
parent
commit
bddda342a1
  1. 8
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 18
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  3. 23
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  4. 9
      ICSharpCode.Decompiler/IL/ILVariable.cs
  5. 63
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  6. 1
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  7. 11
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  8. 1
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

8
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -127,10 +127,10 @@ namespace ICSharpCode.Decompiler.CSharp
return cexpr; return cexpr;
} }
public TranslatedExpression TranslateCondition(ILInstruction condition) public TranslatedExpression TranslateCondition(ILInstruction condition, bool negate = false)
{ {
var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean)); var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean));
return expr.ConvertToBoolean(this); return expr.ConvertToBoolean(this, negate);
} }
ExpressionWithResolveResult ConvertVariable(ILVariable variable) ExpressionWithResolveResult ConvertVariable(ILVariable variable)
@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLogicNot(LogicNot inst, TranslationContext context) protected internal override TranslatedExpression VisitLogicNot(LogicNot inst, TranslationContext context)
{ {
return LogicNot(TranslateCondition(inst.Argument)).WithILInstruction(inst); return TranslateCondition(inst.Argument, negate: true).WithILInstruction(inst);
} }
protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context)
@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst); .WithILInstruction(inst);
} }
ExpressionWithResolveResult LogicNot(TranslatedExpression expr) internal ExpressionWithResolveResult LogicNot(TranslatedExpression expr)
{ {
return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression) return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression)
.WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not, expr.ResolveResult)); .WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not, expr.ResolveResult));

18
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -311,21 +311,29 @@ namespace ICSharpCode.Decompiler.CSharp
/// ///
/// Expects that the input expression is an integer expression; produces an expression /// Expects that the input expression is an integer expression; produces an expression
/// that returns <c>true</c> iff the integer value is not 0. /// that returns <c>true</c> iff the integer value is not 0.
///
/// If negate is true, instead produces an expression that returns <c>true</c> iff the integer value is 0.
/// </summary> /// </summary>
public TranslatedExpression ConvertToBoolean(ExpressionBuilder expressionBuilder) public TranslatedExpression ConvertToBoolean(ExpressionBuilder expressionBuilder, bool negate = false)
{ {
if (Type.IsKnownType(KnownTypeCode.Boolean) || Type.Kind == TypeKind.Unknown) { if (Type.IsKnownType(KnownTypeCode.Boolean) || Type.Kind == TypeKind.Unknown) {
return this; if (negate) {
return expressionBuilder.LogicNot(this).WithoutILInstruction();
} else {
return this;
}
} }
Debug.Assert(Type.GetStackType().IsIntegerType()); Debug.Assert(Type.GetStackType().IsIntegerType());
IType boolType = expressionBuilder.compilation.FindType(KnownTypeCode.Boolean); IType boolType = expressionBuilder.compilation.FindType(KnownTypeCode.Boolean);
if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is int) { if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is int) {
bool val = (int)ResolveResult.ConstantValue != 0; bool val = (int)ResolveResult.ConstantValue != 0;
val ^= negate;
return new PrimitiveExpression(val) return new PrimitiveExpression(val)
.WithILInstruction(this.ILInstructions) .WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(boolType, val)); .WithRR(new ConstantResolveResult(boolType, val));
} else if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is byte) { } else if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is byte) {
bool val = (byte)ResolveResult.ConstantValue != 0; bool val = (byte)ResolveResult.ConstantValue != 0;
val ^= negate;
return new PrimitiveExpression(val) return new PrimitiveExpression(val)
.WithILInstruction(this.ILInstructions) .WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(boolType, val)); .WithRR(new ConstantResolveResult(boolType, val));
@ -333,7 +341,8 @@ namespace ICSharpCode.Decompiler.CSharp
var nullRef = new NullReferenceExpression() var nullRef = new NullReferenceExpression()
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new ConstantResolveResult(SpecialType.NullType, null)); .WithRR(new ConstantResolveResult(SpecialType.NullType, null));
return new BinaryOperatorExpression(Expression, BinaryOperatorType.InEquality, nullRef.Expression) var op = negate ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality;
return new BinaryOperatorExpression(Expression, op, nullRef.Expression)
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual, .WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual,
this.ResolveResult, nullRef.ResolveResult)); this.ResolveResult, nullRef.ResolveResult));
@ -341,7 +350,8 @@ namespace ICSharpCode.Decompiler.CSharp
var zero = new PrimitiveExpression(0) var zero = new PrimitiveExpression(0)
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new ConstantResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.Int32), 0)); .WithRR(new ConstantResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.Int32), 0));
return new BinaryOperatorExpression(Expression, BinaryOperatorType.InEquality, zero.Expression) var op = negate ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality;
return new BinaryOperatorExpression(Expression, op, zero.Expression)
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual, .WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual,
this.ResolveResult, zero.ResolveResult)); this.ResolveResult, zero.ResolveResult));

23
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -134,6 +134,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
DecompileFinallyBlocks(); DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(newBody); ReconstructTryFinallyBlocks(newBody);
// Copy-propagate temporaries holding a copy of 'this'.
// This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables.
foreach (var stloc in function.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
context.Step("Translate fields to local accesses", function); context.Step("Translate fields to local accesses", function);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap); TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
@ -141,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Remove temporaries", function); context.Step("Remove temporaries", function);
foreach (var store in returnStores) { foreach (var store in returnStores) {
if (store.Variable.LoadCount == 0 && store.Variable.AddressCount == 0 && store.Parent is Block block) { if (store.Variable.LoadCount == 0 && store.Variable.AddressCount == 0 && store.Parent is Block block) {
Debug.Assert(SemanticHelper.IsPure(store.Value.Flags));
block.Instructions.Remove(store); block.Instructions.Remove(store);
} }
} }
@ -589,15 +596,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
name = fieldDef.Name.Substring(1, pos - 1); name = fieldDef.Name.Substring(1, pos - 1);
} }
v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name); v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name);
v.StateMachineField = ldflda.Field;
fieldToVariableMap.Add(fieldDef, v); fieldToVariableMap.Add(fieldDef, v);
} }
inst.ReplaceWith(new LdLoca(v)); inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange });
} else if (inst.MatchLdThis()) { } else if (inst.MatchLdThis()) {
inst.ReplaceWith(new InvalidExpression("iterator") { ExpectedResultType = inst.ResultType }); inst.ReplaceWith(new InvalidExpression("stateMachine") { ExpectedResultType = inst.ResultType, ILRange = inst.ILRange });
} else { } else {
foreach (var child in inst.Children) { foreach (var child in inst.Children) {
TranslateFieldsToLocalAccess(function, child, fieldToVariableMap); TranslateFieldsToLocalAccess(function, child, fieldToVariableMap);
} }
if (inst is LdObj ldobj && ldobj.Target is LdLoca ldloca && ldloca.Variable.StateMachineField != null) {
LdLoc ldloc = new LdLoc(ldloca.Variable);
ldloc.AddILRange(ldobj.ILRange);
ldloc.AddILRange(ldloca.ILRange);
inst.ReplaceWith(ldloc);
} else if (inst is StObj stobj && stobj.Target is LdLoca ldloca2 && ldloca2.Variable.StateMachineField != null) {
StLoc stloc = new StLoc(ldloca2.Variable, stobj.Value);
stloc.AddILRange(stobj.ILRange);
stloc.AddILRange(ldloca2.ILRange);
inst.ReplaceWith(stloc);
}
} }
} }
#endregion #endregion

9
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -239,6 +239,12 @@ namespace ICSharpCode.Decompiler.IL
return StoreCount == 1 && AddressCount == 0; return StoreCount == 1 && AddressCount == 0;
} }
} }
/// <summary>
/// The field which was converted to a local variable.
/// Set when the variable is from a 'yield return' or 'async' state machine.
/// </summary>
public IField StateMachineField;
public ILVariable(VariableKind kind, IType type, int index) public ILVariable(VariableKind kind, IType type, int index)
{ {
@ -302,6 +308,9 @@ namespace ICSharpCode.Decompiler.IL
if (CaptureScope != null) { if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label); output.Write(" captured in " + CaptureScope.EntryPoint.Label);
} }
if (StateMachineField != null) {
output.Write(" from state-machine");
}
} }
internal void WriteTo(ITextOutput output) internal void WriteTo(ITextOutput output)

63
ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

@ -18,6 +18,8 @@
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
@ -31,6 +33,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
public class CopyPropagation : IBlockTransform public class CopyPropagation : IBlockTransform
{ {
public static void Propagate(StLoc store, ILTransformContext context)
{
Debug.Assert(store.Variable.IsSingleDefinition);
Block block = (Block)store.Parent;
int i = store.ChildIndex;
DoPropagate(store.Variable, store.Value, block, ref i, context);
}
public void Run(Block block, BlockTransformContext context) public void Run(Block block, BlockTransformContext context)
{ {
for (int i = 0; i < block.Instructions.Count; i++) { for (int i = 0; i < block.Instructions.Count; i++) {
@ -38,31 +48,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction copiedExpr; ILInstruction copiedExpr;
if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) { if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) {
if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) { if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) {
context.Step($"Copy propagate {v.Name}", copiedExpr); DoPropagate(v, copiedExpr, block, ref i, context);
// un-inline the arguments of the ldArg instruction
ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) {
var arg = copiedExpr.Children[j];
var type = context.TypeSystem.Compilation.FindType(arg.ResultType.ToKnownTypeCode());
uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType, arg.ILRange.Start) {
Name = "C_" + arg.ILRange.Start
};
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
}
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs);
// perform copy propagation:
foreach (var expr in v.Function.Descendants) {
if (expr.MatchLdLoc(v)) {
var clone = copiedExpr.Clone();
for (int j = 0; j < uninlinedArgs.Length; j++) {
clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j]));
}
expr.ReplaceWith(clone);
}
}
block.Instructions.RemoveAt(i);
int c = ILInlining.InlineInto(block, i, aggressive: false, context: context);
i -= c + 1;
} }
} }
} }
@ -99,6 +85,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return value.Flags == InstructionFlags.None && value.Children.Count == 0 && target.Kind == VariableKind.StackSlot; return value.Flags == InstructionFlags.None && value.Children.Count == 0 && target.Kind == VariableKind.StackSlot;
} }
} }
static void DoPropagate(ILVariable v, ILInstruction copiedExpr, Block block, ref int i, ILTransformContext context)
{
context.Step($"Copy propagate {v.Name}", copiedExpr);
// un-inline the arguments of the ldArg instruction
ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) {
var arg = copiedExpr.Children[j];
var type = context.TypeSystem.Compilation.FindType(arg.ResultType.ToKnownTypeCode());
uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType, arg.ILRange.Start) {
Name = "C_" + arg.ILRange.Start
};
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
}
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs);
// perform copy propagation:
foreach (var expr in v.LoadInstructions.ToArray()) {
var clone = copiedExpr.Clone();
for (int j = 0; j < uninlinedArgs.Length; j++) {
clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j]));
}
expr.ReplaceWith(clone);
}
block.Instructions.RemoveAt(i);
int c = ILInlining.InlineInto(block, i, aggressive: false, context: context);
i -= c + 1;
}
} }
} }

1
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -224,6 +224,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
context.Step("stobj(ldloca(v), ...) => stloc(v, ...)", inst); context.Step("stobj(ldloca(v), ...) => stloc(v, ...)", inst);
inst.ReplaceWith(new StLoc(v, inst.Value)); inst.ReplaceWith(new StLoc(v, inst.Value));
return;
} }
ILInstruction target; ILInstruction target;

11
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} else { } else {
Debug.Assert(loadInst.OpCode == OpCode.LdLoc); Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression)) if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
return false; return false;
} }
@ -254,7 +254,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <param name="next">The next top-level expression</param> /// <param name="next">The next top-level expression</param>
/// <param name="loadInst">The load within 'next'</param> /// <param name="loadInst">The load within 'next'</param>
/// <param name="inlinedExpression">The expression being inlined</param> /// <param name="inlinedExpression">The expression being inlined</param>
static bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression) static bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression, ILVariable v)
{ {
Debug.Assert(loadInst.IsDescendantOf(next)); Debug.Assert(loadInst.IsDescendantOf(next));
@ -266,6 +266,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
case OpCode.CompoundAssignmentInstruction: case OpCode.CompoundAssignmentInstruction:
return true; return true;
case OpCode.LdLoc:
if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) {
// Roslyn likes to put the result of fetching a state machine field into a temporary variable,
// so inline more aggressively in such cases.
return true;
}
break;
} }
// decide based on the target into which we are inlining // decide based on the target into which we are inlining

1
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -96,6 +96,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!newVariables.TryGetValue(representative, out v)) { if (!newVariables.TryGetValue(representative, out v)) {
v = new ILVariable(inst.Variable.Kind, inst.Variable.Type, inst.Variable.StackType, inst.Variable.Index); v = new ILVariable(inst.Variable.Kind, inst.Variable.Type, inst.Variable.StackType, inst.Variable.Index);
v.Name = inst.Variable.Name; v.Name = inst.Variable.Name;
v.StateMachineField = inst.Variable.StateMachineField;
v.HasInitialValue = false; // we'll set HasInitialValue when we encounter an uninit load v.HasInitialValue = false; // we'll set HasInitialValue when we encounter an uninit load
newVariables.Add(representative, v); newVariables.Add(representative, v);
inst.Variable.Function.Variables.Add(v); inst.Variable.Function.Variables.Add(v);

Loading…
Cancel
Save