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

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

@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
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)

70
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -291,32 +291,52 @@ namespace ICSharpCode.Decompiler.ILAst @@ -291,32 +291,52 @@ namespace ICSharpCode.Decompiler.ILAst
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
/// </summary>
void IntroducePropertyAccessInstructions(ILBlock method)
void IntroducePropertyAccessInstructions(ILNode node)
{
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
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;
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
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;
}
ILExpression parentExpr = node as ILExpression;
if (parentExpr != null) {
for (int i = 0; i < parentExpr.Arguments.Count; i++) {
ILExpression expr = parentExpr.Arguments[i];
IntroducePropertyAccessInstructions(expr);
IntroducePropertyAccessInstructions(expr, parentExpr, i);
}
} else {
foreach (ILNode child in node.GetChildren()) {
IntroducePropertyAccessInstructions(child);
ILExpression expr = child as ILExpression;
if (expr != null) {
IntroducePropertyAccessInstructions(expr, null, -1);
}
}
}
}
void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
{
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
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 @@ -286,9 +286,12 @@ namespace ICSharpCode.Decompiler.ILAst
CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary>
/// <remarks>Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays</remarks>
PropertyAddress
/// <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.
/// 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

63
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
}
return modified;
}
@ -183,31 +183,74 @@ namespace ICSharpCode.Decompiler.ILAst @@ -183,31 +183,74 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent;
int pos;
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))
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:
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 false;
}
/// <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>
/// <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)
{
return pos == 0 && v.Type != null && v.Type.IsValueType && next.Code == ILCode.Stloc
&& (parent.Code == ILCode.Call || parent.Code == ILCode.Callvirt) && ((MethodReference)parent.Operand).HasThis;
if (pos == 0 && v.Type != null && v.Type.IsValueType) {
// 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)

7
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -43,11 +43,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -43,11 +43,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(pos + 1);
new ILInlining(method).InlineIfPossible(body, ref pos);
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>();
int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) {
@ -72,6 +75,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -72,6 +75,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (operands.Count == arrayLength) {
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true;
}
}

4
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

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

10
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

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

Loading…
Cancel
Save