Browse Source

#1499, #1240: Translate pointer arithmetic on managed pointers (ref T) using the System.Runtime.CompilerServices.Unsafe intrinsics

pull/1505/head
Daniel Grunwald 6 years ago
parent
commit
961923f4fd
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 237
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  4. 439
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
  5. 79
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  6. 64
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  7. 2
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  8. 10
      ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
  9. 2
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  10. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
  11. 31
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  12. 5
      ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -88,6 +88,7 @@ @@ -88,6 +88,7 @@
<Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CustomAttributes2.cs" />
<Compile Include="TestCases\Pretty\EnumTests.cs" />
<None Include="TestCases\ILPretty\Unsafe.il" />
<None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Include="TestCases\Pretty\TypeMemberTests.cs" />
<Compile Include="TestCases\Pretty\ValueTypes.cs" />

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -160,6 +160,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -160,6 +160,12 @@ namespace ICSharpCode.Decompiler.Tests
Run();
}
[Test]
public void Unsafe()
{
Run();
}
[Test]
public void FSharpLoops_Debug()
{

237
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -0,0 +1,237 @@ @@ -0,0 +1,237 @@
using System;
using System.Reflection;
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0.0")]
[assembly: AssemblyTitle("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyDescription("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: CLSCompliant(false)]
namespace System.Runtime.CompilerServices
{
public static class Unsafe
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T Read<T>(void* source)
{
return *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T ReadUnaligned<T>(void* source)
{
return *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static T ReadUnaligned<T>(ref byte source)
{
return *(T*)(&source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Write<T>(void* destination, T value)
{
*(T*)destination = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void WriteUnaligned<T>(void* destination, T value)
{
*(T*)destination = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void WriteUnaligned<T>(ref byte destination, T value)
{
*(T*)(&destination) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Copy<T>(void* destination, ref T source)
{
*(T*)destination = source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void Copy<T>(ref T destination, void* source)
{
destination = *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* AsPointer<T>(ref T value)
{
return &value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static int SizeOf<T>()
{
return sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void CopyBlock(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyBlock(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void InitBlock(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitBlock(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T As<T>(object o) where T : class
{
return (T)o;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static ref T AsRef<T>(void* source)
{
return ref *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(in T source)
{
return ref source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static ref TTo As<TFrom, TTo>(ref TFrom source)
{
return ref *(TTo*)(&source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Unbox<T>(object box) where T : struct
{
return ref (T)box;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Add<T>(ref T source, int elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Add<T>(void* source, int elementOffset)
{
return (byte*)source + (long)elementOffset * (long)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Add<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AddByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.AddByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Subtract<T>(ref T source, int elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Subtract<T>(void* source, int elementOffset)
{
return (byte*)source - (long)elementOffset * (long)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Subtract<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T SubtractByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.SubtractByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref T origin, ref T target)
{
return Unsafe.ByteOffset(ref target, ref origin);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool AreSame<T>(ref T left, ref T right)
{
return (ref left) == (ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressGreaterThan<T>(ref T left, ref T right)
{
return (ref left) > (ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressLessThan<T>(ref T left, ref T right)
{
return (ref left) < (ref right);
}
}
}

439
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il

@ -0,0 +1,439 @@ @@ -0,0 +1,439 @@
#define CORE_ASSEMBLY "System.Runtime"
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 4:0:0:0
}
.assembly System.Runtime.CompilerServices.Unsafe
{
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 ) // ...4.0.0.0..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim
65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic
65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 26 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D // ..&System.Runtim
65 2E 43 6F 6D 70 69 6C 65 72 53 65 72 76 69 63 // e.CompilerServic
65 73 2E 55 6E 73 61 66 65 00 00 ) // es.Unsafe..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = (
01 00 15 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b
41 73 73 65 6d 62 6c 79 00 00 00
) // ".NETFrameworkAssembly", ""
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = (
01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54
72 75 65 00 00
) // "Serviceable", "True"
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20 // ../.. Microsoft
43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C // Corporation. Al
6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 // l rights reserve
64 2E 00 00 ) // d...
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 15 4D 69 63 72 6F 73 6F 66 74 20 43 6F 72 // ...Microsoft Cor
70 6F 72 61 74 69 6F 6E 00 00 ) // poration..
.custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1A 4D 69 63 72 6F 73 6F 66 74 C2 AE 20 2E // ...Microsoft.. .
4E 45 54 20 46 72 61 6D 65 77 6F 72 6B 00 00 ) // NET Framework..
.custom instance void [CORE_ASSEMBLY]System.CLSCompliantAttribute::.ctor(bool) = (
01 00 00 00 00
) // false
.hash algorithm 0x00008004
.ver 4:0:5:0
}
.module System.Runtime.CompilerServices.Unsafe.dll
// MVID: {1E97D84A-565B-49C5-B60A-F31A1A4ACE13}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02ED0000
// =============== CLASS MEMBERS DECLARATION ===================
.class public abstract auto ansi sealed beforefieldinit System.Runtime.CompilerServices.Unsafe
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig static !!T Read<T>(void* source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ldobj !!T
ret
} // end of method Unsafe::Read
.method public hidebysig static !!T ReadUnaligned<T>(void* source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unaligned. 0x1
ldobj !!T
ret
} // end of method Unsafe::ReadUnaligned
.method public hidebysig static !!T ReadUnaligned<T>(uint8& source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unaligned. 0x1
ldobj !!T
ret
} // end of method Unsafe::ReadUnaligned
.method public hidebysig static void Write<T>(void* destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
stobj !!T
ret
} // end of method Unsafe::Write
.method public hidebysig static void WriteUnaligned<T>(void* destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
unaligned. 0x01
stobj !!T
ret
} // end of method Unsafe::WriteUnaligned
.method public hidebysig static void WriteUnaligned<T>(uint8& destination,
!!T 'value') cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
unaligned. 0x01
stobj !!T
ret
} // end of method Unsafe::WriteUnaligned
.method public hidebysig static void Copy<T>(void* destination,
!!T& source) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ldobj !!T
stobj !!T
ret
} // end of method Unsafe::Copy
.method public hidebysig static void Copy<T>(!!T& destination,
void* source) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ldobj !!T
stobj !!T
ret
} // end of method Unsafe::Copy
.method public hidebysig static void* AsPointer<T>(!!T& 'value') cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
conv.u
ret
} // end of method Unsafe::AsPointer
.method public hidebysig static int32 SizeOf<T>() cil managed aggressiveinlining
{
.maxstack 1
sizeof !!T
ret
} // end of method Unsafe::SizeOf
.method public hidebysig static void CopyBlock(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
cpblk
ret
} // end of method Unsafe::CopyBlock
.method public hidebysig static void CopyBlock(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
cpblk
ret
} // end of method Unsafe::CopyBlock
.method public hidebysig static void CopyBlockUnaligned(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
cpblk
ret
} // end of method Unsafe::CopyBlockUnaligned
.method public hidebysig static void CopyBlockUnaligned(uint8& destination, uint8& source, uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
cpblk
ret
} // end of method Unsafe::CopyBlockUnaligned
.method public hidebysig static void InitBlock(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
initblk
ret
} // end of method Unsafe::InitBlock
.method public hidebysig static void InitBlock(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
initblk
ret
} // end of method Unsafe::InitBlock
.method public hidebysig static void InitBlockUnaligned(void* startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
initblk
ret
} // end of method Unsafe::InitBlockUnaligned
.method public hidebysig static void InitBlockUnaligned(uint8& startAddress, uint8 'value', uint32 byteCount) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
initblk
ret
} // end of method Unsafe::InitBlockUnaligned
.method public hidebysig static !!T As<class T>(object o) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::As
.method public hidebysig static !!T& AsRef<T>(void* source) cil managed aggressiveinlining
{
// For .NET Core the roundtrip via a local is no longer needed see:
// https://github.com/dotnet/coreclr/issues/13341
// and
// https://github.com/dotnet/coreclr/pull/11218
#ifdef netcoreapp
.maxstack 1
ldarg.0
ret
#else
.locals (int32&)
.maxstack 1
ldarg.0
// Roundtrip via a local to avoid type mismatch on return that the JIT inliner chokes on.
stloc.0
ldloc.0
ret
#endif
} // end of method Unsafe::AsRef
.method public hidebysig static !!T& AsRef<T>(!!T& source) cil managed aggressiveinlining
{
.param [1]
#ifdef netcoreapp
.custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 )
#else
.custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 )
#endif
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::AsRef
.method public hidebysig static !!TTo& As<TFrom, TTo>(!!TFrom& source) cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
ret
} // end of method Unsafe::As
.method public hidebysig static !!T& Unbox<valuetype .ctor ([CORE_ASSEMBLY]System.ValueType) T> (object 'box') cil managed aggressiveinlining
{
.maxstack 1
ldarg.0
unbox !!T
ret
} // end of method Unsafe::Unbox
.method public hidebysig static !!T& Add<T>(!!T& source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static void* Add<T>(void* source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static !!T& Add<T>(!!T& source, native int elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
mul
add
ret
} // end of method Unsafe::Add
.method public hidebysig static !!T& AddByteOffset<T>(!!T& source, native int byteOffset) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
add
ret
} // end of method Unsafe::AddByteOffset
.method public hidebysig static !!T& Subtract<T>(!!T& source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static void* Subtract<T>(void* source, int32 elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
conv.i
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static !!T& Subtract<T>(!!T& source, native int elementOffset) cil managed aggressiveinlining
{
.maxstack 3
ldarg.0
ldarg.1
sizeof !!T
mul
sub
ret
} // end of method Unsafe::Subtract
.method public hidebysig static !!T& SubtractByteOffset<T>(!!T& source, native int byteOffset) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
sub
ret
} // end of method Unsafe::SubtractByteOffset
.method public hidebysig static native int ByteOffset<T>(!!T& origin, !!T& target) cil managed aggressiveinlining
{
.maxstack 2
ldarg.1
ldarg.0
sub
ret
} // end of method Unsafe::ByteOffset
.method public hidebysig static bool AreSame<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
ceq
ret
} // end of method Unsafe::AreSame
.method public hidebysig static bool IsAddressGreaterThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
cgt.un
ret
} // end of method Unsafe::IsAddressGreaterThan
.method public hidebysig static bool IsAddressLessThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
{
.maxstack 2
ldarg.0
ldarg.1
clt.un
ret
} // end of method Unsafe::IsAddressLessThan
} // end of class System.Runtime.CompilerServices.Unsafe
#ifdef netcoreapp
#else
.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute
extends [CORE_ASSEMBLY]System.Attribute
{
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 1
ldarg.0
call instance void [CORE_ASSEMBLY]System.Attribute::.ctor()
ret
} // end of method IsReadOnlyAttribute::.ctor
} // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute
#endif

79
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -369,7 +369,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -369,7 +369,7 @@ namespace ICSharpCode.Decompiler.CSharp
pointerType = typeHint as PointerType;
if (pointerType != null && GetPointerArithmeticOffset(
inst.Argument, Translate(inst.Argument),
pointerType, checkForOverflow: true,
pointerType.ElementType, checkForOverflow: true,
unwrapZeroExtension: true
) is TranslatedExpression offset)
{
@ -958,7 +958,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -958,7 +958,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
return null;
}
TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType, inst.CheckForOverflow)
TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow)
?? FallBackToBytePointer();
if (left.Type.Kind == TypeKind.Pointer) {
@ -985,6 +985,68 @@ namespace ICSharpCode.Decompiler.CSharp @@ -985,6 +985,68 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
/// <summary>
/// Translates pointer arithmetic with managed pointers:
/// ref + int
/// int + ref
/// ref - int
/// ref - ref
/// </summary>
TranslatedExpression? HandleManagedPointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right)
{
if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub))
return null;
if (inst.CheckForOverflow || inst.IsLifted)
return null;
if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) {
// ref - ref => i
return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst);
}
if (inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.I
&& left.Type is ByReferenceType brt) {
// ref [+-] int
string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add");
ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Right, brt.ElementType, inst.CheckForOverflow);
if (offsetInst != null) {
return CallUnsafeIntrinsic(name, new[] { left.Expression, Translate(offsetInst).Expression }, brt, inst);
} else {
return CallUnsafeIntrinsic(name + "ByteOffset", new[] { left.Expression, right.Expression }, brt, inst);
}
}
brt = right.Type as ByReferenceType;
if (inst.LeftInputType == StackType.I && inst.RightInputType == StackType.Ref && brt != null
&& inst.Operator == BinaryNumericOperator.Add) {
// int + ref
ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Left, brt.ElementType, inst.CheckForOverflow);
if (offsetInst != null) {
return CallUnsafeIntrinsic("Add", new[] {
new NamedArgumentExpression("elementOffset", Translate(offsetInst)),
new NamedArgumentExpression("source", right)
}, brt, inst);
} else {
return CallUnsafeIntrinsic("AddByteOffset", new[] {
new NamedArgumentExpression("byteOffset", left.Expression),
new NamedArgumentExpression("source", right)
}, brt, inst);
}
}
return null;
}
internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst)
{
var target = new MemberReferenceExpression {
Target = new TypeReferenceExpression(astBuilder.ConvertType(compilation.FindType(KnownTypeCode.Unsafe))),
MemberName = name
};
var invocation = new InvocationExpression(target, arguments).WithILInstruction(inst);
if (returnType is ByReferenceType brt) {
return WrapInRef(invocation.WithRR(new ResolveResult(brt.ElementType)), brt);
} else {
return invocation.WithRR(new ResolveResult(returnType));
}
}
TranslatedExpression EnsureIntegerType(TranslatedExpression expr)
{
if (!expr.Type.IsCSharpPrimitiveIntegerType()) {
@ -998,9 +1060,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -998,9 +1060,9 @@ namespace ICSharpCode.Decompiler.CSharp
}
TranslatedExpression? GetPointerArithmeticOffset(ILInstruction byteOffsetInst, TranslatedExpression byteOffsetExpr,
PointerType pointerType, bool checkForOverflow, bool unwrapZeroExtension = false)
IType pointerElementType, bool checkForOverflow, bool unwrapZeroExtension = false)
{
var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerType,
var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerElementType,
checkForOverflow: checkForOverflow,
unwrapZeroExtension: unwrapZeroExtension);
if (countOffsetInst == null) {
@ -1086,6 +1148,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1086,6 +1148,11 @@ namespace ICSharpCode.Decompiler.CSharp
var left = Translate(inst.Left);
var right = Translate(inst.Right);
if (left.Type.Kind == TypeKind.ByReference || right.Type.Kind == TypeKind.ByReference) {
var ptrResult = HandleManagedPointerArithmetic(inst, left, right);
if (ptrResult != null)
return ptrResult.Value;
}
if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) {
var ptrResult = HandlePointerArithmetic(inst, left, right);
if (ptrResult != null)
@ -1350,7 +1417,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1350,7 +1417,7 @@ namespace ICSharpCode.Decompiler.CSharp
case AssignmentOperatorType.Add:
case AssignmentOperatorType.Subtract:
if (target.Type.Kind == TypeKind.Pointer) {
var pao = GetPointerArithmeticOffset(inst.Value, value, (PointerType)target.Type, inst.CheckForOverflow);
var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow);
if (pao != null) {
value = pao.Value;
} else {
@ -2411,7 +2478,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2411,7 +2478,7 @@ namespace ICSharpCode.Decompiler.CSharp
throw new ArgumentException("given Block is invalid!");
var binary = (BinaryNumericInstruction)target;
left = left.UnwrapConv(ConversionKind.StopGCTracking);
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow);
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType.ElementType, binary.CheckForOverflow);
if (!left.MatchLdLoc(final.Variable) || offsetInst == null)
throw new ArgumentException("given Block is invalid!");
if (!offsetInst.MatchLdcI(out offset))

64
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.settings = settings;
this.cancellationToken = cancellationToken;
}
public Statement Convert(ILInstruction inst)
{
cancellationToken.ThrowIfCancellationRequested();
@ -102,7 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -102,7 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
return stmt;
}
protected internal override Statement VisitIfInstruction(IfInstruction inst)
{
var condition = exprBuilder.TranslateCondition(inst.Condition);
@ -268,14 +268,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -268,14 +268,14 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
}
/// <summary>Target block that a 'continue;' statement would jump to</summary>
Block continueTarget;
/// <summary>Number of ContinueStatements that were created for the current continueTarget</summary>
int continueCount;
/// <summary>Maps blocks to cases.</summary>
Dictionary<Block, ConstantResolveResult> caseLabelMapping;
protected internal override Statement VisitBranch(Branch inst)
{
if (inst.TargetBlock == continueTarget) {
@ -289,12 +289,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -289,12 +289,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
return new GotoStatement(inst.TargetLabel);
}
/// <summary>Target container that a 'break;' statement would break out of</summary>
BlockContainer breakTarget;
/// <summary>Dictionary from BlockContainer to label name for 'goto of_container';</summary>
readonly Dictionary<BlockContainer, string> endContainerLabels = new Dictionary<BlockContainer, string>();
protected internal override Statement VisitLeave(Leave inst)
{
if (inst.TargetContainer == breakTarget)
@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
return new ThrowStatement(exprBuilder.Translate(inst.Argument));
}
protected internal override Statement VisitRethrow(Rethrow inst)
{
return new ThrowStatement();
@ -348,7 +348,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -348,7 +348,7 @@ namespace ICSharpCode.Decompiler.CSharp
tryCatch.TryBlock = tryBlockConverted as BlockStatement ?? new BlockStatement { tryBlockConverted };
return tryCatch;
}
protected internal override Statement VisitTryCatch(TryCatch inst)
{
var tryCatch = new TryCatchStatement();
@ -372,14 +372,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -372,14 +372,14 @@ namespace ICSharpCode.Decompiler.CSharp
}
return tryCatch;
}
protected internal override Statement VisitTryFinally(TryFinally inst)
{
var tryCatch = MakeTryCatch(inst.TryBlock);
tryCatch.FinallyBlock = ConvertAsBlock(inst.FinallyBlock);
return tryCatch;
}
protected internal override Statement VisitTryFault(TryFault inst)
{
var tryCatch = new TryCatchStatement();
@ -551,7 +551,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -551,7 +551,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Convert the modified body to C# AST:
var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
// Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
Statement firstStatement = foreachBody.Statements.First();
if (firstStatement is LabelStatement) {
@ -828,7 +828,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -828,7 +828,7 @@ namespace ICSharpCode.Decompiler.CSharp
return blockStmt;
}
}
Statement ConvertLoop(BlockContainer container)
{
ILInstruction condition;
@ -982,28 +982,36 @@ namespace ICSharpCode.Decompiler.CSharp @@ -982,28 +982,36 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override Statement VisitInitblk(Initblk inst)
{
var stmt = new ExpressionStatement(new InvocationExpression {
Target = new IdentifierExpression("memset"),
Arguments = {
exprBuilder.Translate(inst.Address),
exprBuilder.Translate(inst.Value),
exprBuilder.Translate(inst.Size)
}
});
var stmt = new ExpressionStatement(
exprBuilder.CallUnsafeIntrinsic(
inst.UnalignedPrefix != 0 ? "InitBlockUnaligned" : "InitBlock",
new Expression[] {
exprBuilder.Translate(inst.Address),
exprBuilder.Translate(inst.Value),
exprBuilder.Translate(inst.Size)
},
exprBuilder.compilation.FindType(KnownTypeCode.Void),
inst
)
);
stmt.InsertChildAfter(null, new Comment(" IL initblk instruction"), Roles.Comment);
return stmt;
}
protected internal override Statement VisitCpblk(Cpblk inst)
{
var stmt = new ExpressionStatement(new InvocationExpression {
Target = new IdentifierExpression("memcpy"),
Arguments = {
exprBuilder.Translate(inst.DestAddress),
exprBuilder.Translate(inst.SourceAddress),
exprBuilder.Translate(inst.Size)
}
});
var stmt = new ExpressionStatement(
exprBuilder.CallUnsafeIntrinsic(
inst.UnalignedPrefix != 0 ? "CopyBlockUnaligned" : "CopyBlock",
new Expression[] {
exprBuilder.Translate(inst.DestAddress),
exprBuilder.Translate(inst.SourceAddress),
exprBuilder.Translate(inst.Size)
},
exprBuilder.compilation.FindType(KnownTypeCode.Void),
inst
)
);
stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment);
return stmt;
}

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

@ -129,7 +129,7 @@ namespace ICSharpCode.Decompiler.IL @@ -129,7 +129,7 @@ namespace ICSharpCode.Decompiler.IL
// ensure that the byte offset is a multiple of the pointer size
return PointerArithmeticOffset.Detect(
binary.Right,
(PointerType)type,
((PointerType)type).ElementType,
checkForOverflow: binary.CheckForOverflow
) != null;
default:

10
ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs

@ -17,17 +17,17 @@ namespace ICSharpCode.Decompiler.IL @@ -17,17 +17,17 @@ namespace ICSharpCode.Decompiler.IL
/// Returns null if no such instruction can be found.
/// </summary>
/// <param name="byteOffsetInst">Input instruction.</param>
/// <param name="pointerType">The pointer type.</param>
/// <param name="pointerElementType">The target type of the pointer type.</param>
/// <param name="checkForOverflow">Whether the pointer arithmetic operation checks for overflow.</param>
/// <param name="unwrapZeroExtension">Whether to allow zero extensions in the mul argument.</param>
public static ILInstruction Detect(ILInstruction byteOffsetInst, PointerType pointerType,
public static ILInstruction Detect(ILInstruction byteOffsetInst, IType pointerElementType,
bool checkForOverflow,
bool unwrapZeroExtension = false)
{
if (byteOffsetInst is Conv conv && conv.InputType == StackType.I8 && conv.ResultType == StackType.I) {
byteOffsetInst = conv.Argument;
}
int? elementSize = ComputeSizeOf(pointerType.ElementType);
int? elementSize = ComputeSizeOf(pointerElementType);
if (elementSize == 1) {
return byteOffsetInst;
} else if (byteOffsetInst is BinaryNumericInstruction mul && mul.Operator == BinaryNumericOperator.Mul) {
@ -36,14 +36,14 @@ namespace ICSharpCode.Decompiler.IL @@ -36,14 +36,14 @@ namespace ICSharpCode.Decompiler.IL
if (mul.CheckForOverflow != checkForOverflow)
return null;
if (elementSize > 0 && mul.Right.MatchLdcI(elementSize.Value)
|| mul.Right.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerType.ElementType)) {
|| mul.Right.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerElementType)) {
var countOffsetInst = mul.Left;
if (unwrapZeroExtension) {
countOffsetInst = countOffsetInst.UnwrapConv(ConversionKind.ZeroExtend);
}
return countOffsetInst;
}
} else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerType.ElementType)) {
} else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerElementType)) {
return new LdcI4(1).WithILRange(byteOffsetInst);
} else if (byteOffsetInst.MatchLdcI(out long val)) {
// If the offset is a constant, it's possible that the compiler

2
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool MatchesElementCount(ILInstruction sizeInBytesInstr, IType elementType, ILInstruction elementCountInstr2)
{
var pointerType = new PointerType(elementType);
var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType, checkForOverflow: true, unwrapZeroExtension: true);
var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType.ElementType, checkForOverflow: true, unwrapZeroExtension: true);
if (!elementCountInstr.Match(elementCountInstr2).Success)
return false;
return true;

4
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -275,7 +275,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -275,7 +275,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!left.MatchLdLoc(store))
break;
var offsetInst = PointerArithmeticOffset.Detect(right, new PointerType(elementType), ((BinaryNumericInstruction)target).CheckForOverflow);
var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow);
if (offsetInst == null)
return false;
if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset)
@ -283,7 +283,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -283,7 +283,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
minExpectedOffset = offset;
}
if (values == null) {
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, new PointerType(elementType), checkForOverflow: true);
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true);
if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1)
return false;
values = new StObj[(int)valuesLength];

31
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -81,30 +81,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -81,30 +81,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out;
public bool IsRef {
get {
if (!(Type.Kind == TypeKind.ByReference && (attributes & inOut) != ParameterAttributes.Out))
return false;
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == 0)
return true;
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
return !parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
}
}
public bool IsRef => DetectRefKind() == CSharp.Syntax.ParameterModifier.Ref;
public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out;
public bool IsIn => DetectRefKind() == CSharp.Syntax.ParameterModifier.In;
public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0;
public bool IsIn {
get {
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == 0 ||
Type.Kind != TypeKind.ByReference || (attributes & inOut) != ParameterAttributes.In)
return false;
CSharp.Syntax.ParameterModifier DetectRefKind()
{
if (Type.Kind != TypeKind.ByReference)
return CSharp.Syntax.ParameterModifier.None;
if ((attributes & inOut) == ParameterAttributes.Out)
return CSharp.Syntax.ParameterModifier.Out;
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0) {
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly))
return CSharp.Syntax.ParameterModifier.In;
}
return CSharp.Syntax.ParameterModifier.Ref;
}
public bool IsParams {

5
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
ReadOnlySpanOfT,
/// <summary><c>System.Memory{T}</c></summary>
MemoryOfT,
/// <summary><c>System.Runtime.CompilerServices.Unsafe</c></summary>
Unsafe,
}
/// <summary>
@ -143,7 +145,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -143,7 +145,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
[Serializable]
public sealed class KnownTypeReference : ITypeReference
{
internal const int KnownTypeCodeCount = (int)KnownTypeCode.MemoryOfT + 1;
internal const int KnownTypeCodeCount = (int)KnownTypeCode.Unsafe + 1;
static readonly KnownTypeReference[] knownTypeReferences = new KnownTypeReference[KnownTypeCodeCount] {
null, // None
@ -200,6 +202,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -200,6 +202,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new KnownTypeReference(KnownTypeCode.SpanOfT, TypeKind.Struct, "System", "Span", 1),
new KnownTypeReference(KnownTypeCode.ReadOnlySpanOfT, TypeKind.Struct, "System", "ReadOnlySpan", 1),
new KnownTypeReference(KnownTypeCode.MemoryOfT, TypeKind.Struct, "System", "Memory", 1),
new KnownTypeReference(KnownTypeCode.Unsafe, TypeKind.Class, "System.Runtime.CompilerServices", "Unsafe", 0),
};
/// <summary>

Loading…
Cancel
Save