Mike
Mike

Reputation: 4051

How is the source code for multidimensional array in C# generated

For example we have a multidimensional array of doubles double[,] d = new double[1,2];

d.GetType() returns {Name = "Double[,]" FullName = "System.Double[,]"}

d[0,0] is compiled to call instance float64 float64[0..., 0...]::Get(int32, int32) IL

How is the source code of type System.Double[,] generated? Is it baked in CLR or Roslyn is responsible for its generation?

Upvotes: 3

Views: 610

Answers (2)

svick
svick

Reputation: 244777

Array types are handled in a special way by the CLR (both single-dimensional and multi-dimensional arrays and each in a different special way). For multi-dimensional arrays, all Roslyn does is to call the Get() method and the CLR takes care of the rest.

What exactly the CLR does is quite complicated (at least it seems that way to me), but I believe the most relevant part starts in Lowering::LowerArrElem.

The end result is that a method like this one:

[MethodImpl(MethodImplOptions.NoInlining)]
private static double Get(double[,] d)
{
    return d[0, 0];
}

compiles to the this x64 code (comments mine):

// stack pointer adjustment, not interesting
sub         rsp,28h  

// eax = 0
xor         eax,eax  

// range check first dimension against eax
sub         eax,dword ptr [rcx+18h]  
cmp         eax,dword ptr [rcx+10h]  
jae         00007FFD0A554521  

// edx = 0
xor         edx,edx  

// range check second dimension against edx
sub         edx,dword ptr [rcx+1Ch]  
cmp         edx,dword ptr [rcx+14h]  
jae         00007FFD0A554521  

// compute item offset
mov         r8d,dword ptr [rcx+14h]  
imul        r8,rax  
mov         rax,rdx  
add         rax,r8  

// load result into xmm0
movsd       xmm0,mmword ptr [rcx+rax*8+20h]  

// stack pointer adjustment, not interesting
add         rsp,28h  

// return
ret

Upvotes: 1

xanatos
xanatos

Reputation: 111860

What you are looking for is in the arraynative.cpp and arraynative.h.

Starting from Array.cs:

public unsafe Object GetValue(params int[] indices)

uses

fixed(int* pIndices = indices)
    InternalGetReference(&elemref, indices.Length, pIndices);

Where InternalGetReference() is (same file):

[MethodImplAttribute(MethodImplOptions.InternalCall)]
// reference to TypedReference is banned, so have to pass result as pointer
private unsafe extern void InternalGetReference(void * elemRef, int rank, int * pIndices);

The MethodImplOptions.InternalCall are defined in ecalllist.h (remember this file... It contains all the MethodImplOptions.InternalCall, so it is quite useful) (if you don't remember the name of the file, you can simply search for InternalGetReference in the github... there aren't many files containing that word):

FCFuncElement("InternalGetReference", ArrayNative::GetReference)

So you have to look for ArrayNative, that is in the two files I linked.

Upvotes: 6

Related Questions