☰
仓颉 LLVM GC Barrier 优化全链路:从 Barrier 生成到后端 Lowering 的终极指南
2026/10/9 9:03:33 网站建设 项目流程

仓颉 LLVM GC Barrier 优化全链路:从 Barrier 生成到后端 Lowering 的终极指南

【免费下载链接】llvm-projectLLVM 项目是一个模块化、可复用的编译器及工具链技术的集合。此fork用于添加仓颉编译器的功能,并支持仓颉编译器项目。项目地址: https://gitcode.com/Cangjie/llvm-project

🎯GC Barrier 优化是仓颉(Cangjie)编译器基于 LLVM 定制的核心能力之一:它在编译器内部自动管理"写屏障(Write Barrier)"的生成、消除与降级,让 GC 程序在正确性的前提下跑得更快。本文带你从 Barrier 生成一直看到后端 Lowering,走完全链路。

1. 为什么编译器需要写屏障?

垃圾回收器必须知道"哪个堆对象里还存着对其它堆对象的引用"。如果编译器不插手,运行时只能在每次写堆引用时都通知 GC——这会拖慢性能。仓颉的做法是:

  • 前端代码生成阶段插入Barrier 内建函数(Intrinsic),标记每一次可能影响可达性的引用写入;
  • 标量优化阶段拆分、删除冗余 Barrier;
  • 后端Lowering阶段把 Barrier 变成具体的高效运行时调用。

下面这张图展示了整个编译驱动的前后端流程,GC Barrier 优化就发生在"前端 IR → 优化 Pass → 后端 CodeGen"这条主线上:

2. Barrier 从哪来:前端生成的 Barrier 内建函数

仓颉前端为每类引用写入生成对应的 GC 内建函数,统一定义在 llvm/lib/IR/CJIntrinsics.cpp。常见的 Barrier 家族包括:

Barrier 类型内建函数覆盖场景
对象字段写入cj_gcwrite_ref/cj_gcwrite_struct普通对象引用 / 结构体字段
静态字段写入cj_gcwrite_static_ref/cj_gcwrite_static_struct全局静态引用
堆读取cj_gcread_ref/cj_gcread_weakref等普通引用 / 弱引用
数组批量拷贝cj_array_copy_ref/cj_array_copy_structSystem.arraycopy类场景
原子操作cj_atomic_store/cj_atomic_load/cj_atomic_swap并发引用读写

配套的 llvm/lib/IR/CJStructTypeGCInfo.cpp 为结构体类型计算 GC 信息,是后续"结构体 Barrier 拆分"的数据基础。

3. 标量优化:Barrier 的拆分、删除与验证

生成出来的 Barrier 往往"过粗",仓颉在标量 Pass 阶段做了三层优化。

3.1 Barrier 拆分:一个大 Barrier 变细粒度

CJBarrierSplit.cpp 中的SplitBarrier会把一次写入整个结构体的粗粒度 Barrier拆分成只覆盖真正包含引用字段的部分。结构体里全是基本类型的部分就不再触发 GC 记录,直接省掉大量无意义的屏障开销。

3.2 Barrier 删除:大数组的免检通道

CJBarrierOpt.cpp 负责barrier split、barrier remove 和 barrier verifier三件事。其中最有代表性的是大数组规则:

当数组大小超过cj-array-threshold(默认128KB,见 llvm/lib/Transforms/Scalar/CJBarrierOpt.cpp)且元素类型是基本类型时,其写屏障可以直接删除——因为基本类型数组根本不含堆引用,GC 扫描它们纯属浪费。

同时该 Pass 识别CJ_MCC_NewObject、CJ_MCC_NewArray*等分配函数:新分配的对象在逃逸之前无需完整屏障处理。

3.3 Barrier 验证:正确性兜底

优化后的 IR 会经过 Barrier Verifier 检查,配合 llvm/lib/Transforms/Scalar/CJIRVerifier.cpp 和 llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp(GC 存活分析),保证"删掉的 Barrier 一定安全"。

4. 后端 Lowering:从内建函数到运行时调用

真正决定运行时形态的是 llvm/lib/CodeGen/CJBarrierLowering.cpp——即CJBarrierLowering Pass(Pass 声明见 llvm/include/llvm/CodeGen/Passes.h)。它把 Barrier 内建函数映射到CJ_MCC_*运行时桩函数,例如:

  • cj_gcwrite_ref→CJ_MCC_WriteRefField
  • cj_gcwrite_static_ref→CJ_MCC_WriteStaticRef
  • cj_atomic_swap→CJ_MCC_AtomicSwapReference

4.1 关键优化开关

Lowering 阶段暴露了几个影响性能形态的选项(均在 llvm/lib/CodeGen/CJBarrierLowering.cpp):

  • enable-tagged-pointer(默认开):启用有标记指针,让 Barrier 能区分"指针"和"整数",减少误写屏障;
  • enable-gc-phase/enable-gc-fast-path(默认开):开启 GC 分相与快速路径——热路径上只做一次状态判断,只有进入收集阶段才走慢路径,这是低延迟 Barrier 的关键;
  • 配合EnableSafepointOnly、EnableSafepointOutline等选项,与仓颉安全点机制(CJ_Safepoint_Stub、CJ_MCC_NewObjectFast)协同工作。

4.2 与 SafePoint 的协作

Barrier 和安全点(Safepoint)共同构成 GC 的"观测点"。X86 后端在 llvm/lib/Target/X86/X86ISelLowering.cpp 中对isCangjieSafepointStub的调用做专门处理(重装载 RSP 等),AArch64 后端也有对应实现——这意味着 Barrier/Lowering 的结果会一路下沉到目标机器指令层。

5. 全链路速览:一张表看懂 Barrier 生命周期

阶段关键文件做什么
① 生成CJIntrinsics.cpp、CJStructTypeGCInfo.cpp前端插入 Barrier 内建函数,计算结构体 GC 信息
② 拆分CJBarrierSplit.cpp粗粒度结构体 Barrier 拆细
③ 删除/验证CJBarrierOpt.cpp、CJGCLiveAnalysis.cpp大数组免屏障、新对象优化、正确性验证
④ LoweringCJBarrierLowering.cpp映射到CJ_MCC_*运行时调用,启用快速路径
⑤ 后端适配X86ISelLowering.cpp 等SafePoint 桩函数在目标后端的机器级处理

💡 想看 IR 层的完整变换效果,可以用 llvm/tools/opt 配合-print-after-all观察cj-barrier-lowering等 DebugType 的输出。

6. 小结

  • GC Barrier 优化是仓颉在 LLVM 上的深度定制:生成 → 拆分 → 删除 → 降级四步走;
  • 优化收益主要来自:结构体 Barrier 拆分、128KB+ 基本类型大数组免屏障、有标记指针与 GC 快速路径;
  • 想深入源码,从 llvm/lib/Transforms/Scalar/CJBarrierOpt.cpp 和 llvm/lib/CodeGen/CJBarrierLowering.cpp 两个入口读起,再顺藤摸瓜到目标后端即可。 🚀

【免费下载链接】llvm-projectLLVM 项目是一个模块化、可复用的编译器及工具链技术的集合。此fork用于添加仓颉编译器的功能,并支持仓颉编译器项目。项目地址: https://gitcode.com/Cangjie/llvm-project

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询