Browse Source

Builtin unsigned right shift operator.

pull/2994/head
Daniel Grunwald 2 years ago
parent
commit
8b9ba20847
  1. 74
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  2. 34
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 1
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs
  5. 10
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

74
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -1691,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1691,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void ShortUnsignedRightShiftTest(short p, CustomClass c, CustomStruct2 s)
{
//X(p >>>= 5);
shortField >>>= 5;
ShortProp >>>= 5;
c.ShortField >>>= 5;
c.ShortProp >>>= 5;
s.ShortField >>>= 5;
s.ShortProp >>>= 5;
customClassField.ShortField >>>= 5;
customClassField.ShortProp >>>= 5;
otherCustomStructField.ShortField >>>= 5;
otherCustomStructField.ShortProp >>>= 5;
CustomClassProp.ShortField >>>= 5;
CustomClassProp.ShortProp >>>= 5;
GetClass().ShortField >>>= 5;
GetClass().ShortProp >>>= 5;
GetRefStruct().ShortField >>>= 5;
GetRefStruct().ShortProp >>>= 5;
GetRefShort() >>>= 5;
}
#endif
public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s)
{
//short l = 0;
@ -2053,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -2053,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void UshortUnsignedRightShiftTest(ushort p, CustomClass c, CustomStruct2 s)
{
//ushort l = 0;
//p >>>= 5;
//l >>>= 5;
ushortField >>>= 5;
UshortProp >>>= 5;
c.UshortField >>>= 5;
c.UshortProp >>>= 5;
s.UshortField >>>= 5;
s.UshortProp >>>= 5;
customClassField.UshortField >>>= 5;
customClassField.UshortProp >>>= 5;
otherCustomStructField.UshortField >>>= 5;
otherCustomStructField.UshortProp >>>= 5;
CustomClassProp.UshortField >>>= 5;
CustomClassProp.UshortProp >>>= 5;
GetClass().UshortField >>>= 5;
GetClass().UshortProp >>>= 5;
GetRefStruct().UshortField >>>= 5;
GetRefStruct().UshortProp >>>= 5;
GetRefUshort() >>>= 5;
}
#endif
public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s)
{
//ushort l = 0;
@ -2415,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -2415,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void IntUnsignedRightShiftTest(int p, CustomClass c, CustomStruct2 s)
{
X(p >>>= 5);
intField >>>= 5;
IntProp >>>= 5;
c.IntField >>>= 5;
c.IntProp >>>= 5;
s.IntField >>>= 5;
s.IntProp >>>= 5;
customClassField.IntField >>>= 5;
customClassField.IntProp >>>= 5;
otherCustomStructField.IntField >>>= 5;
otherCustomStructField.IntProp >>>= 5;
CustomClassProp.IntField >>>= 5;
CustomClassProp.IntProp >>>= 5;
GetClass().IntField >>>= 5;
GetClass().IntProp >>>= 5;
GetRefStruct().IntField >>>= 5;
GetRefStruct().IntProp >>>= 5;
GetRefInt() >>>= 5;
}
#endif
public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s)
{
//int l = 0;

34
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1745,10 +1745,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1745,10 +1745,25 @@ namespace ICSharpCode.Decompiler.CSharp
Sign sign = inst.Sign;
var leftUType = NullableType.GetUnderlyingType(left.Type);
if (leftUType.IsCSharpSmallIntegerType() && sign != Sign.Unsigned && inst.UnderlyingResultType == StackType.I4)
bool couldUseUnsignedRightShift = (
sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift
&& (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType())
);
if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 &&
(sign != Sign.Unsigned || couldUseUnsignedRightShift))
{
// With small integer types, C# will promote to int and perform signed shifts.
// We thus don't need any casts in this case.
// The >>> operator also promotes to signed int, but then performs an unsigned shift.
if (sign == Sign.Unsigned)
{
op = BinaryOperatorType.UnsignedShiftRight;
}
}
else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize())
{
// Use C# 11 unsigned right shift operator. We don't need any casts in this case.
op = BinaryOperatorType.UnsignedShiftRight;
}
else
{
@ -1890,7 +1905,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1890,7 +1905,22 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.ShiftLeft:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft);
case BinaryNumericOperator.ShiftRight:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight);
if (inst.Sign == Sign.Unsigned && inst.Type.GetSign() == Sign.Signed)
{
Debug.Assert(settings.UnsignedRightShift);
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else if (inst.Sign == Sign.Unsigned && inst.Type.IsCSharpSmallIntegerType() && settings.UnsignedRightShift)
{
// For small unsigned integer types promoted to signed int, the sign bit will be zero,
// so there is no difference between signed and unsigned shift.
// However the IL still indicates which C# operator was used, so preserve that if the setting allows us to.
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else
{
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight);
}
default:
throw new ArgumentOutOfRangeException();
}

1
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs

@ -208,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -208,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case BinaryOperatorType.NullCoalescing:
return ExpressionType.Coalesce;
case BinaryOperatorType.Range:
case BinaryOperatorType.UnsignedShiftRight:
return ExpressionType.Extension;
default:
throw new NotSupportedException("Invalid value for BinaryOperatorType");

2
ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs

@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.UnsignedShiftRight:
return AssignmentOperatorType.UnsignedShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:

10
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL @@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL
return false; // operator not supported on pointer types
}
}
else if (type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr))
else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt)
{
// If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr):
// "target.intptr *= 2;" is compiler error, but
// "target.intptr *= (nint)2;" works
if (settings != null && !settings.NativeIntegers)
@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL @@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL
}
if (binary.Sign != Sign.None)
{
bool signMismatchAllowed = (binary.Sign == Sign.Unsigned && binary.Operator == BinaryNumericOperator.ShiftRight && (settings == null || settings.UnsignedRightShift));
if (type.IsCSharpSmallIntegerType())
{
// C# will use numeric promotion to int, binary op must be signed
if (binary.Sign != Sign.Signed)
if (binary.Sign != Sign.Signed && !signMismatchAllowed)
return false;
}
else
{
// C# will use sign from type
if (type.GetSign() != binary.Sign)
// C# will use sign from type; except for right shift with C# 11 >>> operator.
if (type.GetSign() != binary.Sign && !signMismatchAllowed)
return false;
}
}

Loading…
Cancel
Save