[c#] 이 안전하지 않은 코드가 .NET Core 3에서도 작동해야합니까?

Span<T>가능한 경우 힙 할당을 피하기 위해 라이브러리를 리팩토링하고 있지만 이전 프레임 워크를 대상으로하므로 일반적인 대체 솔루션도 구현하고 있습니다. 그러나 지금 이상한 문제가 발견되어 .NET Core 3에서 버그를 발견했는지 아니면 불법적 인 일을하고 있는지 확실하지 않습니다.

문제:

// This returns 1 as expected but cannot be used in older frameworks:
private static uint ReinterpretNew()
{
    Span<byte> bytes = stackalloc byte[4];
    bytes[0] = 1; // FillBytes(bytes);

    // returning bytes as uint:
    return Unsafe.As<byte, uint>(ref bytes.GetPinnableReference());
}

// This returns garbage in .NET Core 3.0 with release build:
private static unsafe uint ReinterpretOld()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1; // FillBytes(bytes);

    // returning bytes as uint:
    return *(uint*)bytes;
}

흥미롭게도 ReinterpretOld.NET Framework 및 .NET Core 2.0에서 잘 작동하지만 결국 행복 할 수 있습니다.

Btw. ReinterpretOld.NET Core 3.0에서도 약간 수정하여 수정할 수 있습니다.

//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;

내 질문:

버그입니까, 아니면 ReinterpretOld우연히 이전 프레임 워크에서 작동합니까? 수정 사항도 적용해야합니까?

비고 :

  • 디버그 빌드는 .NET Core 3.0에서도 작동합니다.
  • 나는 적용하는 시도 [MethodImpl(MethodImplOptions.NoInlining)]ReinterpretOld하지만 아무런 영향을 미치지 않습니다.


답변

오, 이것은 재미있는 발견입니다. 여기에서 일어나고있는 것은 지역이 멀리 최적화지고 있다는 것입니다 – 남아있는 지역 주민, 아니이 있음을 의미가없는 .locals init것을 의미 stackalloc동작합니다는 다르게 , 그리고 공간을 닦지 않고는,

private static unsafe uint Reinterpret1()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1;

    return *(uint*)bytes;
}

private static unsafe uint Reinterpret2()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1;

    uint* asUint = (uint*)bytes;
    return *asUint;
}

된다 :

.method private hidebysig static uint32 Reinterpret1() cil managed
{
    .maxstack 8
    L_0000: ldc.i4.4
    L_0001: conv.u
    L_0002: localloc
    L_0004: dup
    L_0005: ldc.i4.1
    L_0006: stind.i1
    L_0007: ldind.u4
    L_0008: ret
}

.method private hidebysig static uint32 Reinterpret2() cil managed
{
    .maxstack 3
    .locals init (
        [0] uint32* numPtr)
    L_0000: ldc.i4.4
    L_0001: conv.u
    L_0002: localloc
    L_0004: dup
    L_0005: ldc.i4.1
    L_0006: stind.i1
    L_0007: stloc.0
    L_0008: ldloc.0
    L_0009: ldind.u4
    L_000a: ret
}

내가 생각하는 바람직하지 않은 부작용과 행동 주어진 :이 컴파일러의 버그, 또는 적어도 말을 드리겠습니다 이전 결정 “이 .locals는 init을 방출 ‘라고하는 장소에 배치되고 , 특히 시도하고 stackalloc제정신을 유지하십시오 -그러나 컴파일러 사람들의 동의 여부는 그들에게 달려 있습니다.

해결 방법은 stackalloc공간을 정의되지 않은 것으로 처리합니다 (공평하게 말하자면 수행하려는 작업입니다). 0이 될 것으로 예상되는 경우 수동으로 0으로 설정하십시오.


답변