为仓颉语言编译器增加新的目标平台的难点在哪里

仓颉语言编译器增加新目标平台的难点

引言

华为在 HDC 2025 主题演讲中承诺2025 年 7 月底之前仓颉编程语言正式开源。7月30日,仓颉编程语言正式开源,开源内容包括编译器、运行时和标准库等。现在可以从下面地址下载:

华为开源了其编译器以后,最先想到的就是能否为其增加对RISC-V或者龙芯等平台的支持。现阶段华为投在仓颉语言上资源非常有限,估计短时间内不会做这些工作。今天就分享一些本人的思考,由于本人对编译器并不了解,可能有些观点不正确,请批评指正。

编译器结构分析

仓颉编译器的整体框架及编译流程如下图展示:

仓颉编译器采用了LLVM架构,前端生成LLVMIR,然后用后端生成目标代码,并经过链接生成可执行文件。

运行时和标准库是基于C代码编写的,应该容易移植到新的目标平台。

下面是华为给出的Linux 环境, 每个组件依赖的构建工具和版本要求如下:

依赖构建工具cjccjdbruntimestdcjpmcjfmtlsphlestdx
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 标志:包含垃圾回收标志位
模块间关系与整体架构

这些文件构成一个完整的托管运行时系统,各模块关系如下:

  1. 启动流程:0-test.s → 4-test.s → 2-test.s/5-test.s
  2. 核心逻辑:1-test.s 实现业务功能,3-test.s 提供辅助
  3. 运行时支持:5-test.s 处理入口与安全机制,6-test.s 提供元数据
  4. 跨文件调用:通过 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 策略(如noneshadow-stackstatepoint-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这样的问题。这个问题留待今后分析。

### 仓颉中文编译器的技术信息与工具 #### 1. 仓颉中文编译器概述 仓颉输入法作为一种基于字根的编码输入方法,其核心在于将汉字拆解为基本字根并映射到键盘上的键位[^1]。然而,“仓颉中文编译器”这一术语并非传统意义上的编程语言编译器,而是指与仓颉输入法相关的工具或软件,用于支持仓颉编码的输入、转换和处理。 #### 2. 下载与安装 仓颉输入法及相关工具通常由第三方开发者提供。以下是一些常见的下载来源: - **Windows系统**:可以使用微软自带的输入法配置工具或通过第三方软件如Rime输入法框架进行安装[^2]。 - **macOS系统**:macOS内置了对仓颉输入法的支持,用户可以在系统偏好设置中启用[^3]。 - **Linux系统**:可以通过IBus或Fcitx等输入法框架安装仓颉输入法插件[^4]。 #### 3. 使用方法 在安装完成后,用户需要熟悉仓颉输入法的字根表和拆分规则。以下是基本使用步骤: - 配置输入法:确保仓颉输入法已正确添加到系统的输入法列表中。 - 字根学习:参考仓颉字根表,掌握常见汉字的拆分方式。 - 输入练习:通过实际输入汉字,逐步提高打字速度和准确性。 #### 4. 配置与优化 对于高级用户,可以通过以下方式进行配置和优化: - **自定义词库**:许多仓颉输入法支持用户自定义词库,以适应特定领域的专业术语[^5]。 - **调整候选词顺序**:通过修改配置文件或使用图形界面工具,调整常用词汇的优先级。 - **扩展功能**:部分输入法框架(如Rime)允许用户编写脚本,实现更复杂的文本处理功能。 #### 5. 示例代码:Rime配置文件示例 以下是一个简单的Rime配置文件示例,展示如何启用仓颉输入法: ```yaml schema_list: - schema: cangjie5 cangjie5: engine_name: cangjie5 display_name: "仓颉输入法" layout: us ``` #### 6. 常见问题与解决方案 - **问题1**:无法找到仓颉输入法选项。 - 确保已正确安装输入法,并检查系统语言设置是否支持中文输入[^6]。 - **问题2**:输入时候选词不准确。 - 调整输入法的学习模式,或手动更新词库以包含更多常用词汇。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神一样的老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值