程序员开发实例大全宝库

网站首页 > 编程文章 正文

.Net8顶级技术--C#源码是如何一步步变成机器码的(二)?

zazugpt 2024-09-07 01:50:46 编程文章 21 ℃ 0 评论

前言

你写的C#源代码是如何变成机器码,在特定的平台上运行的呢?比如X64,Arm64,Risc-V64这些平台。本篇来一步步剖析下,以下以最广泛的X64为例。上一篇:.Net8的顶级技术JIT机器码生成


概括

1.C#源码

static void Main(){ Console.WriteLine("Hello World");}

假设有以上简单的C#源码,它的第一步是通过Roslyn前端编译把它编译成MSIL代码

2.IL代码
IL代码分为两类
一.动态链接库的IL代码

.method private hidebysig static void Main() cil managed{ .entrypoint // 代码大小 11 (0xb) .maxstack 8 IL_0000: ldstr "Hello World" IL_0005: call void [System.Console]System.Console::WriteLine(string) IL_000a: ret} // end of method Program::Main

二.导入JIT的IL代码

IL to import:IL_0000 72 01 00 00 70 ldstr 0x70000001IL_0005 28 0b 00 00 0a call 0xA00000BIL_000a 2a ret

为什么有两种类型的ILd代码?因为动态链接库里面的是完全的IL代码。它需要的是随时提供完整的信息,而通过CLR导入到JIT的IL代码,则是需要被编译的代码。所以信息量就比较少。

3.IR(中间表示)
这个IR是所有编译器必备的表达方式,也就是把IL代码转换成IR中间表示,此后通过IR转换成响应平台的机器码

INS_push REG_RBPINS_push REG_RDIINS_sub REG_RSP, 0INS_lea //此上的四个IR中间表示,的意思是分配栈空间,且保存RBP和RDI寄存器
INS_cmpINS_je //这两句IR表示,是否调用调用调试器
INS_call //是的话就调用调试器
INS_nopINS_mov REG_RCX, 1 //传递给Console.WriteLine的参数INS_call //Console.WriteLineINS_nopINS_nop //返回的ret指令

4.机器码

push rbppush rdisub rsp,28hlea rbp,[rsp+30h]
cmp dword ptr [2878031C100h],0 je 00000287801399E1 //很明显这个地方是调试信息
call 00000287DEAD7068
nopmov rcx,28780209C88hcall qword ptr [287807C53F8h] //这里就是调用Console.WriteLinenopret

可以看到机器码与IL一一对应。以上就是完整的源码变成机器码的过程。这里面细节非常多,成千上万。


结尾

其实看到,从C#源代码到机器码,中间经历了一些过程。尤其是IR中间表示这一层,是比较重要的节点。比如JIT的优化,PGO,OSR,GDV,IH,LC等都是在一层进行的。
作者:江湖评谈

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表