personal-website

Mars Studio

所属分类:技术博客与个人站

分享 LLVM 后端、编译器工程与开发工具实践的中英文技术博客。

Mars Studio 是一位编译器工程师维护的技术博客,标语“取法乎上得其中哦,取法乎中得其下呀”表达了向优秀实践学习的态度。站点的重点内容包括 LLVM 后端、指令选择、GPU 调试与开发工具,也有 Python、Swift REPL 和编程启蒙等主题。首页以近期文章为入口,较早的文章则可以沿着归档继续阅读。不同于只介绍 API 的参考手册,这里的文章以真实工程问题为线索,解释为什么遇到问题、方案如何组织,以及 LLVM 基础设施中哪些扩展点可以发挥作用。

Mars Studio:从 LLVM 后端工程看编译器实践

发布于 2026年10月21日更新于 2026年10月27日

Mars Studio 首页与 LLVM 技术文章

指令编码为何会变成后端问题

LLVM Backend Practices 系列讨论了指令编码随架构演进而变化的情况。传统后端实现通常在指令定义文件中描述指令以及各字段映射,新增指令时再补充新的指令定义和编码映射。当指令集的语义大体稳定、编码布局也稳定时,这样的方式直观且容易维护。但实际项目中,架构不同代之间可能只少量改变指令功能,却频繁调整编码方式;有时字段还会逐位映射,而不是按连续区间顺序排列。编码本身也可能需要支持可变长度,以便利用缩短指令的机会。

当编码规则持续变化,人工维护每条指令的位域映射就容易出现重复劳动和版本差异。文章提出把每代架构的编码描述放入一张表格中,再根据表格自动生成代码,并通过 LLVM 后端的 PostEncoderMethod 扩展点处理编码阶段逻辑。这个设计将“规则数据”与“生成机制”分开:表格表达各字段应该落在哪些位,生成器把规则转换成后端可用的代码,LLVM hook 则让特定编码操作接入既有的指令发射流程。

LLVM Backend Practices 指令编码设计文章

这种分层方法的价值在于,架构人员和编译器开发者可以围绕同一份结构化描述协作。若编码表易于阅读和编辑,架构变化时就能集中修改数据,再由生成流程检查格式并构建输出;通用的 LLVM 后端逻辑则继续承担指令选择、寄存器分配和汇编生成等职责。通过扩展点隔离架构特定处理,能避免把所有定制规则散落在通用流程的多个位置。

自动生成并不等于没有验证

自动代码生成能减少重复手工操作,却也把正确性责任转移到输入格式、生成器和输出校验上。编码描述表需要明确字段宽度、位序、重叠约束和不同架构版本之间的差异;生成器则需要对缺失字段、越界位置和重复映射给出明确错误。否则,一张看似简单的表可能生成合法但错误的指令编码,直到运行或硬件测试阶段才暴露问题。

因此后端工程应配套多层验证。首先可以针对编码表本身做结构检查,确保所有指令字段都能映射且不存在非法位范围。其次要测试生成代码是否与已知编码样例一致,并通过反汇编或汇编器进行往返核对。再进一步,可以将不同架构版本和边界编码加入测试集,检查可变长指令缩短逻辑是否只在合法条件下生效。生成工具让规则更易维护,测试则为它提供可持续的信心。

可变长度指令的“shrink”尤其说明了编码规则与代码生成策略之间的联系。如果某条指令可以用更短格式表示,编译器就需要知道哪些操作数、寄存器或立即数范围满足缩短条件,并在合适阶段决定采用哪种编码。缩短能改善代码尺寸,但也可能增加后端实现复杂度,并影响分支距离、对齐和最终布局。工程师需要在压缩收益、编译时间、编码可读性和验证成本之间权衡,而不是仅仅追求最短字节数。

LLVM 扩展点与架构定制的边界

LLVM 提供多种机制描述目标指令和接入目标特定逻辑。TableGen 适合表达指令属性和声明式规则,目标后端则负责把这些信息用于指令选择和发射。PostEncoderMethod 可用于指令编码完成前的后处理,使复杂或非传统的位映射能在适当边界上实现。选择扩展点时,关键是把逻辑放在最接近其语义的位置:描述稳定属性时尽量使用数据定义,生成重复结构时使用自动化,只有目标相关且难以声明式描述的行为才进入定制代码。

把逻辑放错层会增加长期维护负担。若每个指令定义里都重复一大段编码处理,后续架构变更就会出现多个近似实现;若通用代码里塞入特定芯片的条件分支,则会让其他目标也承担无关复杂度。较清晰的接口能使后端继续利用 LLVM 的通用流水线,同时允许项目针对自己的编码格式补充行为。阅读文章时,可以关注它如何将“描述数据”“代码生成”和“目标特定 hook”分开,这种职责划分适用于许多编译器和代码生成项目。

从技术文章提取工程方法

对系统软件工程师而言,Mars Studio 的文章适合结合代码与项目背景阅读。遇到编译器问题时,可以先还原输入约束:架构版本如何变化、指令编码需要满足什么要求、LLVM 当前阶段能看到哪些信息。然后比较几个实现位置:规则能否用声明描述,重复逻辑能否自动生成,特殊行为是否有合适的扩展点。最后再通过测试验证正确性和维护成本。这样的思路比只记住某个 API 名称更有迁移价值,因为不同目标架构可能需要不同实现,但问题分层和验证方法往往相通。

站点还有 GPU 调试、LLVM 指令选择和 REPL 实现等文章,为深入阅读提供相邻主题。指令选择关注程序操作如何映射为目标指令;GPU 调试涉及异构执行环境下的观察和调试工具;REPL 则展示编程语言运行时与交互环境之间的联系。通过这些文章,读者可以从目标指令编码继续扩展到完整工具链:前端产生中间表示,后端选择并编码指令,调试设施帮助定位运行问题,交互式环境则简化实验与探索。

适合哪些读者

正在学习 LLVM 或开发目标后端的工程师,可以从 LLVM Backend Practices 系列了解一个具体的编码问题及其处理方式;对编译器整体流程感兴趣的读者,可以继续阅读指令选择和 GPU 调试主题;更广泛的系统软件开发者也能从“规则表—代码生成—框架扩展点—测试验证”的方法中得到参考。文章并非 LLVM 官方规范,文中方案针对作者的工程背景,应用到其他项目时需要检查 LLVM 版本、目标架构和具体编码要求。

Mars Studio 将编译器实践写成一篇篇有上下文的工程记录,并通过 Atom 与公开 GitHub 入口连接到站点之外的资料。它适合作为深入技术主题的阅读起点:先理解问题的来龙去脉,再对照 LLVM 官方文档和源代码验证接口,最后根据自己的目标做实验。对后端开发者来说,这种从实际约束出发、将编码规则组织为可维护描述并配套验证的方式,正是复杂系统工程持续演进所需要的能力。

本文依据 Mars Studio 首页与 LLVM Backend Practices 公开文章整理,具体 LLVM 接口和实现细节请以当前 LLVM 版本文档及项目源码为准。

公开来源