文章摘要
这篇文章介绍了一个简单的即时编译器(JIT)实现。作者通过一个编程挑战任务,展示了如何将递归关系运算从解释执行转换为动态生成x86-64机器码执行,从而提升性能。文章提供了相关代码示例和讨论链接。
文章总结
基础即时编译器教程
本文作者分享了一个简单的x86-64即时编译(JIT)实现过程,该技术用于将递归关系表达式直接编译为机器码执行。
项目背景
作者受Reddit每日编程挑战启发,需编写程序解析形如"+2 3 -5"的递归关系表达式(如u(n+1)=(u(n)+2)3-5)。传统解释器会逐步执行运算,而作者选择将其编译为原生机器指令。
关键技术实现
可执行内存分配
- 现代操作系统通过页面保护机制隔离内存权限(读/写/执行)
- 使用mmap/VirtualAlloc分配可写内存,通过mprotect/VirtualProtect后期设置为可执行
- 实现asmbuf结构管理指令缓冲区,支持跨平台(POSIX/Win32)
调用约定处理
- x86-64架构遵循System V AMD64 ABI(参数通过rdi传递,结果存rax)
- Windows平台特殊处理:首个参数使用rcx寄存器
- 基础指令模板:
assembly mov rax, rdi ; 初始传参 ret ; 函数返回
操作符编译
- 通过反汇编工具确定机器码:
- 立即数加载:
mov rdi, [operand]→ 48BF[小端序操作数] - 算术运算:
c '+' → 4801F8 (add) '-' → 4829F8 (sub) '*' → 480FAFC7 (imul) '/' → 4831D2 + 48F7FF (xor+idiv)
- 立即数加载:
- 通过反汇编工具确定机器码:
执行流程
- 动态生成机器码
- 设置内存可执行权限
- 将缓冲区转换为函数指针调用:
c long (*recurrence)(long) = (void *)buf->code; result = recurrence(input);
扩展建议
- 支持模运算、位操作等扩展运算符
- 考虑浮点运算支持
- 未来可探索更复杂的控制流和函数调用
这个简化实现展示了JIT核心原理,虽然未涉及分支/栈操作等高级特性,但为理解底层编译技术提供了良好起点。作者表示未来将尝试开发更复杂的JIT编译器。
(注:原文中的就业声明、社区讨论链接等非技术细节已省略,完整代码示例可查看原文链接)
评论总结
以下是评论内容的总结:
- 关于线性递推解的讨论(评论1)
- 主要观点:文章提供的闭式解仅对计算特定大项有意义,而非计算所有项
- 关键引用:
- "This is really only interesting if a particular (potentially really large) term of the sequence is desired"
- "any sequence of the given set of operations reduces to a linear recurrence which has the given solution"
- 关于JIT术语的争议(评论2)
- 主要观点:作者误用了JIT概念,应称为"即时AOT编译"
- 关键引用:
- "The canonical definition of JIT is 'compilation during execution of a program'"
- "I'd prefer not overloading the term 'JIT'"
- 关于static关键字的质疑(评论3)
- 主要观点:作者可能误解了static的用法
- 关键引用:
- "is the author using static wrong?"
- "Should it actually be the function level static variable?"
- 关于x86汇编的吐槽(评论4,6)
- 主要观点:
- x86汇编生成困难(评论4)
- 页面大小在不同系统表现不同(评论6)
- 关键引用:
- "I friggin despise how hard generating x86 assembly is"
- "sysconf(SCPAGESIZE) will always be 4KB...Except on Cosmopolitan"
- 对文章内容的失望(评论5)
- 主要观点:期待看到BASIC相关的JIT实现但落空
- 关键引用:
- "Ah, I thought this was going to be about a JITted BASIC. No such luck"