Browse Source

Improve yield-return-decompiler.

pull/734/merge
Daniel Grunwald 8 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 @@ -127,10 +127,10 @@ namespace ICSharpCode.Decompiler.CSharp
return cexpr;
}
public TranslatedExpression TranslateCondition(ILInstruction condition)
public TranslatedExpression TranslateCondition(ILInstruction condition, bool negate = false)
{
var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean));
return expr.ConvertToBoolean(this);
return expr.ConvertToBoolean(this, negate);
}
ExpressionWithResolveResult ConvertVariable(ILVariable variable)
@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp
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)
@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst);
}
ExpressionWithResolveResult LogicNot(TranslatedExpression expr)
internal ExpressionWithResolveResult LogicNot(TranslatedExpression expr)
{
return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression)
.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 @@ -311,21 +311,29 @@ namespace ICSharpCode.Decompiler.CSharp
///
/// Expects that the input expression is an integer expression; produces an expression
/// 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>
public TranslatedExpression ConvertToBoolean(ExpressionBuilder expressionBuilder)
public TranslatedExpression ConvertToBoolean(ExpressionBuilder expressionBuilder, bool negate = false)
{
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());
IType boolType = expressionBuilder.compilation.FindType(KnownTypeCode.Boolean);
if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is int) {
bool val = (int)ResolveResult.ConstantValue != 0;
val ^= negate;
return new PrimitiveExpression(val)
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(boolType, val));
} else if (ResolveResult.IsCompileTimeConstant && ResolveResult.ConstantValue is byte) {
bool val = (byte)ResolveResult.ConstantValue != 0;
val ^= negate;
return new PrimitiveExpression(val)
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(boolType, val));
@ -333,7 +341,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -333,7 +341,8 @@ namespace ICSharpCode.Decompiler.CSharp
var nullRef = new NullReferenceExpression()
.WithoutILInstruction()
.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()
.WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual,
this.ResolveResult, nullRef.ResolveResult));
@ -341,7 +350,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -341,7 +350,8 @@ namespace ICSharpCode.Decompiler.CSharp
var zero = new PrimitiveExpression(0)
.WithoutILInstruction()
.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()
.WithRR(new OperatorResolveResult(boolType, System.Linq.Expressions.ExpressionType.NotEqual,
this.ResolveResult, zero.ResolveResult));

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

@ -134,6 +134,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -134,6 +134,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
DecompileFinallyBlocks();
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);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
@ -141,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -141,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Remove temporaries", function);
foreach (var store in returnStores) {
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);
}
}
@ -589,15 +596,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -589,15 +596,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
name = fieldDef.Name.Substring(1, pos - 1);
}
v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name);
v.StateMachineField = ldflda.Field;
fieldToVariableMap.Add(fieldDef, v);
}
inst.ReplaceWith(new LdLoca(v));
inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange });
} else if (inst.MatchLdThis()) {
inst.ReplaceWith(new InvalidExpression("iterator") { ExpectedResultType = inst.ResultType });
inst.ReplaceWith(new InvalidExpression("stateMachine") { ExpectedResultType = inst.ResultType, ILRange = inst.ILRange });
} else {
foreach (var child in inst.Children) {
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

9
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -239,6 +239,12 @@ namespace ICSharpCode.Decompiler.IL @@ -239,6 +239,12 @@ namespace ICSharpCode.Decompiler.IL
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)
{
@ -302,6 +308,9 @@ namespace ICSharpCode.Decompiler.IL @@ -302,6 +308,9 @@ namespace ICSharpCode.Decompiler.IL
if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label);
}
if (StateMachineField != null) {
output.Write(" from state-machine");
}
}
internal void WriteTo(ITextOutput output)

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

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.IL.Transforms
{
@ -31,6 +33,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,6 +33,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
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)
{
for (int i = 0; i < block.Instructions.Count; i++) {
@ -38,31 +48,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -38,31 +48,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction copiedExpr;
if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) {
if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) {
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.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;
DoPropagate(v, copiedExpr, block, ref i, context);
}
}
}
@ -99,6 +85,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -99,6 +85,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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 @@ -224,6 +224,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
context.Step("stobj(ldloca(v), ...) => stloc(v, ...)", inst);
inst.ReplaceWith(new StLoc(v, inst.Value));
return;
}
ILInstruction target;

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

@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
} else {
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;
}
@ -254,7 +254,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -254,7 +254,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <param name="next">The next top-level expression</param>
/// <param name="loadInst">The load within 'next'</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));
@ -266,6 +266,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -266,6 +266,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
case OpCode.CompoundAssignmentInstruction:
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

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

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

Loading…
Cancel
Save