• 用C实现一个Roslyn+CLR+JIT的编译链
  • 发布于 2个月前
  • 307 热度
    0 评论
前言
大约73行C语言代码,实现了一个简单的Roslyn前端+CLR中端+JIT后端的Compile大致过程。用了三个小时左右,兴之所至的作品。代码也非常简单,限于比较懒散和自由度过高,但应该能了解微软技术栈的核心级模式。

概括
1.源码:
既然自己实现,则源码需要一个自己的语言编写,暂取名江湖评谈的江湖二字。叫:jianghu语言。江湖语言的风格,以及源码长的下面的样子,结合C和C#特点。
u s.c   //引入 using system.core库
void main() //函数入口
{
   print("Hello World"); //内存存储helloworld
}
u表示using导入,s.c表示system.core表示核心不可或缺的库。下面就是入口函数main返回值为void类型,print意思是把hello world字符串存储到内存。把以上源码保存扩展名为.jhpt,也就是江湖评谈的每个字的首字母。

2.前端编译
前端编译,类似于Roslyn。把这段代码编译成IL中间和IR表现
0x177 0x2102 0x6703 IL_Using IL_System.Core0x177 0x2102 0x6703 IR_Using IR_System.CoreIL_void IL_MainIR_void IR_MainIL_Print IL_Hello WorldIR_Print IR_Hello World
由于JIT里面的IL是先导入,然后变形成IR。为了简单起见,这里是直接写入。C代码如下:
命令行:Roslyn.exe filepath
 // Roslyn前端编译部分 把源码编译成IL和IR
  char* filepath = argv[1];
  char str[100];
  FILE* read = fopen(filepath, "r"); //VS2022里注意fopen会报错不安全,解决:右键属性-》配置属性-》C/C++-》预处理器-》预处理器定义输入:_CRT_SECURE_NO_WARNINGS
  if (read == NULL)
  {
    printf("读取文件出错");
    return 0;
  }

  FILE* write = fopen("D:\\Roslyn.h", "w");
  fgets(str, 100, read);
  if (str[0] == 0x75)
  {
    fputs("0x177 0x2102 0x6703 IL_Using IL_System.Core\0\r\n", write); //IL表示
    fputs("0x177 0x2102 0x6703 IR_Using IR_System.Core\0\r\n", write); //IR表示
    //fclose(write);
  }
  fgets(str, 100, read);
  if (str[0] == 0x76)
  {
    fputs("IL_void IL_Main\0\r\n", write);//IL表示
    fputs("IR_void IR_Main\0\r\n", write);//IR表示
    //fclose(write);
  }
  fgets(str, 100, read);
  if (str[0] == 0x7b)
  {
    fputs("IL_Print IL_Hello World\0\r\n", write);//IL表示
    fputs("IR_Print IR_Hello World\0\r\n", write);//IR表示
  }

  fclose(write);
  fclose(read);
3.CLR+JIT
前端complie之后,就是CLR内存模型的加载,同时也是为了简单。这里只是实现了一个案例MethodDesc.
struct MethodDesc
{
   char m_pszDebugMethodName[10];
   char m_pszDebugClassName[10];
   char m_pszDebugMethodSignature[10];
   char m_pDebugMethodTable[10];
};
C代码如下:
// 堆代码 duidaima.com
// CLR 内存加载部分 构建MethodDesc模型
  struct MethodDesc
  {
    char m_pszDebugMethodName[10];
    char m_pszDebugClassName[10];
    char m_pszDebugMethodSignature[10];
    char m_pDebugMethodTable[10];
  };
  struct MethodDesc p_MethodDesc;
  FILE* readCLR = fopen("D:\\Roslyn.h", "r");
  fgets(str, 100, read);
  if (str[0] == 0x30)
  {
    strncpy(p_MethodDesc.m_pszDebugClassName, str +32, 11);
  }
  fgets(str, 100, read);
  fgets(str, 100, read);
  fgets(str, 100, read);
  if (str[0] == 0x69)
  {
      strncpy(p_MethodDesc.m_pszDebugMethodName, str+0xd,4);
  }
  strncpy(p_MethodDesc.m_pDebugMethodTable, "MethodTable",10);
  strncpy(p_MethodDesc.m_pszDebugMethodSignature, "MethodSignature",10);
  fclose(readCLR);
最后一个JIT小功能,把helloworld写入内存
//JIT 部分 编译成机器码
char *addr =(char*)malloc(100);
strcpy(addr, "hello world");
free(addr);

结尾
以上模拟.Net8的运行过程,省略了非常繁琐的东西,基本上用C简单语法和常用的函数实现了Roslyn+CLR+JIT的Compile的大致过程。
用户评论