Browse Source

Add support for 'ptr - ptr'.

pull/924/head
Daniel Grunwald 8 years ago
parent
commit
dc995b15c6
  1. 11
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/UnsafeCode.cs
  2. 120
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 13
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

11
ICSharpCode.Decompiler.Tests/TestCases/Correctness/UnsafeCode.cs

@ -198,17 +198,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -198,17 +198,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public unsafe int PointerSubtraction(long* p, long* q)
{
return (int)((long)(p - q));
return (int)(p - q);
}
public unsafe long PointerSubtractionLong(long* p, long* q)
{
return p - q;
}
public unsafe int PointerSubtraction2(long* p, short* q)
{
return (int)((long)((byte*)p - (byte*)q));
return (int)((byte*)p - (byte*)q);
}
public unsafe int PointerSubtraction3(void* p, void* q)
{
return (int)((long)((byte*)p - (byte*)q));
return (int)((byte*)p - (byte*)q);
}
unsafe ~UnsafeCode()

120
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -645,7 +645,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -645,7 +645,8 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.Mul:
return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply);
case BinaryNumericOperator.Div:
return HandleBinaryNumeric(inst, BinaryOperatorType.Divide);
return HandlePointerSubtraction(inst)
?? HandleBinaryNumeric(inst, BinaryOperatorType.Divide);
case BinaryNumericOperator.Rem:
return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus);
case BinaryNumericOperator.BitAnd:
@ -724,7 +725,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -724,7 +725,6 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression? GetPointerArithmeticOffset()
{
int? elementSize = ComputeSizeOf(pointerType.ElementType);
int val;
if (elementSize == 1) {
return byteOffsetExpr;
} else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) {
@ -732,27 +732,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -732,27 +732,24 @@ namespace ICSharpCode.Decompiler.CSharp
return null;
if (mul.IsLifted)
return null;
if (elementSize > 0 && mul.Right.UnwrapConv(ConversionKind.SignExtend).MatchLdcI4(elementSize.Value)) {
if (elementSize > 0 && mul.Right.MatchLdcI(elementSize.Value)) {
return Translate(mul.Left);
} else {
return null;
}
} else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend).MatchLdcI4(out val)) {
} else if (byteOffsetInst.MatchLdcI(out long val)) {
// If the offset is a constant, it's possible that the compiler
// constant-folded the multiplication.
if (elementSize > 0 && (val % elementSize == 0) && val > 0) {
val /= elementSize.Value;
return new PrimitiveExpression(val)
.WithILInstruction(byteOffsetInst)
.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), val));
} else {
return null;
if (val <= int.MaxValue) {
return new PrimitiveExpression((int)val)
.WithILInstruction(byteOffsetInst)
.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), val));
}
}
} else {
return null;
}
return null;
}
TranslatedExpression FallBackToBytePointer()
{
pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte));
@ -760,6 +757,73 @@ namespace ICSharpCode.Decompiler.CSharp @@ -760,6 +757,73 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
/// <summary>
/// Called for divisions, detect and handles the code pattern:
/// div(sub(a, b), sizeof(T))
/// when a,b are of type T*.
/// This is what the C# compiler generates for pointer subtraction.
/// </summary>
TranslatedExpression? HandlePointerSubtraction(BinaryNumericInstruction inst)
{
Debug.Assert(inst.Operator == BinaryNumericOperator.Div);
if (inst.CheckForOverflow || inst.LeftInputType != StackType.I)
return null;
if (!(inst.Left is BinaryNumericInstruction sub && sub.Operator == BinaryNumericOperator.Sub))
return null;
if (sub.CheckForOverflow)
return null;
// First, attempt to parse the 'sizeof' on the RHS
IType elementType;
if (inst.Right.MatchLdcI(out long elementSize)) {
elementType = null;
// OK, might be pointer subtraction if the element size matches
} else if (inst.Right.MatchSizeOf(out elementType)) {
// OK, might be pointer subtraction if the element type matches
} else {
return null;
}
var left = Translate(sub.Left);
var right = Translate(sub.Right);
IType pointerType;
if (IsMatchingPointerType(left.Type)) {
pointerType = left.Type;
} else if (IsMatchingPointerType(right.Type)) {
pointerType = right.Type;
} else if (elementSize == 1 && left.Type.Kind == TypeKind.Pointer && right.Type.Kind == TypeKind.Pointer) {
// two pointers (neither matching), we're dividing by 1 (debug builds only),
// -> subtract two byte pointers
pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte));
} else {
// neither is a matching pointer type
// -> not a pointer subtraction after all
return null;
}
// We got a pointer subtraction.
left = left.ConvertTo(pointerType, this);
right = right.ConvertTo(pointerType, this);
var rr = new OperatorResolveResult(
compilation.FindType(KnownTypeCode.Int64),
ExpressionType.Subtract,
left.ResolveResult, right.ResolveResult
);
var result = new BinaryOperatorExpression(
left.Expression, BinaryOperatorType.Subtract, right.Expression
).WithILInstruction(new[] { inst, sub })
.WithRR(rr);
return result;
bool IsMatchingPointerType(IType type)
{
if (type is PointerType pt) {
if (elementType != null)
return elementType.Equals(pt.ElementType);
else if (elementSize > 0)
return ComputeSizeOf(pt.ElementType) == elementSize;
}
return false;
}
}
int? ComputeSizeOf(IType type)
{
var rr = resolver.ResolveSizeOf(type);
@ -1081,7 +1145,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1081,7 +1145,7 @@ namespace ICSharpCode.Decompiler.CSharp
// We just need to ensure the input type before the conversion is signed.
// Also, if the argument was translated into an oversized C# type,
// we need to perform the truncatation to the input stack type.
if (inputType.GetSign() != Sign.Signed || inputType.GetSize() > inputStackType.GetSize()) {
if (inputType.GetSign() != Sign.Signed || ValueMightBeOversized(arg.ResolveResult, inputStackType)) {
// Note that an undersized C# type is handled just fine:
// If it is unsigned we'll zero-extend it to the width of the inputStackType here,
// and it is signed we just combine the two sign-extensions into a single sign-extending conversion.
@ -1143,6 +1207,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1143,6 +1207,32 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
/// <summary>
/// Gets whether the ResolveResult computes a value that might be oversized for the specified stack type.
/// </summary>
bool ValueMightBeOversized(ResolveResult rr, StackType stackType)
{
IType inputType = NullableType.GetUnderlyingType(rr.Type);
if (inputType.GetSize() <= stackType.GetSize()) {
// The input type is smaller or equal to the stack type,
// it can't be an oversized value.
return false;
}
if (rr is OperatorResolveResult orr) {
if (stackType == StackType.I && orr.OperatorType == ExpressionType.Subtract
&& orr.Operands.Count == 2
&& orr.Operands[0].Type.Kind == TypeKind.Pointer
&& orr.Operands[1].Type.Kind == TypeKind.Pointer)
{
// Even though a pointer subtraction produces a value of type long in C#,
// the value will always fit in a native int.
return false;
}
}
// We don't have any information about the value, so it might be oversized.
return true;
}
protected internal override TranslatedExpression VisitCall(Call inst, TranslationContext context)
{
return new CallBuilder(this, typeSystem, settings).Build(inst);

13
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.IL @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.IL
}
/// <summary>
/// Matches either LdcI4 or LdcI8.
/// Matches ldc.i4, ldc.i8, and extending conversions.
/// </summary>
public bool MatchLdcI(out long val)
{
@ -43,6 +43,17 @@ namespace ICSharpCode.Decompiler.IL @@ -43,6 +43,17 @@ namespace ICSharpCode.Decompiler.IL
val = intVal;
return true;
}
if (this is Conv conv) {
if (conv.Kind == ConversionKind.SignExtend) {
return conv.Argument.MatchLdcI(out val);
} else if (conv.Kind == ConversionKind.ZeroExtend && conv.InputType == StackType.I4) {
if (conv.Argument.MatchLdcI(out val)) {
// clear top 32 bits
val &= uint.MaxValue;
return true;
}
}
}
return false;
}

Loading…
Cancel
Save