Browse Source

Add ILPretty regression test for duplicate stackalloc store

A stackalloc initializer whose first element is written twice must not be
folded into a 'stackalloc int[] { ... }': doing so drops the earlier store,
which is observable when its value has side effects. HandleSequentialLocAllocInitializer
already rejects this (the extra store trips the LoadCount and elementCount
guards); the sequential-store offset matcher on its own would misclassify the
second offset-0 write. The IL is derived from a C# 'stackalloc int[] { a, b, c }'
disassembly with an extra offset-0 store injected, a shape no compiler emits.

Assisted-by: Claude:claude-fable-5:Claude Code
master
Siegfried Pammer 10 hours ago committed by Siegfried Pammer
parent
commit
a308b64a7f
  1. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  2. 18
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs
  3. 70
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.il

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -279,6 +279,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -279,6 +279,12 @@ namespace ICSharpCode.Decompiler.Tests
await Run();
}
[Test]
public async Task StackAllocDuplicateStore()
{
await Run();
}
[Test, Platform("Win")] // UseLegacyAssembler requires the .NET Framework ilasm
public async Task Unsafe()
{

18
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public static class StackAllocDuplicateStore
{
public unsafe static int Seq(int a, int b, int c)
{
byte* num = stackalloc byte[12];
*(int*)num = a;
*(int*)num = 99;
((int*)num)[1] = b;
((int*)num)[2] = c;
Span<int> span = new Span<int>(num, 3);
return span[0] + span[1] + span[2];
}
}
}

70
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.il

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
// Metadata version: v4.0.30319
.assembly extern System.Runtime
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 9:0:0:0
}
.assembly StackAllocDuplicateStore
{
.ver 1:0:0:0
}
.module StackAllocDuplicateStore.dll
.corflags 0x00000001 // ILONLY
// A stackalloc initializer that writes the first element twice. The sequential-store
// initializer transform must not fold this into a 'stackalloc int[] { ... }': the earlier
// store would be dropped, which is observable when its value has side effects.
.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.StackAllocDuplicateStore
extends [System.Runtime]System.Object
{
.method public hidebysig static int32 Seq(int32 a,
int32 b,
int32 c) cil managed
{
.maxstack 4
.locals init (valuetype [System.Runtime]System.Span`1<int32> V_0)
IL_0000: ldc.i4.s 12
IL_0002: conv.u
IL_0003: localloc
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stind.i4
IL_0008: dup
IL_0009: ldc.i4.s 99
IL_000b: stind.i4
IL_000c: dup
IL_000d: ldc.i4.4
IL_000e: add
IL_000f: ldarg.1
IL_0010: stind.i4
IL_0011: dup
IL_0012: ldc.i4.2
IL_0013: conv.i
IL_0014: ldc.i4.4
IL_0015: mul
IL_0016: add
IL_0017: ldarg.2
IL_0018: stind.i4
IL_0019: ldc.i4.3
IL_001a: newobj instance void valuetype [System.Runtime]System.Span`1<int32>::.ctor(void*,
int32)
IL_001f: stloc.0
IL_0020: ldloca.s V_0
IL_0022: ldc.i4.0
IL_0023: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
IL_0028: ldind.i4
IL_0029: ldloca.s V_0
IL_002b: ldc.i4.1
IL_002c: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
IL_0031: ldind.i4
IL_0032: add
IL_0033: ldloca.s V_0
IL_0035: ldc.i4.2
IL_0036: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
IL_003b: ldind.i4
IL_003c: add
IL_003d: ret
} // end of method StackAllocDuplicateStore::Seq
} // end of class StackAllocDuplicateStore
Loading…
Cancel
Save