Browse Source

Introduce 'AddressOf' pseudo-opcode to make the type system aware of inlined value types (#139)

pull/144/head
Daniel Grunwald 14 years ago
parent
commit
660505e04d
  1. 2
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 2
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  3. 70
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 9
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 63
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  6. 7
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  7. 4
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  8. 10
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

2
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -743,6 +743,8 @@ namespace ICSharpCode.Decompiler.Ast
} }
case ILCode.InitializedObject: case ILCode.InitializedObject:
return new InitializedObjectExpression(); return new InitializedObjectExpression();
case ILCode.AddressOf:
return MakeRef(arg1);
default: default:
throw new Exception("Unknown OpCode: " + byteCode.Code); throw new Exception("Unknown OpCode: " + byteCode.Code);
} }

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

@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
static bool IsWithoutSideEffects(Expression left) static bool IsWithoutSideEffects(Expression left)
{ {
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression; return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
} }
void IAstTransform.Run(AstNode node) void IAstTransform.Run(AstNode node)

70
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -291,32 +291,52 @@ namespace ICSharpCode.Decompiler.ILAst
/// ///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)". /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
/// </summary> /// </summary>
void IntroducePropertyAccessInstructions(ILBlock method) void IntroducePropertyAccessInstructions(ILNode node)
{ {
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { ILExpression parentExpr = node as ILExpression;
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) { if (parentExpr != null) {
MethodReference cecilMethod = (MethodReference)expr.Operand; for (int i = 0; i < parentExpr.Arguments.Count; i++) {
if (cecilMethod.DeclaringType is ArrayType) { ILExpression expr = parentExpr.Arguments[i];
switch (cecilMethod.Name) { IntroducePropertyAccessInstructions(expr);
case "Get": IntroducePropertyAccessInstructions(expr, parentExpr, i);
expr.Code = ILCode.CallGetter; }
break; } else {
case "Set": foreach (ILNode child in node.GetChildren()) {
expr.Code = ILCode.CallSetter; IntroducePropertyAccessInstructions(child);
break; ILExpression expr = child as ILExpression;
case "Address": if (expr != null) {
expr.Code = ILCode.CallGetter; IntroducePropertyAccessInstructions(expr, null, -1);
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress)); }
break; }
} }
} else { }
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) { void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
if (cecilMethodDef.IsGetter) {
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter; if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
else if (cecilMethodDef.IsSetter) MethodReference cecilMethod = (MethodReference)expr.Operand;
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; if (cecilMethod.DeclaringType is ArrayType) {
} switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
expr.Code = ILCode.CallGetter;
if (parentExpr != null) {
parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
}
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
} }
} }
} }

9
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -286,9 +286,12 @@ namespace ICSharpCode.Decompiler.ILAst
CallSetter, CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary> /// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter, CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary> /// <summary>Simulates getting the address of the argument instruction.</summary>
/// <remarks>Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays</remarks> /// <remarks>
PropertyAddress /// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays.
/// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))"
/// </remarks>
AddressOf
} }
public static class ILCodeUtil public static class ILCodeUtil

63
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) { foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb); modified |= InlineAllInBasicBlock(bb);
} }
return modified; return modified;
} }
@ -183,31 +183,74 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent; ILExpression parent;
int pos; int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (ldloc == 0) if (ldloc == 0) {
{
if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v)) if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v))
return false; return false;
} else {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
} }
else if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
// Assign the ranges of the ldloc instruction: // Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
parent.Arguments[pos] = inlinedExpression; if (ldloc == 0) {
// it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types
// comes out correctly
parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression);
} else {
parent.Arguments[pos] = inlinedExpression;
}
return true; return true;
} }
return false; return false;
} }
/// <summary> /// <summary>
/// Is this a temporary variable generated by the C# compiler for instance method calls on immutable value type values /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary> /// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="pos">Index of the load within 'parent'</param>
/// <param name="v">The variable being inlined.</param>
bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v) bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v)
{ {
return pos == 0 && v.Type != null && v.Type.IsValueType && next.Code == ILCode.Stloc if (pos == 0 && v.Type != null && v.Type.IsValueType) {
&& (parent.Code == ILCode.Call || parent.Code == ILCode.Callvirt) && ((MethodReference)parent.Operand).HasThis; // inline the compiler-generated variable that are used when accessing a member on a value type:
switch (parent.Code) {
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
MethodReference mr = (MethodReference)parent.Operand;
return mr.HasThis;
case ILCode.Stfld:
case ILCode.Ldfld:
case ILCode.Ldflda:
return true;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent)
{
switch (next.Code) {
case ILCode.Ret:
case ILCode.Brtrue:
return parent == next;
case ILCode.Switch:
return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default:
return false;
}
} }
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)

7
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -43,11 +43,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) { if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(pos + 1); body.RemoveAt(pos + 1);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true; return true;
} }
} }
const int maxConsecutiveDefaultValueExpressions = 10; // Put in a limit so that we don't consume too much memory if the code allocates a huge array
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
const int maxConsecutiveDefaultValueExpressions = 300;
List<ILExpression> operands = new List<ILExpression>(); List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0; int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) { for (int j = pos + 1; j < body.Count; j++) {
@ -72,6 +75,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (operands.Count == arrayLength) { if (operands.Count == arrayLength) {
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove); body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true; return true;
} }
} }

4
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (exprInit.Code == ILCode.Ldloc) if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca; exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter) else if (exprInit.Code == ILCode.CallGetter)
exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress)); exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
else else
exprInit.Code = ILCode.Ldsflda; exprInit.Code = ILCode.Ldsflda;
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
@ -567,8 +567,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.Stobj) { if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { } else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
initialValue = new ILExpression(ILCode.AddressOf, null, initialValue);
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else { } else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema); initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);

10
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -319,10 +319,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method); return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else { } else {
TypeReference type = SubstituteTypeArgs(method.ReturnType, method); return SubstituteTypeArgs(method.ReturnType, method);
if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
type = new ByReferenceType(type);
return type;
} }
} }
case ILCode.Newobj: case ILCode.Newobj:
@ -463,6 +460,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference); InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
} }
return new ByReferenceType((TypeReference)expr.Operand); return new ByReferenceType((TypeReference)expr.Operand);
case ILCode.AddressOf:
{
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null;
}
#endregion #endregion
#region Arithmetic instructions #region Arithmetic instructions
case ILCode.Not: // bitwise complement case ILCode.Not: // bitwise complement

Loading…
Cancel
Save