尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
LLVM实站指南:从仓库结构到自定义Pass与后端优化
从第一次把git clone下来的 llvm-project 仓库摊开在眼前起大多数人就会意识到一件事这玩意儿的体量根本不是“一个项目”能概括的。这个仓库里装着的东西往小了说是一个编译器后端往大了说是整个现代编译基础设施的骨架。很多人冲着“学习 LLVM”点进来结果被源码目录里的 Clang、MLIR、LLD、libc、compiler-rt 这些子项目直接劝退——因为它们实在太多了多到让人不知道从哪下手。我自己当初也在这个仓库里迷路过很长一段时间。后来经过反复的构建、阅读、改代码、重新编译这套循环之后才算把脑子里那些零散的概念跟仓库里的真实目录一一对应上。这篇文章不打算给你讲什么高深的理论我就以 llvm-project 这个仓库为主体讲清楚它的目录结构、构建方式、工作原理以及怎么在上面做二次开发。这篇文章面向的是那些已经装过编译器、写过点 C/C、现在想深入编译器底层看看的开发者。如果你正处于“知道 LLVM 很牛但不知道它为什么牛”的阶段那你来对地方了。1. 这个仓库到底装了什么从目录结构看懂 LLVM 的“全家桶”设计LLVM 这个名字最早指的是底层虚拟机Low Level Virtual Machine但今天它已经完全没有虚拟机的意思了。它是一整套编译器基础设施的集合而llvm-project这个 GitHub 仓库就是这套基础设施的统一代码库。你把它 clone 下来列一下顶层目录就会看到下面这些关键角色。llvm目录是整个项目的核心它包含了优化器和代码生成相关的所有代码。你写的任何语言只要能编译成 LLVM 中间表示IR就能复用这里面的全部优化和各个目标平台的后端生成能力。clang是 C/C/Objective-C 的前端负责把源代码解析成抽象语法树然后生成 LLVM IR。lld是链接器libc是 C 标准库实现compiler-rt是运行时库而mlir则是专门用来构建编译器和领域特定语言的框架。这个布局本身就在告诉你一件事LLVM 的设计哲学是把编译器拆成前端、优化器、后端三个相对独立的模块。这种三段式的架构从教科书里走进了真实代码库而且比教科书更彻底——因为每个模块内部还被进一步模块化成了独立的库。如果你点进llvm/lib看一下会发现里面有很多子目录IR放中间表示的核心数据结构和操作Analysis放各种静态分析 passTransforms放优化 passCodeGen放目标代码生成的通用逻辑Target下面再按 CPU 架构分成 X86、ARM、RISC-V、AArch64 这些子目录。这种组织方式意味着你完全可以在不动前端和后端的情况下往中间插入自己的优化 pass 来改变程序行为。仓库里还有一些容易被忽略但非常有用的子项目。lldb是调试器polly是做循环优化的多面体模型框架clang-tools-extra里藏着clang-tidy、clangd这些日常开发中离不开的工具。所以当你在网上看到有人说“LLVM 是个编译器”时严格来说是不准确的——它是一个包含了完整工具链的编译器生态也是无数科研项目、工业级工具链和商业产品的地基。从学习策略上讲我的建议是不要试图一次性理解整个仓库而应该先确定自己感兴趣的切入点你想了解编译器的哪个阶段是想做前端语言解析还是想改优化 pass或者想研究某个新架构的后端移植确定方向后在仓库中沿着对应目录深挖远比通读所有源码更高效。整个 llvm-project 仓库的代码量在千万行级别靠“从头读到尾”这种方法是行不通的。2. 为什么构建是最大的门槛环境准备、CMake 配置与硬件资源考量如果你想真正在 llvm-project 上动手做点什么第一关就是把它编译出来。这一步劝退了很多人因为在硬件配置不够的情况下全量构建 llvm-project 的时间能让耐心耗尽。先讲最基本的硬件要求。以我自己的经验来看构建整个 LLVMClang 工具链最少需要 8GB 内存和 30GB 磁盘空间但这只是“能编”而不是“编得动”。如果只有 8GB 内存用ninja并行编译时非常容易内存吃紧导致 OOM内存耗尽。我在一台 16GB 内存的机器上编译也曾经因为并行度过高直接把系统卡到无响应后来才明白编译 LLVM 的并行度不是越大越好。磁盘方面建议准备 50GB 以上因为编译产物和中间文件比你想象中占地方得多。如果你只是想研究某个特定 pass 或代码生成逻辑其实可以考虑只构建必要的目标和后端不必全量开启所有架构支持这样能大幅缩短构建时间。在操作系统和工具链的选择上Linux 是最顺手的平台macOS 也可以但 Windows 上构建 LLVM 需要额外注意 MSVC 版本和路径长度限制因为 LLVM 源码中的文件路径比较深容易触发 Windows 的 MAX_PATH 限制。编译器的话推荐先装好一个新版本的 GCC 或 Clang因为 LLVM 新版本本身会用到较新的 C 标准特性——你需要一个“能编编译器的编译器”这就是编译领域常说的 Bootstrapping自举过程的起点。接下来是最关键的一步用 CMake 配置构建。在llvm-project根目录下新建一个build目录然后执行 CMake 配置命令。下面这组配置是我多次实践后认为最适合作开发调试用的cmake -G Ninja -S llvm -B build \ -DCMAKE_BUILD_TYPEDebug \ -DLLVM_ENABLE_PROJECTSclang;lld \ -DLLVM_TARGETS_TO_BUILDX86 \ -DLLVM_ENABLE_ASSERTIONSON \ -DBUILD_SHARED_LIBSON逐项解释一下这些选项的含义和作用-G Ninja指定使用 Ninja 作为构建系统它比传统的 Makefile 并行效率更高增量编译又快我几乎没法想象没有 Ninja 去编译 LLVM 会是什么体验。-DCMAKE_BUILD_TYPEDebug生成带调试信息的版本这对学习和二次开发来说是必需的因为你一定会在源码里打断点调试代价是 Debug 版本的运行效率比 Release 低不少但学习阶段这种牺牲是值得的。-DLLVM_ENABLE_PROJECTSclang;lld指定除了核心 LLVM 之外额外构建哪些子项目Clang 前端肯定是需要的LLD 链接器在测试生成的可执行文件时很好用它比系统自带的 GNU ld 快很多。-DLLVM_TARGETS_TO_BUILDX86只生成 X86 后端的代码不带 ARM、AArch64、RISC-V 这些目标这一步能把构建时间大幅缩小代价是你未来如果想做跨架构的实验就得重新配置。-DLLVM_ENABLE_ASSERTIONSON开启 LLVM 内部的断言机制开发过程中很多错误会在这里暴露出来建议永远打开它。-DBUILD_SHARED_LIBSON将 LLVM 的各组件编译成动态库而不是一个巨大的静态库这样链接你自己的测试程序会更快也不容易遇到静态库链接顺序问题。配置完成之后执行ninja -j$(nproc)开始构建。这里nproc会获取你的 CPU 核心数但如果你内存不够大建议手动调低并行度比如-j4或-j2不要让内存成为瓶颈。我当时第一次配置时只开启一个 X86 后端、Debug 模式完整构建 clang 加 lld 大概花了半小时到一小时如果全量编译所有目标架构几个小时也是正常现象。第一次构建的时候可以先去泡杯咖啡或者干点别的这个等待是值得的。3. 从源码到机器码LLVM 内部的三段式流水线是如何工作的构建完成之后你手上就有了一套完整的 LLVM 工具链。这时候最该做的事不是急着去读源码而是先用工具链亲手走一遍“从源代码到机器码”的完整流程直观感受 LLVM 的三段式架构。这种体验认知上的冲击比我用一万字跟你解释都来得直接。假设有一个最简单的 C 程序int add(int a, int b) { return a b; } int main() { return add(1, 2); }第一步用 clang 把 C 源码编译成 LLVM IR。LLVM IR 是 LLVM 生态中的关键中间表示它是前端和后端之间的分界线。执行下面的命令build/bin/clang -S -emit-llvm add.c -o add.ll-S表示生成汇编文件而不是目标文件-emit-llvm告诉 clang 输出的不是机器汇编而是 LLVM IR。打开add.ll你会看到类似这样的内容define i32 add(i32 noundef %a, i32 noundef %b) { %add add nsw i32 %a, %b ret i32 %add }这个 IR 比较接近高级语言变量是带名字的有明确的类型信息运算指令也保留了原始语义。它既不像 C 那样接近人类语言也不像汇编那样贴近机器是一种在两者之间做了折中的表示方式。之所以 LLVM 要先转成 IR是因为这样做前端只需要输出统一的 IR后面的优化和后端就可以被不同语言的前端复用。第二步对 IR 做优化。LLVM 的优化器以opt工具的形式存在你可以指定跑哪些优化 pass按顺序看看 IR 是怎么一步步变的build/bin/opt -passesinstcombine,mem2reg add.ll -S -o add.opt.llinstcombine会做指令合并和简化mem2reg会把内存操作提升到寄存器这是优化中非常基础的一步。打开优化后的文件你可能看到add函数内容变了或者在main里面那个常量加法已经被算出来了变成了ret i32 3——这就是常量折叠优化的效果。你可以多试几个 pass观察 IR 从“像高级语言”逐步变成“更像机器语言”的过程这是理解编译器优化的最好的方式。第三步把优化后的 IR 变成目标机器代码。这一步由llc完成build/bin/llc add.ll -o add.s打开add.s会看到 X86 汇编代码比如addl、ret这些指令。LLVM 的后端在这里完成指令选择、寄存器分配、指令调度等一整套工作。如果想看看循环、分支等复杂结构是怎么被后端的各种算法处理的可以写一个带循环的程序再试试。这三步走下来你就会发现 LLVM 的设计思路其实非常清晰前端负责理解语言优化器负责提升代码质量后端负责适配机器。每一层之间通过 IR 解耦这也是为什么世界上有那么多编程语言都在用 LLVM 做后端——Kotlin/Native、Rust、Swift 都基于 LLVM因为只要语言前端能生成合法的 LLVM IR后面所有优化和后端输出能力就直接复用了一套成熟的方案。理解这条流水线今后你再在源码里看到clang前端调用CodeGenAction、opt调用PassManager、llc调用TargetMachine这些代码时就不会觉得抽象了。4. 在 llvm-project 上写第一个自定义 Pass原理、步骤与实测光会跑工具链还只是使用层面的事。真正对得起“在 llvm-project 上做开发”这件事的是写一个属于自己的 LLVM pass。这里我以写一个最简单的 Function Pass 为例演示如何统计一个函数里的基本块个数然后把它跑在之前生成的 IR 上。这是很多人学习 LLVM 二次开发的传统入门项目麻雀虽小但五脏俱全。先说明一下背景LLVM 新版本用new pass manager已经成为主流和旧版legacy pass manager的写法差别较大。下面我直接按新版 API 来写这样你照着做在当前的主干代码上能直接编译运行。在你自己的源码目录下新建一个文件MyPass.cpp。比如我在 llvm-project 之外建了一个llvm-tutorial目录里面放一个插件形式的 pass源码如下#include llvm/IR/Function.h #include llvm/IR/Module.h #include llvm/Pass.h #include llvm/Passes/PassBuilder.h #include llvm/Passes/PassPlugin.h #include llvm/Support/raw_ostream.h using namespace llvm; namespace { struct CountBasicBlocksPass : public PassInfoMixinCountBasicBlocksPass { PreservedAnalyses run(Function F, FunctionAnalysisManager AM) { unsigned count 0; for (auto BB : F) { (void)BB; count; } errs() Function: F.getName() , basic blocks: count \n; return PreservedAnalyses::all(); } }; } // namespace llvm::PassPluginLibraryInfo getLLVMPassPluginInfo() { return { LLVM_PLUGIN_API_VERSION, CountBasicBlocksPass, LLVM_VERSION_STRING, [](PassBuilder PB) { PB.registerPipelineParsingCallback( [](StringRef Name, FunctionPassManager FPM, ArrayRefPassBuilder::PipelineElement) { if (Name count-basic-blocks) { FPM.addPass(CountBasicBlocksPass()); return true; } return false; }); }}; } extern C LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { return getLLVMPassPluginInfo(); }这段代码的核心逻辑在run方法里遍历 Function 的每个基本块BasicBlock计数并打印函数名。PreservedAnalyses::all()表示这个 pass 没有修改任何 IR所以无需替换分析结果这是一种性能优化声明。把这个文件编译成动态库的命令如下clang -shared -fPIC -fno-rtti MyPass.cpp \ -I path/to/llvm-project/llvm/include \ -I path/to/build/include \ -o libMyPass.so注意一定要加-fno-rtti因为 LLVM 编译时禁用了 RTTI如果你的代码里有dynamic_cast或者异常处理相关操作链接时会报错。编译完成后用opt加载这个插件并跑在add.ll上build/bin/opt -load-pass-plugin./libMyPass.so \ -passescount-basic-blocks add.ll -S -o /dev/null如果一切正常终端会输出类似Function: add, basic blocks: 1; Function: main, basic blocks: 1这样的信息。这说明你的第一个自定义 pass 已经成功在 llvm-project 上跑通了。从“照抄能跑”到“真正理解”有几个问题值得深入想一想。为什么这里要用PassInfoMixin因为新 pass manager 把每个 pass 定义为一个 mixin 类通过 CRTP 模式关联起来。为什么注册要用PassBuilder的回调因为这样可以让你在命令行里通过-passesxxx动态启用 pass不必把 pass 硬编码进某个 pipeline。为什么最终导出的符号必须是llvmGetPassPluginInfo因为opt加载插件时就是通过dlsym查这个符号来拿到 pass 信息的。理解了这几个为什么你对 LLVM 插件机制的认识就基本成型了。如果你的需求不是写插件而是把 pass 直接加到opt工具甚至 clang 的优化流水线里路径会不同你需要把 pass 源码放进llvm/lib/Transforms/Utils这类目录修改对应的 CMakeLists.txt然后重新编译整个项目。这种方式的迭代成本高很多所以我建议前期学习阶段一律用插件方式每次只编一个小.so几秒钟就能完成编译调试循环快得多。5. 驾驭 clang 前端从 AST 到 IR 之间的调试技巧与常用工具写 pass 只能算是在 IR 层做文章如果你关心的是“一种语言是怎么被翻译成 IR 的”那就要把注意力放到 clang 前端上。很多人在这一层最容易迷路因为 AST 太庞杂、IR 生成又涉及大量细节。这里我分享几个自己长期在用的调试工具和技巧能帮你少走很多弯路。clang 提供了一族非常直观的调试选项。最常用的是clang -ast-dump它能把解析出来的抽象语法树直接打印出来。拿之前那个add.c为例build/bin/clang -Xclang -ast-dump -fsyntax-only add.c你会看到从TranslationUnitDecl到FunctionDecl、BinaryOperator、ReturnStmt一层层的节点结构每个节点前面还带着它在源码中的位置信息。用这个命令可以非常直观地看清 clang 把源代码切成了哪些语法节点每个节点的类型、子节点、关联类型都一目了然。如果再往下走想看 clang 是怎么把 AST 变成 LLVM IR 的可以加-emit-llvm然后再配合-S输出。但更深入的问题往往是“这个变量在 IR 里为什么没有了”“这个地方为什么插入了这样一个转换指令”这就需要在 clang 的 CodeGen 代码里打断点了。在调试器里断在CodeGenFunction.cpp的EmitStmt或者EmitExpr相关函数上单步看它如何递归遍历 AST 生成指令这一遍走下来你对前端和后端衔接的理解会有一个质的提升。还有一个极其实用但很多人不知道的工具是clang-check它在clang-tools-extra里。它能对你的源码做完整的语法和语义分析并输出诊断信息适合在开发 Clang 工具、静态分析器或者重构工具时做快速验证。比如你想测试 clang 对某个 C 特性的解析结果是否符合预期写个测试文件然后跑clang-check它会在不改动文件的情况下告诉你解析过程中有没有问题。如果你打算做更进阶的前端开发比如写一个 Clang AST 的 Custom Checker建议把LibTooling的框架学明白。它允许你写一个独立的工具程序用 Clang 的库来对任意 C 源码做词法、语法、语义分析并和 clang-tidy 共享基础设施。我自己的经验是先花一个下午玩熟ast-dump和clang-check对 AST 的形态有了体感之后再上手 LibTooling能省掉很多在源码里大海捞针的时间。6. 绕不开的坑与效率法宝并行编译、资源限制与增量构建实操在 llvm-project 上开发了一段时间之后我逐渐意识到真正影响你效率的往往不是源码本身而是构建和调试这套基础设施带来的摩擦。这里把我在实际操作中遇到最频繁的坑和一些提升效率的方法盘一下每一条都来自真实的编译-失败-调整循环。第一个坑是链接内存不足OOM。Debug 模式加 BUILD_SHARED_LIBSON 的情况下链接clang这个二进制可能需要十几 GB 内存这是最让人头疼的问题。解决思路有三个一是尽量用BUILD_SHARED_LIBSON把庞大的静态链接拆成动态链接每个共享库单独链接时内存压力小很多二是切换到lld链接器它比系统默认的 GNU ld 速度快、内存占用也更低三是实在不行就降低并行度ninja -j2虽然慢但至少能编完。我自己后来在 16GB 内存的机器上总结出的可靠组合是-DLLVM_PARALLEL_LINK_JOBS2这个参数专门限制链接阶段的并行数避免多个大二进制同时链接把内存吃爆而编译阶段仍然可以保持高并行度。第二个坑是增量构建时 cmake 没有感知到你改了什么文件。有时候你改了某个头文件但运行ninja之后并没有触发重新编译这个时候别着急锤键盘先看看是不是没有把对应的依赖目录加入CMAKE_DEPENDS中。一般来说只要你改的是项目内已有源文件增量构建是能正确触发的但如果你新增了一个源文件却没有把它写进对应目录的CMakeLists.txt那 ninja 根本不知道这个文件的存在编译自然也不会发生。新增源码后第一件事就是改 CMakeLists这是新手最容易犯的错。第三个坑是各种版本不匹配问题。llvm-project 的 main 分支更新非常快可能昨天还能编过今天 pull 下来之后某个 API 就变了。如果你的目的只是学习或稳定开发强烈建议切换到一个 release tag 上比如llvmorg-19.1.0。我见过太多人一上来就 clone main 分支然后被 nightly 版本的代码变动折磨到放弃。用 release tag 能让你查资料时和网上大多数文章、答案保持在同一 API 版本上这对新手至关重要。还有一个小技巧特别值得说如果你只是改一个 pass 或者改 clang 的某一个局部功能完全不需要每次都全量 build。把build/bin/opt或build/bin/clang加入 shell 的 history配合ninja opt或者ninja clang只构建特定目标编译时间能从一小时级别降到几分钟级别。我是这么做的写了一个 shell 函数build_llvm() { ninja -C /path/to/build opt clang lld $; }日常改代码就敲这个命令十分钟之内基本能完成从改代码到测试的整个循环。最后一个不能忽视的是 ccache——编译缓存工具。在构建 LLVM 这种大型项目时ccache 能把重复编译的产物缓存起来第二次构建同一段代码时直接命中缓存速度提升非常明显。特别是你频繁在几个版本之间切换、或者经常改一块头文件导致大量重编时ccache 的价值会被放大到难以想象的程度。配置方法很简单安装 ccache 后把CMAKE_C_COMPILER_LAUNCHER和CMAKE_CXX_COMPILER_LAUNCHER指过去即可。7. 深入后端读懂指令选择与寄存器分配的源码路径很多人停留在 IR 层和 pass 层之后就以为 LLVM 已经玩明白了。但实际上现代编译器中最复杂、最接近硬件的部分——指令选择、寄存器分配、指令调度——全部藏在后端。在 llvm-project 里对应的是llvm/lib/Target目录。这一节我带你看懂后端的关键源码路径即便你不打算做后端移植这部分的认知也能帮你写出对编译器更友好的代码。整个后端流程大致是这样的LLVM IR 经过SelectionDAG或新的GlobalISel变成一张有向无环图DAG然后在这张图上做指令选择——把平台无关的 IR 节点映射成目标平台的具体指令节点。以 X86 后端为例指令选择的核心关注点是把add这样的 IR 节点匹配成 X86 的ADD32rr或ADD32rm指令前者是两个寄存器相加后者是寄存器加内存。为什么会有多种匹配因为add指令的两个操作数来源不同指令的编码长度和执行效率也不同编译器要选择最优匹配。如果你想从源码上验证这一点可以打开llvm/lib/Target/X86/X86ISelLowering.cpp和X86InstrInfo.td。前者是“指令选择前的合法化和类型合法化”的落点也就是某些 IR 节点在目标平台不支持或不高效时后端如何把它拆成更基本的操作后者用 TableGen 语言描述了 X86 的每条指令的编码、语法、操作数约束和调度信息。TableGen 是一种 LLVM 自己发明的领域特定语言它能把指令描述编译成 C 代码从而避免手写大量重复的匹配逻辑。如果你看到.td后缀的文件那基本上就是在描述目标架构的指令集。寄存器分配是另一个值得花时间的核心环节。IR 里理论上可以有无限个虚拟寄存器但真实机器的物理寄存器数量是有限的比如 X86 64 位模式下整数通用寄存器大概十几个。分配器要做的就是把虚拟寄存器映射到物理寄存器上装不下的时候就把多余的值“溢出”spill到内存栈上。这部分代码主要在llvm/lib/CodeGen/RegAllocGreedy.cpp。这个文件的核心是贪心算法它会为每个虚拟寄存器计算活跃区间按区间重叠情况分配寄存器实在分配不了就插入mov指令在内存和寄存器之间移动数据。我在读这个文件时最深的感受是合理利用活跃区间分析能大幅度减少溢出这也是后端性能差异的主要来源之一。后端的学习路径我建议从“看llc -debug的输出”开始。llc的-debug选项会打印出后端每个阶段处理 IR 的详细中间结果包括 DAG 构建后、指令选择后、寄存器分配前等各阶段的状态。这种“黑盒里装了个示波器”式的调试体验远比直接读代码更直观。你拿一个简单的add.c文件运行build/bin/llc -debug add.ll -o /dev/null然后跟着输出的日志对照SelectionDAGBuilder的代码一条条看就能理解每个后端 pass 到底做了什么。唯一的问题是日志量非常大建议配合-debug-onlyisel这类细粒度选项把输出范围缩小到指令选择阶段否则你的终端会被几千行日志刷得没法看。8. 学习路径建议从源码阅读到参与 LLVM 社区坦白讲llvm-project 的学习曲线是相当陡峭的但它的回报也足够丰盛。很多人学了 C 之后想看看真正的工业级 C 项目是什么样的LLVM 几乎是最合适的样本。它代码规范、模块划分清晰、注释相对充足而且测试框架非常完善——llvm/test下面躺着几十万个测试用例每一个基本都能独立运行。这些东西读下来不仅是对编译器知识的提升更是对工程能力的全面训练。如果是初学者我建议的阅读顺序是先读llvm/docs/ProgrammersManual.rst和llvm/docs/CodeGenerator.rst这两个文档能帮你建立全局框架。然后在 IR 层做几个实验比如写一个简单的 pass、用opt跑各种优化。接下来用clang -ast-dump认识前端 AST用llc -debug认识后端流程。等你对这些工具链都熟悉了再挑一个具体的子项目深入——比如你想研究如何给一门新语言做编译器重点看 Clang 和前端想研究性能优化重点看llvm/lib/Transforms想研究底层移植那就泡在llvm/lib/Target里。顺畅融入 LLVM 社区还有个很现实的路径从good first issue和 Google Code-in 这类新手任务入手。LLVM 的 GitHub 仓库里会长期挂着一些适合新手的问题标签例如“beginner”和“good first issue”它们一般不需要对整个系统有非常全面的了解大多局限在一个子模块内非常适合用来练手。完成两三个这样的小任务之后你会对社区的协作方式、代码审查规范、测试要求有真实的体感。提交一个 patch 被 review 的过程虽然折磨但每一轮 review 都是资深开发者手把手在教你写代码这种学习效率比自己瞎看源码不知道高到哪里去了。有一点我必须提醒别好高骛远。我看过太多人计划“先读一遍源码”结果坚持了两周就放弃了。LLVM 这种体量的项目不适合线性通读适合“带着问题去查”。比如你在写 pass 时发现某个优化没有生效那就顺藤摸瓜去读那个优化对应的 pass 代码比如你想知道为什么某个循环被展开就去找循环展开的 pass 看它的判定逻辑。这种问题驱动的阅读方式一次只解决一个小问题但积累起来你对整个项目的理解会形成一张紧密的网络。我个人还有一个习惯用 git 管理我对 llvm-project 的所有改动。不要直接在主线目录里写代码而是拉一个自己的分支每次实验性的改动都单独提交。这样当你把源码改坏了、想回到某个节点的时候一行git checkout -- .就能解决不用反复重新 clone。我见过不少人因为乱改源码导致构建崩溃最后只能删掉整个 build 目录重来几个小时就这样没了。用 git 管理源码怎么折腾都不怕这个习惯越早养成越好。回到文章开头那个问题llvm-project 到底是什么它当然是个仓库但又不止是个仓库。它是现代编译技术各种思想的聚合体是无数开发者二十多年积累下来的一套体系。你可以把它当作工具来用也可以把它当作教科书来读还可以把它当作试验田来改。打开它编译它调试它改动它——在这个过程中你对“程序是怎么变成机器指令的”这件事的理解会从模糊的概念变成清晰的图景。这套能力是你在任何一本单纯讲编译原理的书里都得不到的。
RELATED

相关推荐

LibreChat自托管部署指南:多模型接入与配置实践

LibreChat自托管部署指南:多模型接入与配置实践

1. 从零认识 LibreChat:它到底解决的是什么问题第一次接触 LibreChat 的人,多半是被一个很具体的痛点逼过来的:手头有好几套模型服务,OpenAI 的、Claude 的、本地跑的 Ollama、还有公司内部部署的推理接口,每换一个就要…

📅 2026/9/20 19:31:12
基于抛物方程的大气波导电波传播建模与仿真研究

基于抛物方程的大气波导电波传播建模与仿真研究

简介:这是一份针对大气波导环境下电波传播建模与仿真的PPT学习教案,适合通信工程、电磁场与微波技术专业高年级学生、研究生及相关领域科研与工程技术人员阅读。课件从大气折射的四种基本类型入手,说明标准折射、超折射、临界折射与陷获折射的…

📅 2026/9/20 19:26:12
VisionMaster4.2.0 C#控件开发实战:WPF上位机集成与避坑指南

VisionMaster4.2.0 C#控件开发实战:WPF上位机集成与避坑指南

1. 为什么要在 VisionMaster4.2.0 上做 C# 控件开发VisionMaster4.2.0 是海康机器人推出的机器视觉算法平台,做工业自动化的朋友应该不陌生。它自带一套可视化流程编排界面,拖拖拽拽就能搭出定位、测量、检测、识别这些常见视觉任务。但实际项目里&#…

📅 2026/9/20 19:26:12
MORE NEWS

更多资讯

📰

AssetRipper:三步跑通 Unity 资产提取的完整指南

AssetRipper:三步跑通 Unity 资产提取的完整指南 【免费下载链接】AssetRipper GUI application to analyze game files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRipper 是一款用于 Unity 资产提取与游戏文件分析的图形化工具&a…

📰

OneUptime 真实用户监控(RUM)完全指南:OpenTelemetry 分类机制、接入流程与排障实战

OneUptime 真实用户监控(RUM)完全指南:OpenTelemetry 分类机制、接入流程与排障实战 【免费下载链接】oneuptime Complete open-source monitoring and observability platform. 项目地址: https://gitcode.com/GitHub_Trending/on/oneupti…

📰

Eclipse Theia @theia/filesystem 扩展详解:文件监听、上传下载与文件树 Widget 的实现机制

Eclipse Theia theia/filesystem 扩展详解:文件监听、上传下载与文件树 Widget 的实现机制 【免费下载链接】theia Eclipse Theia is a cloud & desktop IDE framework implemented in TypeScript. 项目地址: https://gitcode.com/gh_mirrors/th/theia E…

📰

web3.js v4 类型基石 web3-types 的演进全解析:从 CHANGELOG 到源码读懂以太坊 TypeScript 类型系统

区块链Web3 【免费下载链接】web3.js Collection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions. 项目地址: https://gitcode.com/gh_mirrors/we/web3.js 点击查看 免费下载 导读 web3-types 是 …

📰

Windows下MySQL 8安装全攻略:MSI与ZIP双方案及避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Spring IoC 循环依赖源码解析:三级缓存与“提前暴露“机制

Spring IoC 循环依赖源码解析:三级缓存与"提前暴露"机制 【免费下载链接】source-code-hunter 😱 从源码层面,剖析挖掘互联网行业主流技术的底层实现原理,为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬