Browse Source

Implemented post-increment operator. Closes #76.

pull/100/head
Daniel Grunwald 15 years ago
parent
commit
4c7a896a84
  1. 11
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 25
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 25
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  4. 161
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  5. 7
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  6. 33
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

11
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -284,6 +284,17 @@ namespace ICSharpCode.Decompiler.Ast @@ -284,6 +284,17 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
case ILCode.PostIncrement:
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
if (arg1 is DirectionExpression)
arg1 = ((DirectionExpression)arg1).Expression.Detach();
var uoe = new Ast.UnaryOperatorExpression(
(int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1);
uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
return uoe;
}
#endregion
#region Arrays
case ILCode.Newarr:

25
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -27,6 +27,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -27,6 +27,7 @@ namespace ICSharpCode.Decompiler.ILAst
TransformArrayInitializers,
TransformCollectionInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
InlineVariables2,
FindLoops,
FindConditions,
@ -122,6 +123,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -122,6 +123,9 @@ namespace ICSharpCode.Decompiler.ILAst
modified |= block.RunOptimization(MakeAssignmentExpression);
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
modified |= block.RunOptimization(IntroducePostIncrement);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation();
@ -576,6 +580,27 @@ namespace ICSharpCode.Decompiler.ILAst @@ -576,6 +580,27 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
public static bool IsLoadFromArray(this ILCode code)
{
switch (code) {
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
return true;
default:
return false;
}
}
/// <summary>
/// Can the expression be used as a statement in C#?
/// </summary>

25
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -268,10 +268,27 @@ namespace ICSharpCode.Decompiler.ILAst @@ -268,10 +268,27 @@ namespace ICSharpCode.Decompiler.ILAst
Ldc_Decimal,
YieldBreak,
YieldReturn,
DefaultValue, // default(T)
CompoundAssignment, // assignment combined with binary operator
Pattern // used for ILAst pattern nodes
/// <summary>
/// Represents the 'default(T)' instruction.
/// </summary>
/// <remarks>Introduced by SimplifyLdObjAndStObj step</remarks>
DefaultValue,
/// <summary>
/// ILExpression with a single child: binary operator.
/// This expression means that the binary operator will also assign the new value to its left-hand side.
/// 'CompoundAssignment' must not be used for local variables, as inlining (and other) optimizations don't know that it modifies the variable.
/// </summary>
/// <remarks>Introduced by MakeCompoundAssignments step</remarks>
CompoundAssignment,
/// <summary>
/// Represents the post-increment operator.
/// The first argument is the address of the variable to increment (ldloca instruction).
/// The second arugment is the amount the variable is incremented by (ldc.i4 instruction)
/// </summary>
/// <remarks>Introduced by IntroducePostIncrement step</remarks>
PostIncrement,
PostIncrement_Ovf, // checked variant of PostIncrement
PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers
}
public static class ILCodeUtil

161
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -240,28 +240,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -240,28 +240,11 @@ 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 || IsArrayStore(store.Code))) {
if (store != null && (store.Code == ILCode.Stloc || store.Code == ILCode.Stfld || store.Code == ILCode.Stsfld || store.Code.IsStoreToArray())) {
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
@ -284,7 +267,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -284,7 +267,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool MakeCompoundAssignmentForArray(ILExpression expr)
{
// stelem.any(..., ldloc(array), ldloc(pos), <OP>(ldelem.any(int32, ldloc(array), ldloc(pos)), <RIGHT>))
if (!IsArrayStore(expr.Code))
if (!expr.Code.IsStoreToArray())
return false;
ILVariable arrayVar, posVar;
if (!(expr.Arguments[0].Match(ILCode.Ldloc, out arrayVar) && expr.Arguments[1].Match(ILCode.Ldloc, out posVar)))
@ -354,6 +337,146 @@ namespace ICSharpCode.Decompiler.ILAst @@ -354,6 +337,146 @@ namespace ICSharpCode.Decompiler.ILAst
}
#endregion
#region IntroducePostIncrement
bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = IntroducePostIncrementForVariables(body, expr, pos);
Debug.Assert(body[pos] == expr); // IntroducePostIncrementForVariables shouldn't change the expression reference
ILExpression newExpr = IntroducePostIncrementForInstanceFields(expr);
if (newExpr != null) {
modified = true;
body[pos] = newExpr;
new ILInlining(method).InlineIfPossible(body, ref pos);
}
return modified;
}
bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos)
{
// Works for variables and static fields
// expr = ldloc(i)
// stloc(i, add(expr, ldc.i4(1)))
// ->
// expr = postincrement(1, ldloca(i))
ILVariable exprVar;
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false;
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld))
return false;
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null || !(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand))
return false;
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false;
if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca;
else
exprInit.Code = ILCode.Ldsflda;
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges
return true;
}
ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr)
{
// stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4:int32(1)))
// ->
// stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// Also works for array elements:
// stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4:int32(1)))
// ->
// stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray()))
return null;
// Test that all arguments except the last are ldloc (1 arg for fields, 2 args for arrays)
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
if (expr.Arguments[i].Code != ILCode.Ldloc)
return null;
}
ILExpression addExpr = expr.Arguments[expr.Arguments.Count - 1];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
ILVariable helperVar;
ILExpression initialValue;
if (!(incrementAmount != 0 && addExpr.Arguments[0].Match(ILCode.Stloc, out helperVar, out initialValue)))
return null;
if (expr.Code == ILCode.Stfld) {
if (!(initialValue.Code == ILCode.Ldfld && initialValue.Operand == expr.Operand))
return null;
} else {
if (!initialValue.Code.IsLoadFromArray())
return null;
}
Debug.Assert(expr.Arguments.Count - 1 == initialValue.Arguments.Count);
for (int i = 0; i < initialValue.Arguments.Count; i++) {
if (!initialValue.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return null;
}
ILExpression stloc = addExpr.Arguments[0];
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
// TODO: ILRanges?
return stloc;
}
ILCode GetIncrementCode(ILExpression addExpr, out int incrementAmount)
{
ILCode incrementCode;
bool decrement = false;
switch (addExpr.Code) {
case ILCode.Add:
incrementCode = ILCode.PostIncrement;
break;
case ILCode.Add_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
break;
case ILCode.Add_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
break;
case ILCode.Sub:
incrementCode = ILCode.PostIncrement;
decrement = true;
break;
case ILCode.Sub_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
decrement = true;
break;
case ILCode.Sub_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
decrement = true;
break;
default:
incrementAmount = 0;
return ILCode.Nop;
}
if (addExpr.Arguments[1].Match(ILCode.Ldc_I4, out incrementAmount)) {
if (incrementAmount == -1 || incrementAmount == 1) { // TODO pointer increment?
if (decrement)
incrementAmount = -incrementAmount;
return incrementCode;
}
}
incrementAmount = 0;
return ILCode.Nop;
}
#endregion
#region IntroduceFixedStatements
bool IntroduceFixedStatements(List<ILNode> body, int i)
{

7
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -431,6 +431,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -431,6 +431,13 @@ namespace ICSharpCode.Decompiler.ILAst
return typeSystem.IntPtr;
case ILCode.Sizeof:
return typeSystem.Int32;
case ILCode.PostIncrement:
case ILCode.PostIncrement_Ovf:
case ILCode.PostIncrement_Ovf_Un:
{
ByReferenceType byReferenceType = InferTypeForExpression(expr.Arguments[0], null) as ByReferenceType;
return byReferenceType != null ? byReferenceType.ElementType : null;
}
#endregion
#region Arithmetic instructions
case ILCode.Not: // bitwise complement

33
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -62,13 +62,28 @@ public class IncrementDecrement @@ -62,13 +62,28 @@ public class IncrementDecrement
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]--;
// }
public int PostIncrementInAddition(int i, int j)
{
return i++ + j;
}
public int PostDecrementArrayElement(int[] array, int pos)
{
return array[pos]--;
}
public int PostIncrementStaticField()
{
return IncrementDecrement.StaticField++;
}
public int PostIncrementInstanceField(IncrementDecrement.MutableClass m)
{
return m.Field++;
}
public int PostDecrementInstanceField()
{
return this.M().Field--;
}
}

Loading…
Cancel
Save