Browse Source

Fixed compound assignment and post/pre increment for pointer dereference

pull/2971/head
ElektroKill 2 years ago
parent
commit
ea1f6e7c25
No known key found for this signature in database
GPG Key ID: 7E3C5C084E40E3EC
  1. 22
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  2. 108
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

22
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
#nullable enable
#nullable enable
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
@ -78,6 +78,26 @@ namespace ICSharpCode.Decompiler.IL @@ -78,6 +78,26 @@ namespace ICSharpCode.Decompiler.IL
}
}
public static bool HasOppositeSign(this PrimitiveType primitiveType)
{
switch (primitiveType)
{
case PrimitiveType.I1:
case PrimitiveType.I2:
case PrimitiveType.I4:
case PrimitiveType.I8:
case PrimitiveType.U1:
case PrimitiveType.U2:
case PrimitiveType.U4:
case PrimitiveType.U8:
case PrimitiveType.I:
case PrimitiveType.U:
return true;
default:
return false;
}
}
/// <summary>
/// Gets the size in bytes of the primitive type.
///

108
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -145,10 +145,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -145,10 +145,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
else if (pointerType is PointerType pointer)
newType = pointer.ElementType;
}
if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem))
if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem, out bool canChangeSign))
{
// 'stobj' is implicitly truncating the value
return false;
if (canChangeSign)
{
// Change the sign of the type to skip implicit truncation
newType = SwapSign(newType, context.TypeSystem);
}
else
{
// 'stobj' is implicitly truncating the value
return false;
}
}
context.Step("Inline assignment stobj", stobj);
stobj.Type = newType;
@ -214,6 +222,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -214,6 +222,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private static IType SwapSign(IType type, ICompilation compilation)
{
return type.ToPrimitiveType() switch {
PrimitiveType.I1 => compilation.FindType(KnownTypeCode.Byte),
PrimitiveType.I2 => compilation.FindType(KnownTypeCode.UInt16),
PrimitiveType.I4 => compilation.FindType(KnownTypeCode.UInt32),
PrimitiveType.I8 => compilation.FindType(KnownTypeCode.UInt64),
PrimitiveType.U1 => compilation.FindType(KnownTypeCode.SByte),
PrimitiveType.U2 => compilation.FindType(KnownTypeCode.Int16),
PrimitiveType.U4 => compilation.FindType(KnownTypeCode.Int32),
PrimitiveType.U8 => compilation.FindType(KnownTypeCode.Int64),
PrimitiveType.I => compilation.FindType(KnownTypeCode.UIntPtr),
PrimitiveType.U => compilation.FindType(KnownTypeCode.IntPtr),
_ => type
};
}
static ILInstruction UnwrapSmallIntegerConv(ILInstruction inst, out Conv conv)
{
conv = inst as Conv;
@ -507,12 +532,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -507,12 +532,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false)
{
return IsImplicitTruncation(value, type, compilation, out _, allowNullableValue);
}
/// <summary>
/// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value'
/// due to implicit truncation.
/// </summary>
static internal bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false)
internal static bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, out bool canChangeSign, bool allowNullableValue = false)
{
canChangeSign = false;
if (!type.IsSmallIntegerType())
{
// Implicit truncation in ILAst only happens for small integer types;
@ -542,7 +573,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -542,7 +573,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
else if (value is Conv conv)
{
return conv.TargetType != type.ToPrimitiveType();
PrimitiveType primitiveType = type.ToPrimitiveType();
PrimitiveType convTargetType = conv.TargetType;
if (convTargetType == primitiveType)
return false;
if (primitiveType.GetSize() == convTargetType.GetSize() && primitiveType.GetSign() != convTargetType.GetSign())
canChangeSign = primitiveType.HasOppositeSign();
return true;
}
else if (value is Comp)
{
@ -557,14 +594,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -557,14 +594,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case BinaryNumericOperator.BitXor:
// If both input values fit into the type without truncation,
// the result of a binary operator will also fit.
return IsImplicitTruncation(bni.Left, type, compilation, allowNullableValue)
|| IsImplicitTruncation(bni.Right, type, compilation, allowNullableValue);
bool leftIsTruncation = IsImplicitTruncation(bni.Left, type, compilation, out bool leftChangeSign, allowNullableValue);
// If the left side is truncating and a sign change is not possible we do not need to evaluate the right side
if (leftIsTruncation && !leftChangeSign)
return true;
bool rightIsTruncation = IsImplicitTruncation(bni.Right, type, compilation, out bool rightChangeSign, allowNullableValue);
if (!rightIsTruncation)
return false;
canChangeSign = rightChangeSign;
return true;
}
}
else if (value is IfInstruction ifInst)
{
return IsImplicitTruncation(ifInst.TrueInst, type, compilation, allowNullableValue)
|| IsImplicitTruncation(ifInst.FalseInst, type, compilation, allowNullableValue);
bool trueIsTruncation = IsImplicitTruncation(ifInst.TrueInst, type, compilation, out bool trueChangeSign, allowNullableValue);
// If the true branch is truncating and a sign change is not possible we do not need to evaluate the false branch
if (trueIsTruncation && !trueChangeSign)
return true;
bool falseIsTruncation = IsImplicitTruncation(ifInst.FalseInst, type, compilation, out bool falseChangeSign, allowNullableValue);
if (!falseIsTruncation)
return false;
canChangeSign = falseChangeSign;
return true;
}
else
{
@ -575,7 +626,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -575,7 +626,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (inferredType.Kind != TypeKind.Unknown)
{
return !(inferredType.GetSize() <= type.GetSize() && inferredType.GetSign() == type.GetSign());
var inferredPrimitive = inferredType.ToPrimitiveType();
var primitiveType = type.ToPrimitiveType();
bool sameSign = inferredPrimitive.GetSign() == primitiveType.GetSign();
if (inferredPrimitive.GetSize() <= primitiveType.GetSize() && sameSign)
return false;
if (inferredPrimitive.GetSize() == primitiveType.GetSize() && !sameSign)
canChangeSign = primitiveType.HasOppositeSign();
return true;
}
}
return true;
@ -790,6 +849,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -790,6 +849,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
if (conv is not null)
{
var primitiveType = targetType.ToPrimitiveType();
if (primitiveType.GetSize() == conv.TargetType.GetSize() && primitiveType.GetSign() != conv.TargetType.GetSign())
targetType = SwapSign(targetType, context.TypeSystem);
}
if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings))
return false;
stloc = binary.Left as StLoc;
@ -858,10 +925,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -858,10 +925,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var tmpVar = inst.Variable;
if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem))
return false;
if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem))
if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem, out bool canChangeSign))
{
// 'stloc tmp' is implicitly truncating the value
return false;
// If 'store' is a stobj and 'canChangeSign' is true, then the
// implicit truncation can be skipped by flipping the sign of the `stobj` type.
if (!canChangeSign || store is not StObj stObj || !stObj.Type.Equals(targetType))
{
// 'stloc tmp' is implicitly truncating the value
return false;
}
}
if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch,
forbiddenVariable: inst.Variable,
@ -883,6 +955,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -883,6 +955,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
else if (!(binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1)))
return false;
if (canChangeSign && store is StObj stObj)
{
// Change the sign of the type to skip implicit truncation
stObj.Type = targetType = SwapSign(targetType, context.TypeSystem);
}
if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings))
return false;
context.Step("TransformPostIncDecOperator (builtin)", inst);
@ -899,6 +976,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -899,6 +976,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (operatorCall.IsLifted)
return false; // TODO: add tests and think about whether nullables need special considerations
context.Step("TransformPostIncDecOperator (user-defined)", inst);
if (canChangeSign && store is StObj stObj)
{
// Change the sign of the type to skip implicit truncation
stObj.Type = targetType = SwapSign(targetType, context.TypeSystem);
}
finalizeMatch?.Invoke(context);
inst.Value = new UserDefinedCompoundAssign(operatorCall.Method,
CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1));

Loading…
Cancel
Save