罕见的技术:.Net8机器码的生成

1.前言
任何编程语言最后运行在计算机上的一定是机器码,只有机器码才能被计算机底层硬件识别并执行,这里通过.Net8 PreView JIT的机器码生成来看下一些过程。

2.概述
一:概念
首先了解一些概念,比如地址,机器码,汇编等。我们先来看一段汇编代码:

00007FF85EAC0022 48 81 EC 88 00 00 00 sub         rsp,88h  
00007FF85EAC0029 48 8D AC 24 90 00 00 00 lea         rbp,[rsp+90h]  
00007FF85EAC0031 C5 D8 57 E4          vxorps      xmm4,xmm4,xmm4

以第一行代码为例,最前面的00007FF85EAC0022这种就是地址了

后面跟着的

48 81 EC 88 00 00 00

它就是机器码,代表了在硬件上指向的数据。再后面跟着的就是汇编代码了,也就是机器码的形容词,以方便人来观察这条机器码到底执行的是啥,因为如果人直接观察这种十六进制的机器码,根本分不清楚。

sub  rsp,88h

OK了解以上简单的概念,我们下面继续。

二:生成
我们从MSIL开始,我们知道MSIL是有前端编译器Roslyn生成的,它里面包含了IL代码和一些元数据。JIT就根据这些数据生成机器码,然后在机器上运行。根据上面所说:
一条汇编的生成,必然是有地址,所需填充的机器码。我们只需要传递所需填充的机器码到相应的地址上,就可以生成一条汇编代码。
比如往地址:00007FF85EAC0022里面填充了

48 81 EC 88 00 00 00

那么代表了就是以下汇编代码

sub  rsp,88h

我们可以确认的是,在CLR里面一条机器码的生成跟MSIL息息相关。由于过程非常复杂,这里主要看下它最终生成机器码的过程。

微软官方源码地址:

https://github.com/dotnet/runtime/blob/main/src/coreclr/jit/emit.cpp:4384行

我们看到这行的代码是:

is = emitOutputInstr(ig, id, dp);

emitOutputInstr函数里面包含了三个参数,第一个参数ig是需要生成的机器码的链表合集。id是当前需要生成的机器码的伪代码,dp也就是上面所说的需要被机器码填充的地址。

有了这个三个参数,其实非常好搞了。
思路就是:循环遍历ig,然后逐一取出机器码伪代码id,通过相应的操作,往dp地址里面填充机器码,这样就会一行行生成机器码。最终的结果就是被机器码执行,也就是.Net最终生成的形态。

参考代码:

https://github.com/dotnet/runtime/blob/main/src/coreclr/jit/emitxarch.cpp:16147


size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
{
    assert(emitIssuing);


    BYTE*         dst           = *dp;
size_t        sz            = sizeof(instrDesc);
    instruction   ins           = id->idIns();
unsigned char callInstrSize = 0;
//这里省略十万行
}

以上就是JIT机器码生成的大致过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值