引言
华为在 HDC 2025 主题演讲中承诺2025 年 7 月底之前仓颉编程语言正式开源。7月30日,仓颉编程语言正式开源,开源内容包括编译器、运行时和标准库等。现在可以从下面地址下载:
- 编译器:项目首页 - cangjie_compiler:仓颉编译器源码及 cjdb 调试工具。 - GitCode
- 运行时和标准库:项目首页 - cangjie_runtime:仓颉编程语言运行时与标准库。 - GitCode
- stdx库:项目首页 - cangjie_stdx:仓颉编程语言提供了 stdx 模块,该模块提供了网络、安全等领域的通用能力。 - GitCode
华为开源了其编译器以后,最先想到的就是能否为其增加对RISC-V或者龙芯等平台的支持。现阶段华为投在仓颉语言上资源非常有限,估计短时间内不会做这些工作。今天就分享一些本人的思考,由于本人对编译器并不了解,可能有些观点不正确,请批评指正。
编译器结构分析
仓颉编译器的整体框架及编译流程如下图展示:

仓颉编译器采用了LLVM架构,前端生成LLVMIR,然后用后端生成目标代码,并经过链接生成可执行文件。
运行时和标准库是基于C代码编写的,应该容易移植到新的目标平台。
下面是华为给出的Linux 环境, 每个组件依赖的构建工具和版本要求如下:
| 依赖构建工具 | cjc | cjdb | runtime | std | cjpm | cjfmt | lsp | hle | stdx |
|---|---|---|---|---|---|---|---|---|---|
| python: >3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| googletest: >=1.18 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| llvm: >=15.0.4 且 <16 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| binutils: >=2.38 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| gcc: >7.3.0 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| cmake: >=3.16.5 且 <4 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| ninja: >1.10 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
| openssl: >3 | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
所以重点应该是如何让llc生成目标平台的汇编代码。
中间代码分析
仓颉语言的编译器有个选项:
--save-temps <value>
该选项保留编译过程中生成的中间文件并保存至 <value> 路径下。编译器会保留编译过程中生成的 .bc, .o 等中间文件。
我尝试保存了中间结果,发现cjc把程序分成7个部分,最后再把7个.o文件合并成一个。

使用Trae.ai分析了一下代码,下面是它给出的分析结果:
根据
temp目录下的所有汇编文件,以下是完整分析:1.
0-test.s
- 程序入口与初始化:包含
main函数和运行时初始化逻辑- 包初始化:定义
_CGP7defaultiiHv等包初始化函数- 运行时启动:调用
CJ_MRT_CjRuntimeInit和CJ_MRT_CjRuntimeStart启动运行时环境- 安全机制:包含栈溢出检查和安全点处理
2.
1-test.s
- 业务逻辑实现:定义
_CN7default4mainHvEL_E_20核心业务函数- 字符串处理:使用
StringBuilder构建字符串- 控制台输出:调用
_CNat7printlnHRNat6StringE打印信息- 延迟操作:通过
_CNat5sleepHRNat8DurationE实现休眠- 循环逻辑:包含条件判断和循环控制流
3.
2-test.s
- 多线程管理:创建和管理线程,调用
CJ_MCC_NewCJThread- 异步操作:使用
Future处理异步任务- 对象创建:通过
CJ_MCC_NewObjectFast创建对象实例- 异常处理:包含
SpawnException异常抛出逻辑- 并发控制:实现线程等待和同步机制
4.
3-test.s
- 辅助函数:定义
_CCN7default4mainHvEL_E_20$g辅助函数- 对象管理:处理对象字段赋值和引用
- 内存分配:实现
CJ_MCC_NewObjectFast内存分配函数- 栈映射:包含栈映射元数据,支持垃圾回收
5.
4-test.s
- 用户入口:定义
user.main用户主函数- 函数转发:调用
_CN7default6<main>Hv函数- 异常表:包含 GCC 异常处理表
- 元数据:提供方法描述和字符串池信息
6.
5-test.s
- 入口函数:定义
_CCN7default4mainHvEL_E_20$i入口函数- 栈管理:实现动态栈分配与溢出检查
- 安全点处理:调用
CJ_MCC_HandleSafepoint处理垃圾回收安全点- 异常框架:包含完整的 GCC 异常处理元数据
- 元数据管理:提供栈映射和方法描述信息
7.
6-test.s
- 元数据存储:定义
default_default_6.packageInfo包信息结构- 反射支持:提供反射相关的字符串常量
- 全局变量:声明
CJ_MCC_HandleSafepoint.CJStubGV等全局变量- GC 标志:包含垃圾回收标志位
模块间关系与整体架构
这些文件构成一个完整的托管运行时系统,各模块关系如下:
- 启动流程:0-test.s → 4-test.s → 2-test.s/5-test.s
- 核心逻辑:1-test.s 实现业务功能,3-test.s 提供辅助
- 运行时支持:5-test.s 处理入口与安全机制,6-test.s 提供元数据
- 跨文件调用:通过 PLT 机制实现函数间相互调用(如 5-test.s 调用 1-test.s 中的函数)
整体架构呈现分层设计,从运行时初始化、业务逻辑实现到多线程支持,形成了一个功能完善的程序执行环境。
尝试代码生成
我先尝试使用llc(LLVM 静态编译器)将.bc文件编译为 RISC-V 架构的汇编代码:
test@ERAZE-UHYFLAFVU:/mnt/d/cangjie/temp$ llc -march=riscv64 -filetype=asm 0-test.bc -o 0-test-rv.s
LLVM ERROR: unsupported GC: cangjie
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0. Program arguments: llc -march=riscv64 -filetype=asm 0-test.bc -o 0-test-rv.s
#0 0x0000751f0903fd01 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xe3fd01)
#1 0x0000751f0903da3e llvm::sys::RunSignalHandlers() (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xe3da3e)
#2 0x0000751f09040236 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xe40236)
#3 0x0000751f07a42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
#4 0x0000751f07a969fc __pthread_kill_implementation ./nptl/./nptl/pthread_kill.c:44:76
#5 0x0000751f07a969fc __pthread_kill_internal ./nptl/./nptl/pthread_kill.c:78:10
#6 0x0000751f07a969fc pthread_kill ./nptl/./nptl/pthread_kill.c:89:10
#7 0x0000751f07a42476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
#8 0x0000751f07a287f3 abort ./stdlib/./stdlib/abort.c:81:7
#9 0x0000751f08f7a723 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xd7a723)
#10 0x0000751f08f7a74e (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xd7a74e)
#11 0x0000751f09143128 llvm::getGCStrategy(llvm::StringRef) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xf43128)
#12 0x0000751f092c2c30 llvm::GCModuleInfo::getGCStrategy(llvm::StringRef) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0x10c2c30)
#13 0x0000751f092c2ab3 llvm::GCModuleInfo::getFunctionInfo(llvm::Function const&) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0x10c2ab3)
#14 0x0000751f092c48e3 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0x10c48e3)
#15 0x0000751f091819e1 llvm::FPPassManager::doInitialization(llvm::Module&) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xf819e1)
#16 0x0000751f0917a831 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xf7a831)
#17 0x0000000000410223 main (/usr/lib/llvm-14/bin/llc+0x410223)
#18 0x0000751f07a29d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#19 0x0000751f07a29e40 call_init ./csu/../csu/libc-start.c:128:20
#20 0x0000751f07a29e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#21 0x000000000040b065 _start (/usr/lib/llvm-14/bin/llc+0x40b065)
Aborted (core dumped)
很不幸,程序报告错误了:LLVM ERROR: unsupported GC: cangjie。从错误信息来看,问题出在 LLVM IR 中使用了名为cangjie的垃圾回收(GC)策略,而当前的 LLVM 工具链(llc)不支持这种 GC 策略,导致编译失败。但 LLVM 默认只支持几种标准 GC 策略(如none、shadow-stack、statepoint-example等),不认识cangjie这个自定义 GC 策略。
我尝试现将.bc转换为.ll:
llvm-dis 0-test.bc -o 0-test.ll
在.ll中可以看到下面这样的语句:
define i32 @"cj_entry$"() gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" {
allocas:
%0 = alloca %Unit.Type, align 8
%1 = alloca %Unit.Type, align 8
%2 = alloca %Unit.Type, align 8
%3 = alloca %Unit.Type, align 8
%4 = alloca %Unit.Type, align 8
%5 = alloca %Unit.Type, align 8
%6 = alloca %Unit.Type, align 8
br label %entry
如何让LLVM支持cangjie这个自定义GC策略,超出了我现有的知识,有待后续了解。
华为提供了一个fork版本的LLVM:项目首页 - llvm-project:LLVM 项目是一个模块化、可复用的编译器及工具链技术的集合。此fork用于添加仓颉编译器的功能,并支持仓颉编译器项目。 - GitCode
这个版本修改了哪些地方暂时未知,但猜测它可以支持类似的一些cangjie特有的功能。
结束语
目前看,将仓颉语言移植到其他目标平台上是有不小工作量的,特别是要解决llc支持cangjie GC这样的问题。这个问题留待今后分析。
仓颉语言编译器增加新目标平台的难点
2847

被折叠的 条评论
为什么被折叠?



