How to build a struct large than 64kb using Native AOT?

Yotic 20 Reputation points
2023-04-27T22:04:10.66+00:00

I'm macking a struct consisting of several constants, vars (int and byte), and one 64kb fixed-size array of bytes. When I build using NAOT by command "dotnet publish -r win-x64 -c Release" I get error "ILC: Method will always throw because: Array of type cannot be created because base value type is too large"
I found out that if reduce the size of the array to 64Kb - 17b, then the code builds successfully. That is, if the structure is more than 16kb, then it cant to build it. What is it related to and how can I solve it? (Also I found it https://github.com/dotnet/runtime/blob/4719135a5fd6c9f22ce429ad8a211d696eae0426/src/coreclr/gc/gcdesc.h#L265), https://github.com/dotnet/runtime/blob/6246ce37ef665f5dc08b542a1ab092d90293eb62/src/coreclr/vm/array.cpp#L401-L405)

Also I was get the full error stack - https://pastebin.com/Aj8y11Nq

[StructLayout(LayoutKind.Sequential)]
public unsafe struct DChunk
{
    public const byte CHUNK_SECTION_SHIFT = 11;
    public const int CHUNK_SECTION_BYTES = 2 << CHUNK_SECTION_SHIFT;
    public const int BLOCKS_COUNT = 2 << CHUNK_SECTION_SHIFT << 4;

    public DChunk(int x, int z, byte[] blocks, bool fullChunk, byte lastChunkIndex = 15)
    {
        X = x;
        Z = z;

        fixed (byte* originalBlocksPtr = Blocks)
            fixed (byte* blocksPtr = blocks)
                Buffer.MemoryCopy(originalBlocksPtr, blocksPtr, blocks.Length, blocks.Length);

        FullChunk = fullChunk;
        LastChunkIndex = lastChunkIndex;
    }

    public fixed byte Blocks[BLOCKS_COUNT - 17]; //Change -17 to -16 and you will get error
    public byte LastChunkIndex;
    public int X, Z;
    public bool FullChunk;
}

public class Program
{

    static List<DChunk> chunks = new List<DChunk>();
    public static void Main()
    {
        byte[] blocks = ;
        DChunk chunk = new DChunk(0, 0, new byte[0] blocks, true, 0);
        Console.WriteLine(chunk.X + " " + chunk.Z);

        chunks.Add(chunk);
        DChunk c = chunks[0];
        chunks.RemoveAt(0);
        chunks.Add(c);
    }
}
Developer technologies | C#
{count} votes

Accepted answer
  1. Bruce (SqlWork.com) 78,006 Reputation points Volunteer Moderator
    2023-04-28T15:21:04.7266667+00:00

    the old limit was 16 bytes, and 20 bytes the new recommended limit. 64kb is well past the limit.

    the issue is in c# structs are value types and copies are passed to methods. being too large means it does not fit in a register, and the stack must be used. also if too large, it can not be copied with a single cpu instruction.

    here is an article better explaining struct use in C#:

    https://devblogs.microsoft.com/premier-developer/the-in-modifier-and-the-readonly-structs-in-c/

    you might be interested in Span<T> structs as they designed for manage low level memory:

    https://learn.microsoft.com/en-us/dotnet/api/system.span-1?view=net-8.0

    note: other languages like rust and swift, are designed for structs value types, and do not have a size limitation other than stack size.

    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.