如何生成C#中多维数组的源代码

3
例如,我们有一个双精度多维数组: double[,] d = new double[1,2]; d.GetType() 返回 {Name = "Double[,]" FullName = "System.Double[,]"} d[0,0] 编译成了 call instance float64 float64[0..., 0...]::Get(int32, int32) IL
类型为 System.Double[,] 的源代码是如何生成的? 它是在CLR中内置的还是由Roslyn负责生成?

3
这是由编写并将其编译为.NET运行时的开发人员生成的。 - spender
我在 GitHub 上挖掘 coreclr 存储库,找不到那个逻辑的确切位置。 - Mike
@MikeMazmanyan 如果你不知道在哪里寻找,要在coreclr中找到低级别的东西是相当复杂的。 - xanatos
@xanatos 这就是为什么我需要 .Net 社区的帮助。 - Mike
2个回答

6
你需要的内容在arraynative.cpparraynative.h中。
Array.cs开始:
public unsafe Object GetValue(params int[] indices)

使用

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

其中InternalGetReference()在同一文件中:

[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);
MethodImplOptions.InternalCall是在ecalllist.h中定义的(记住这个文件...它包含了所有的MethodImplOptions.InternalCall,所以非常有用)(如果你不记得文件名,可以在github上简单搜索InternalGetReference...没有多少文件包含那个词):
FCFuncElement("InternalGetReference", ArrayNative::GetReference)

所以你需要查找 ArrayNative,它在我提供的两个文件中。


@MikeMazmanyan 如果你在 arraynative.cpp 的 1342 行附近看一下,我看到了一个非常具体的 InternalGetReference 实现,它可以完成所有操作。 - xanatos
@MikeMazmanyan,即使是ArrayNative :: CreateInstance似乎也非常具体...它充满了对类型系统的神奇调用...但它相当合理...我们在这里深入挖掘。 这是我不会碰的代码。 - xanatos
最初的问题在于多维数组和锯齿数组中get调用的差异。 - Mike
Array.GetValue(int[]) 不同于 double[,].Get(int, int) - svick
@svick 是的...你说得对...这甚至更糟...它可能在array.cpp中定义。似乎有一些代码生成器用于GetSetAddress这些“特殊”函数。 - xanatos

1

CLR(单维和多维数组)以不同的特殊方式处理数组类型。对于多维数组,Roslyn只需调用Get()方法,CLR会负责其余部分。

CLR实际上做了什么相当复杂(至少对我来说是这样),但我认为最相关的部分始于Lowering::LowerArrElem

最终结果是像这个方法一样的:

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

编译成这个x64代码(注释是我的):

// 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

你知道为什么Roslyn在多维数组中使用Get方法而不是一些特殊的IL指令吗? - Mike
@MikeMazmanyan 因为这是.Net规范所要求的(而且多维数组没有特殊的IL指令)。至于为什么规范会这样说,我不知道。 - svick
谢谢,遗憾的是 double[n,m]double[n][m] 更慢。 - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接