.Net JIT支持的Risc-V/La/Arm
1.前言
.Net CLR主線合并的部分Risc-v,龍芯,ARM代碼。當(dāng)然ARM一直是主力支持的,這里主要是Risc-V和龍芯。通過UnwindCode看下。
2.概述
UnwindCode這個(gè)東西,主要是通過一些二進(jìn)制代碼描述一些機(jī)器碼含義,然后對其進(jìn)行相應(yīng)的操作。
我們通過lldb來看下這些操作的內(nèi)容:
(lldb) b RunMainInternal
(lldb) r
(lldb) b allocUnwindInfo
(lldb) c
(lldb) source info
(lldb) b jitinterface.cpp:11232
(lldb) c
內(nèi)容如下:
lldb) c
Process 58851 resuming
Process 58851 stopped
* thread #1, name = 'clrrun', stop reason = breakpoint 6.1
frame #0: 0x00007ffff6e676c7 libcoreclr.so`CEEJitInfo::allocUnwindInfo(this=0x00007fffffffaf70, pHotCode="UH\x83\xecPH\x8dl$P\xc4A8W\xc0\xc5y\U0000007fE\xc0\xc5y\U0000007fE\xd0\xc5y\U0000007fE\xe03\xc0H\x89E\xf0H\x89}\xf8\x83=\xa9\xf3\U0000001d", pColdCode=0x0000000000000000, startOffset=0, endOffset=395, unwindSize=8, pUnwindBlock="\U00000001\U00000005\U00000002", funcKind=CORJIT_FUNC_ROOT) at jitinterface.cpp:11232:5
11229 }
11230 #endif // _DEBUG
11231
-> 11232 memcpy(pUnwindInfoRW, pUnwindBlock, unwindSize);
我們看到它是一個(gè)memcpy的賦值??聪聀UnwindInfoRW里面是什么
(lldb) n
(lldb) p/x *pUnwindInfoRW
(UNWIND_INFO) $6 = {
Version = 0x01
Flags = 0x00
SizeOfProlog = 0x05
CountOfUnwindCodes = 0x02
FrameRegister = 0x00
FrameOffset = 0x00
UnwindCode = {
[0] = {
= (CodeOffset = 0x05, UnwindOp = 0x02, OpInfo = 0x09)
EpilogueCode = (OffsetLow = 0x05, UnwindOp = 0x02, OffsetHigh = 0x09)
FrameOffset = 0x9205
}
}
}
CountOfUnwindCodes表示總共有兩個(gè)UnwindCode。
下面是UnwindCode成員意義:
CodeOffset表示機(jī)器碼的長度。UnwindOp表示對是枚舉類型_UNWIND_OP_CODES,表示機(jī)器碼指令,比如UWOP_ALLOC_SMALL表示分配小對象??臻g(sub指令),UWOP_PUSH_NONVOL(push指令)。OpInfo則表示機(jī)器操作數(shù)。
看下兩個(gè)UnwindCode的結(jié)構(gòu)
(lldb) p/x pUnwindInfoRW->UnwindCode[0]
(UNWIND_CODE) $12 = {
= (CodeOffset = 0x05, UnwindOp = 0x02, OpInfo = 0x09)
EpilogueCode = (OffsetLow = 0x05, UnwindOp = 0x02, OffsetHigh = 0x09)
FrameOffset = 0x9205
}
(lldb) p/x pUnwindInfoRW->UnwindCode[1]
(UNWIND_CODE) $13 = {
= (CodeOffset = 0x01, UnwindOp = 0x00, OpInfo = 0x05)
EpilogueCode = (OffsetLow = 0x01, UnwindOp = 0x00, OffsetHigh = 0x05)
FrameOffset = 0x5001
}
(lldb) p/x pUnwindInfoRW->UnwindCode[2]
(UNWIND_CODE) $14 = {
= (CodeOffset = 0x00, UnwindOp = 0x00, OpInfo = 0x00)
EpilogueCode = (OffsetLow = 0x00, UnwindOp = 0x00, OffsetHigh = 0x00)
FrameOffset = 0x0000
}
第三個(gè)索引它就為零,確實(shí)是是兩個(gè)。它的Unwindop分別是0x02和0x00.這分別代表了:UWOP_ALLOC_SMALL和UWOP_PUSH_NONVOL.也即機(jī)器碼:sub和push。它的OpInfo分別為:0x09和0x05。分別代表了操作機(jī)器碼:8* 9+8以及rbp。
OpInfo
if (OpInfo == kRBP)
那么這個(gè)指令就很明顯了
push rbp
sub rsp_80
OK,以上是UnwindCode解析,下面來看下UnwindCode對于Risc-V和龍芯的這次hi。
Risc-v和龍芯
在它進(jìn)行賦值完成之后,也即是:
memcpy(pUnwindInfoRW, pUnwindBlock, unwindSize);
它總共有五個(gè)target,也就是指令集,分別為:amd64,arm64/arm,la64,riscv64
#elif defined(TARGET_AMD64)
pUnwindInfoRW->Flags = UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER;
#elif defined(TARGET_ARM64)
*(LONG *)pUnwindInfoRW |= (1 << 20); // X bit
#elif defined(TARGET_ARM)
*(LONG *)pUnwindInfoRW |= (1 << 20); // X bit
#elif defined(TARGET_LOONGARCH64)
*(LONG *)pUnwindInfoRW |= (1 << 20); // X bit
#elif defined(TARGET_RISCV64)
*(LONG *)pUnwindInfoRW |= (1 << 20); // X bit
#endif
我們看到.Net的主線當(dāng)中支持的Risc-v和La64,這里和x64的區(qū)別就在于,x64是pUnwindInfoRW的flag是或上
UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER;
而其他的三個(gè)則是:
*(LONG *)pUnwindInfoRW |= (1 << 20);