C#中多维数组的源码是如何生成的
How is the source code for multidimensional array in C# generated
例如我们有一个双精度的多维数组
double[,] d = new double[1,2];
d.GetType()
returns {Name = "Double[,]" FullName = "System.Double[,]"}
d[0,0]
被编译为 call instance float64 float64[0..., 0...]::Get(int32, int32)
IL
System.Double[,]
类型的源代码是如何生成的?
它是在 CLR 中烘焙还是由 Roslyn 负责生成?
您要查找的内容在 arraynative.cpp and arraynative.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
。
数组类型由 CLR 以特殊方式处理(一维和多维数组,每一种都以不同的特殊方式处理)。对于多维数组,Roslyn 所做的只是 call
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
例如我们有一个双精度的多维数组
double[,] d = new double[1,2];
d.GetType()
returns {Name = "Double[,]" FullName = "System.Double[,]"}
d[0,0]
被编译为 call instance float64 float64[0..., 0...]::Get(int32, int32)
IL
System.Double[,]
类型的源代码是如何生成的?
它是在 CLR 中烘焙还是由 Roslyn 负责生成?
您要查找的内容在 arraynative.cpp and arraynative.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
。
数组类型由 CLR 以特殊方式处理(一维和多维数组,每一种都以不同的特殊方式处理)。对于多维数组,Roslyn 所做的只是 call
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