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

本文详细阐述了C#源代码如何通过Roslyn编译成MSIL,然后转换为IR中间表示,并在X64平台上经JIT编译生成机器码的过程。文章强调了IR在优化如JIT、PGO等中的关键作用,并展示了从IL到机器码的具体对应示例。
摘要由CSDN通过智能技术生成

前言

你写的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        0x70000001
IL_0005  28 0b 00 00 0a    call         0xA00000B
IL_000a  2a                ret

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

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

INS_push REG_RBP
INS_push REG_RDI
INS_sub REG_RSP, 0
INS_lea  //此上的四个IR中间表示,的意思是分配栈空间,且保存RBP和RDI寄存器


INS_cmp
INS_je //这两句IR表示,是否调用调用调试器


INS_call //是的话就调用调试器


INS_nop
INS_mov REG_RCX, 1  //传递给Console.WriteLine的参数
INS_call //Console.WriteLine
INS_nop
INS_nop  //返回的ret指令

4.机器码

push        rbp
push        rdi
sub         rsp,28h
lea         rbp,[rsp+30h]


cmp  dword ptr [2878031C100h],0 
je   00000287801399E1  //很明显这个地方是调试信息


call  00000287DEAD7068


nop
mov rcx,28780209C88h
call qword ptr [287807C53F8h] //这里就是调用Console.WriteLine
nop
ret

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

结尾

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

cb7436885ca9ab35b429db37f2cb1f31.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值