仓颉 LLVM GC Barrier 优化全链路:从 Barrier 生成到后端 Lowering 的终极指南
仓颉 LLVM GC Barrier 优化全链路从 Barrier 生成到后端 Lowering 的终极指南【免费下载链接】llvm-projectLLVM 项目是一个模块化、可复用的编译器及工具链技术的集合。此fork用于添加仓颉编译器的功能并支持仓颉编译器项目。项目地址: https://gitcode.com/Cangjie/llvm-projectGC 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.cppGC 存活分析保证删掉的 Barrier 一定安全。4. 后端 Lowering从内建函数到运行时调用真正决定运行时形态的是 llvm/lib/CodeGen/CJBarrierLowering.cpp——即CJBarrierLowering PassPass 声明见 llvm/include/llvm/CodeGen/Passes.h。它把 Barrier 内建函数映射到CJ_MCC_*运行时桩函数例如cj_gcwrite_ref→CJ_MCC_WriteRefFieldcj_gcwrite_static_ref→CJ_MCC_WriteStaticRefcj_atomic_swap→CJ_MCC_AtomicSwapReference4.1 关键优化开关Lowering 阶段暴露了几个影响性能形态的选项均在 llvm/lib/CodeGen/CJBarrierLowering.cppenable-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),仅供参考