Browse Source

Improved support for compound assignments and the pre-increment operator.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
1df82cc3d1
  1. 23
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 66
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  3. 1
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 1
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 131
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  6. 20
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  7. 4
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  8. 62
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  9. 6
      ICSharpCode.Decompiler/Tests/TestRunner.cs

23
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -16,6 +16,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -16,6 +16,8 @@ namespace ICSharpCode.Decompiler.Ast
using Ast = ICSharpCode.NRefactory.CSharp;
using Cecil = Mono.Cecil;
public class ArrayAccessAnnotation {}
public class AstMethodBodyBuilder
{
MethodDefinition methodDef;
@ -23,6 +25,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -23,6 +25,8 @@ namespace ICSharpCode.Decompiler.Ast
DecompilerContext context;
HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
static readonly ArrayAccessAnnotation arrayAccessAnnotation = new ArrayAccessAnnotation();
/// <summary>
/// Creates the body for the method definition.
/// </summary>
@ -312,8 +316,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -312,8 +316,9 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_Any:
return arg1.Indexer(arg2);
case ILCode.Ldelema: return MakeRef(arg1.Indexer(arg2));
return arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation);
case ILCode.Ldelema:
return MakeRef(arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation));
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
@ -323,7 +328,19 @@ namespace ICSharpCode.Decompiler.Ast @@ -323,7 +328,19 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3);
return new Ast.AssignmentExpression(arg1.Indexer(arg2).WithAnnotation(arrayAccessAnnotation), arg3);
case ILCode.CompoundAssignment:
{
BinaryOperatorExpression boe = (BinaryOperatorExpression)arg1;
return new Ast.AssignmentExpression {
Left = boe.Left.Detach(),
Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator),
Right = boe.Right.Detach()
}.CopyAnnotationsFrom(boe);
// We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because
// the operator cannot be translated back to the expanded form (as the left-hand expression
// would be evaluated twice, and might have side-effects)
}
#endregion
#region Comparison
case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);

66
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -177,38 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -177,38 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
if (IsWithoutSideEffects(assignment.Left) && assignment.Left.Match(binary.Left) != null) {
switch (binary.Operator) {
case BinaryOperatorType.Add:
assignment.Operator = AssignmentOperatorType.Add;
break;
case BinaryOperatorType.Subtract:
assignment.Operator = AssignmentOperatorType.Subtract;
break;
case BinaryOperatorType.Multiply:
assignment.Operator = AssignmentOperatorType.Multiply;
break;
case BinaryOperatorType.Divide:
assignment.Operator = AssignmentOperatorType.Divide;
break;
case BinaryOperatorType.Modulus:
assignment.Operator = AssignmentOperatorType.Modulus;
break;
case BinaryOperatorType.ShiftLeft:
assignment.Operator = AssignmentOperatorType.ShiftLeft;
break;
case BinaryOperatorType.ShiftRight:
assignment.Operator = AssignmentOperatorType.ShiftRight;
break;
case BinaryOperatorType.BitwiseAnd:
assignment.Operator = AssignmentOperatorType.BitwiseAnd;
break;
case BinaryOperatorType.BitwiseOr:
assignment.Operator = AssignmentOperatorType.BitwiseOr;
break;
case BinaryOperatorType.ExclusiveOr:
assignment.Operator = AssignmentOperatorType.ExclusiveOr;
break;
}
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
@ -227,7 +196,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -227,7 +196,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++))
if (assignment.Parent is ExpressionStatement)
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
else
else
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
}
@ -236,6 +205,34 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -236,6 +205,34 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop)
{
switch (bop) {
case BinaryOperatorType.Add:
return AssignmentOperatorType.Add;
case BinaryOperatorType.Subtract:
return AssignmentOperatorType.Subtract;
case BinaryOperatorType.Multiply:
return AssignmentOperatorType.Multiply;
case BinaryOperatorType.Divide:
return AssignmentOperatorType.Divide;
case BinaryOperatorType.Modulus:
return AssignmentOperatorType.Modulus;
case BinaryOperatorType.ShiftLeft:
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:
return AssignmentOperatorType.BitwiseOr;
case BinaryOperatorType.ExclusiveOr:
return AssignmentOperatorType.ExclusiveOr;
default:
return AssignmentOperatorType.Assign;
}
}
static bool IsWithoutSideEffects(Expression left)
{
if (left is ThisReferenceExpression)
@ -245,6 +242,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -245,6 +242,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return mre.Annotation<FieldReference>() != null && IsWithoutSideEffects(mre.Target);
IndexerExpression ie = left as IndexerExpression;
if (ie != null && ie.Annotation<ArrayAccessAnnotation>() != null)
return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
return false;
}

1
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -120,6 +120,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -120,6 +120,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -269,6 +269,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -269,6 +269,7 @@ namespace ICSharpCode.Decompiler.ILAst
YieldBreak,
YieldReturn,
DefaultValue, // default(T)
CompoundAssignment, // assignment combined with binary operator
Pattern // used for ILAst pattern nodes
}

131
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -59,6 +59,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -59,6 +59,18 @@ namespace ICSharpCode.Decompiler.ILAst
static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
expr = SimplifyLdObjAndStObj(expr, ref modified);
if (modified && body != null)
body[pos] = expr;
for (int i = 0; i < expr.Arguments.Count; i++) {
expr.Arguments[i] = SimplifyLdObjAndStObj(expr.Arguments[i], ref modified);
modified |= SimplifyLdObjAndStObj(null, expr.Arguments[i], -1);
}
return modified;
}
static ILExpression SimplifyLdObjAndStObj(ILExpression expr, ref bool modified)
{
if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
@ -84,13 +96,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -84,13 +96,17 @@ namespace ICSharpCode.Decompiler.ILAst
}
if (newCode != null) {
arg.Code = newCode.Value;
if (expr.Code == ILCode.Stobj)
if (expr.Code == ILCode.Stobj) {
arg.InferredType = expr.InferredType;
arg.ExpectedType = expr.ExpectedType;
arg.Arguments.Add(arg2);
}
arg.ILRanges.AddRange(expr.ILRanges);
body[pos] = arg;
modified = true;
return arg;
} else {
return expr;
}
return modified;
}
#endregion
@ -214,11 +230,118 @@ namespace ICSharpCode.Decompiler.ILAst @@ -214,11 +230,118 @@ namespace ICSharpCode.Decompiler.ILAst
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
{
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld)) {
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld || IsArrayStore(store.Code))) {
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
return false;
}
static bool IsArrayStore(ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_Ref:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
return true;
}
return false;
}
#endregion
#region MakeCompoundAssignments
bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
modified |= MakeCompoundAssignmentForArray(expr);
modified |= MakeCompoundAssignmentForInstanceField(expr);
// Static fields and local variables are not handled here - those are expressions without side effects
// and get handled by ReplaceMethodCallsWithOperators
// (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form).
foreach (ILExpression arg in expr.Arguments) {
modified |= MakeCompoundAssignments(null, arg, -1);
}
if (modified && body != null)
new ILInlining(method).InlineInto(body, pos, aggressive: false);
return modified;
}
bool MakeCompoundAssignmentForArray(ILExpression expr)
{
// stelem.any(..., ldloc(array), ldloc(pos), <OP>(ldelem.any(int32, ldloc(array), ldloc(pos)), <RIGHT>))
if (!IsArrayStore(expr.Code))
return false;
ILVariable arrayVar, posVar;
if (!(expr.Arguments[0].Match(ILCode.Ldloc, out arrayVar) && expr.Arguments[1].Match(ILCode.Ldloc, out posVar)))
return false;
// At least one of the variables must be generated; otherwise we just keep the expanded form.
if (!(arrayVar.IsGenerated || posVar.IsGenerated))
return false;
ILExpression op = expr.Arguments[2];
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldelem = op.Arguments[0];
if (!(ldelem.Code == ILCode.Ldelem_Any && ldelem.Arguments[0].MatchLdloc(arrayVar) && ldelem.Arguments[1].MatchLdloc(posVar)))
return false;
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveRange(0, 2);
// result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))"
return true;
}
bool MakeCompoundAssignmentForInstanceField(ILExpression expr)
{
// stfld(field, expr, <OP>(ldfld(field, expr), <RIGHT>))
FieldReference field;
ILExpression firstLoad, op;
ILVariable exprVar;
if (!(expr.Match(ILCode.Stfld, out field, out firstLoad, out op) && firstLoad.Match(ILCode.Ldloc, out exprVar) && exprVar.IsGenerated))
return false;
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldfld = op.Arguments[0];
if (!(ldfld.Code == ILCode.Ldfld && ldfld.Operand == field && ldfld.Arguments[0].MatchLdloc(exprVar)))
return false;
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveAt(0);
// result is "CompoundAssignment(<OP>(ldfld(...), <RIGHT>))"
return true;
}
static bool CanBeRepresentedAsCompoundAssignment(ILCode code)
{
switch (code) {
case ILCode.Add:
case ILCode.Add_Ovf:
case ILCode.Add_Ovf_Un:
case ILCode.Sub:
case ILCode.Sub_Ovf:
case ILCode.Sub_Ovf_Un:
case ILCode.Mul:
case ILCode.Mul_Ovf:
case ILCode.Mul_Ovf_Un:
case ILCode.Div:
case ILCode.Div_Un:
case ILCode.Rem:
case ILCode.Rem_Un:
case ILCode.And:
case ILCode.Or:
case ILCode.Xor:
case ILCode.Shl:
case ILCode.Shr:
case ILCode.Shr_Un:
return true;
default:
return false;
}
}
#endregion
#region IntroduceFixedStatements

20
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -470,6 +470,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -470,6 +470,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32);
case ILCode.CompoundAssignment:
{
TypeReference varType = InferTypeForExpression(expr.Arguments[0].Arguments[0], null);
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], varType);
}
return varType;
}
#endregion
#region Constant loading instructions
case ILCode.Ldnull:
@ -553,14 +561,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -553,14 +561,16 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
case ILCode.Stelem_Any:
if (forceInferChildren) {
{
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
if (arrayType != null) {
InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
if (arrayType != null) {
InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
}
}
return arrayType != null ? arrayType.ElementType : null;
}
return null;
#endregion
#region Conversion instructions
case ILCode.Conv_I1:

4
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<NoWarn>67,169</NoWarn>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
@ -27,7 +28,6 @@ @@ -27,7 +28,6 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@ -35,7 +35,6 @@ @@ -35,7 +35,6 @@
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
@ -54,6 +53,7 @@ @@ -54,6 +53,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="Switch.cs" />
<Compile Include="UnsafeCode.cs" />
<Compile Include="YieldReturn.cs" />

62
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
// 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;
public class IncrementDecrement
{
public class MutableClass
{
public int Field;
}
private IncrementDecrement.MutableClass M()
{
return new IncrementDecrement.MutableClass();
}
public int PreIncrementInAddition(int i, int j)
{
return i + ++j;
}
public int PreDecrementArrayElement(int[] array, int pos)
{
return --array[pos];
}
public int PreIncrementInstanceField()
{
return ++this.M().Field;
}
public int PreIncrementInstanceField2(IncrementDecrement.MutableClass m)
{
return ++m.Field;
}
public int CompoundMultiplyInstanceField()
{
return this.M().Field *= 10;
}
public int CompoundMultiplyArrayElement1(int[] array, int pos)
{
return array[pos] *= 10;
}
public int CompoundMultiplyArrayElement2(int[] array)
{
return array[Environment.TickCount] *= 10;
}
// public int PostIncrementInAddition(int i, int j)
// {
// return i++ + j;
// }
//
// public int PostDecrementArrayElement(int[] array, int pos)
// {
// return array[pos]--;
// }
}

6
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\Generics.cs");
}
[Test]
public void IncrementDecrement()
{
TestFile(@"..\..\Tests\IncrementDecrement.cs");
}
[Test, Ignore("Formatting issues (array initializers not on single line)")]
public void InitializerTests()
{

Loading…
Cancel
Save