Skip to content

Commit b50d68c

Browse files
authored
Merge pull request #3413 from ds5678/use-type-hint-in-pointer-arithmetic
Use type hint in pointer arithmetic
2 parents dba836c + ceed9eb commit b50d68c

File tree

7 files changed

+164
-9
lines changed

7 files changed

+164
-9
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
<Compile Include="TestCases\ILPretty\MonoFixed.cs" />
138138
<Compile Include="TestCases\Pretty\Comparisons.cs" />
139139
<Compile Include="TestCases\Pretty\Issue3406.cs" />
140+
<Compile Include="TestCases\Pretty\PointerArithmetic.cs" />
140141
<Compile Include="TestCases\Pretty\Issue3439.cs" />
141142
<Compile Include="TestCases\Pretty\Issue3442.cs" />
142143
<None Include="TestCases\VBPretty\VBAutomaticEvents.vb" />

ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,12 @@ public async Task MetadataAttributes([ValueSource(nameof(defaultOptions))] Compi
730730
await RunForLibrary(cscOptions: cscOptions);
731731
}
732732

733+
[Test]
734+
public async Task PointerArithmetic([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
735+
{
736+
await RunForLibrary(cscOptions: cscOptions);
737+
}
738+
733739
async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action<DecompilerSettings> configureDecompiler = null)
734740
{
735741
await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, configureDecompiler);

ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public unsafe void ProblemFunction(Guid[] A_0, int A_1)
1111
fixed (Guid* ptr = A_0)
1212
{
1313
void* ptr2 = ptr;
14-
UIntPtr* ptr3 = (UIntPtr*)((byte*)ptr2 - sizeof(UIntPtr));
14+
UIntPtr* ptr3 = (UIntPtr*)ptr2 - 1;
1515
UIntPtr uIntPtr = *ptr3;
1616
try
1717
{

ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,16 +222,16 @@ public unsafe string NegativeOffsets(int a, int b, int c)
222222
#if OPT
223223
byte* num = stackalloc byte[12];
224224
*(int*)num = 1;
225-
*(int*)(num - 4) = 2;
226-
*(int*)(num - 8) = 3;
225+
*((int*)num - 1) = 2;
226+
*((int*)num - 2) = 3;
227227
int* ptr = (int*)num;
228228
Console.WriteLine(*ptr);
229229
return UsePointer((byte*)ptr);
230230
#else
231231
byte* ptr = stackalloc byte[12];
232232
*(int*)ptr = 1;
233-
*(int*)(ptr - 4) = 2;
234-
*(int*)(ptr - 8) = 3;
233+
*((int*)ptr - 1) = 2;
234+
*((int*)ptr - 2) = 3;
235235
int* ptr2 = (int*)ptr;
236236
Console.WriteLine(*ptr2);
237237
return UsePointer((byte*)ptr2);
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using System;
2+
3+
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
4+
{
5+
public class PointerArithmetic
6+
{
7+
public unsafe static void AssignmentVoidPointerToIntPointer(void* ptr)
8+
{
9+
((int*)ptr)[2] = 1;
10+
}
11+
12+
public unsafe static int AccessVoidPointerToIntPointer(void* ptr)
13+
{
14+
return ((int*)ptr)[2];
15+
}
16+
17+
public unsafe static void AssignmentLongPointerToIntPointer_2(long* ptr)
18+
{
19+
((int*)ptr)[2] = 1;
20+
}
21+
22+
public unsafe static int AccessLongPointerToIntPointer_2(long* ptr)
23+
{
24+
return ((int*)ptr)[2];
25+
}
26+
27+
public unsafe static void AssignmentLongPointerToIntPointer_3(long* ptr)
28+
{
29+
((int*)ptr)[3] = 1;
30+
}
31+
32+
public unsafe static int AccessLongPointerToIntPointer_3(long* ptr)
33+
{
34+
return ((int*)ptr)[3];
35+
}
36+
37+
public unsafe static void AssignmentGuidPointerToIntPointer(Guid* ptr)
38+
{
39+
((int*)ptr)[2] = 1;
40+
}
41+
42+
public unsafe static int AccessGuidPointerToIntPointer(Guid* ptr)
43+
{
44+
return ((int*)ptr)[2];
45+
}
46+
47+
public unsafe static uint AccessGuidPointerToUIntPointer(Guid* ptr)
48+
{
49+
return ((uint*)ptr)[2];
50+
}
51+
52+
public unsafe static void AssignmentGuidPointerToDateTimePointer(Guid* ptr)
53+
{
54+
((DateTime*)ptr)[2] = DateTime.Now;
55+
}
56+
57+
public unsafe static void AssignmentGuidPointerToDateTimePointerDefault(Guid* ptr)
58+
{
59+
((DateTime*)ptr)[2] = default(DateTime);
60+
}
61+
62+
public unsafe static void AssignmentGuidPointerToDateTimePointer_2(Guid* ptr)
63+
{
64+
*(DateTime*)(ptr + 2) = DateTime.Now;
65+
}
66+
67+
public unsafe static void AssignmentGuidPointerToDateTimePointerDefault_2(Guid* ptr)
68+
{
69+
*(DateTime*)(ptr + 2) = default(DateTime);
70+
}
71+
72+
public unsafe static DateTime AccessGuidPointerToDateTimePointer(Guid* ptr)
73+
{
74+
return ((DateTime*)ptr)[2];
75+
}
76+
77+
public unsafe static DateTime AccessGuidPointerToDateTimePointer_2(Guid* ptr)
78+
{
79+
return *(DateTime*)(ptr + 2);
80+
}
81+
82+
public unsafe static void AssignmentIntPointer(int* ptr)
83+
{
84+
ptr[2] = 1;
85+
}
86+
87+
public unsafe static int AccessIntPointer(int* ptr)
88+
{
89+
return ptr[2];
90+
}
91+
92+
public unsafe static void AssignmentGuidPointer(Guid* ptr)
93+
{
94+
ptr[2] = Guid.NewGuid();
95+
}
96+
97+
public unsafe static Guid AccessGuidPointer(Guid* ptr)
98+
{
99+
return ptr[2];
100+
}
101+
102+
public unsafe static void AssignmentVoidPointerToGuidPointer(void* ptr)
103+
{
104+
((Guid*)ptr)[2] = Guid.NewGuid();
105+
}
106+
107+
public unsafe static Guid AccessVoidPointerToGuidPointer(void* ptr)
108+
{
109+
return ((Guid*)ptr)[2];
110+
}
111+
112+
public unsafe static void AssignmentIntPointerToGuidPointer(int* ptr)
113+
{
114+
((Guid*)ptr)[2] = Guid.NewGuid();
115+
}
116+
117+
public unsafe static void AssignmentIntPointerToGuidPointer_2(int* ptr)
118+
{
119+
*(Guid*)(ptr + 2) = Guid.NewGuid();
120+
}
121+
122+
public unsafe static Guid AccessIntPointerToGuidPointer(int* ptr)
123+
{
124+
return ((Guid*)ptr)[2];
125+
}
126+
}
127+
}

ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ public unsafe int PointerCasts()
212212
{
213213
int result = 0;
214214
*(float*)(&result) = 0.5f;
215-
((byte*)(&result))[3] = 3;
215+
((sbyte*)(&result))[3] = 3;
216+
((sbyte*)(&result))[3] = -1;
216217
return result;
217218
}
218219

ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ protected internal override TranslatedExpression VisitBinaryNumericInstruction(B
12201220
/// Returns null if 'inst' is not performing pointer arithmetic.
12211221
/// 'ptr - ptr' is not handled here, but in HandlePointerSubtraction()!
12221222
/// </summary>
1223-
TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right)
1223+
TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right, TranslationContext context)
12241224
{
12251225
if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub))
12261226
return null;
@@ -1249,7 +1249,27 @@ protected internal override TranslatedExpression VisitBinaryNumericInstruction(B
12491249
{
12501250
return null;
12511251
}
1252-
TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow)
1252+
TranslatedExpression? offsetExpressionFromTypeHint = null;
1253+
if (context.TypeHint.Kind == TypeKind.Pointer)
1254+
{
1255+
// We use the type hint if one of the following is true:
1256+
// * The current element type is a non-primitive struct.
1257+
// * The current element type has a different size than the type hint element type.
1258+
// This prevents the type hint from overriding in undesirable situations (eg changing char* to short*).
1259+
1260+
var typeHint = (PointerType)context.TypeHint;
1261+
int elementTypeSize = pointerType.ElementType.GetSize();
1262+
if (elementTypeSize == 0 || typeHint.ElementType.GetSize() != elementTypeSize)
1263+
{
1264+
offsetExpressionFromTypeHint = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, typeHint.ElementType, inst.CheckForOverflow);
1265+
if (offsetExpressionFromTypeHint != null)
1266+
{
1267+
pointerType = typeHint;
1268+
}
1269+
}
1270+
}
1271+
TranslatedExpression offsetExpr = offsetExpressionFromTypeHint
1272+
?? GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow)
12531273
?? FallBackToBytePointer();
12541274

12551275
if (left.Type.Kind == TypeKind.Pointer)
@@ -1534,7 +1554,7 @@ TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOp
15341554
}
15351555
if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer)
15361556
{
1537-
var ptrResult = HandlePointerArithmetic(inst, left, right);
1557+
var ptrResult = HandlePointerArithmetic(inst, left, right, context);
15381558
if (ptrResult != null)
15391559
return ptrResult.Value;
15401560
}

0 commit comments

Comments
 (0)