仓颉语言编译器学习(一):编译器插件开发

新开一个系列,分享一些仓颉编程语言的知识。

前言

仓颉编程语言是华为公司推出的产品,也是我在华为的重点工作内容之一,内部研发过程中多次保障它的网络安全,在2025年7月30日开源。

毫不夸张地说,我是全世界最懂仓颉安全的人,那么我也应当为之布道。

愿景:本系列不一定讲安全,想到什么讲什么,希望重铸 LLVM学习 系列的荣光,也希望仓颉语言能蒸蒸日上。

任务目标

创建cmake项目,编译一个so/dll,使用 cjc 编译器加载它,打印 CHIR 的基本信息。

环境准备

Linux可以,Windows不行,Mac不清楚。

当前 1.0.x 的LTS发布包里缺少include文件,需要从 https://gitcode.com/Cangjie/nightly_build/releases 下载一份daily-build,保证能找到对应的include文件。

1
2
3
4
5
6
7
8
9
tree cangjie/include/
cangjie/include/
├── MetaTransform.h
└── cangjie
├── AST
│   ├── ASTCasting.h
│   ├── ASTContext.h
│   ├── ASTKind.inc
│   ├── ASTTypeValidator.h

编写pass

cmake项目依赖,添加头文件,链接路径。当前依赖的是 libcangjie-lsp.so,这个行为很逆天,已提 issue https://gitcode.com/Cangjie/cangjie_compiler/issues/460

1
2
3
4
5
6
7
8
9
10
11
12
cmake_minimum_required(VERSION 3.5)
project(HelloCjPlugin)

set(CMAKE_CXX_STANDARD 17)

if ("$ENV{CANGJIE_HOME}" STREQUAL "")
message(FATAL_ERROR "\n##### Please confirm cjc is well configured in current environment. #####")
endif ()
include_directories($ENV{CANGJIE_HOME}/include)
link_directories($ENV{CANGJIE_HOME}/tools/lib)
add_library(skeleton SHARED skeleton.cpp)
target_link_libraries(skeleton PRIVATE cangjie-lsp)

MetaTransform.h 文件提供了一键注册的宏展开

1
#define CHIR_PLUGIN(plugin_name)

展开之后代码大约是

1
2
3
4
5
6
7
8
9
extern "C" MetaTransformPluginInfo getMetaTransformPluginInfo() {
return {
Cangjie::CANGJIE_VERSION.c_str(), [](MetaTransformPluginBuilder &mtBuilder) {
mtBuilder.RegisterCHIRPluginCallback([](CHIRPluginManager &mtm, CHIR::CHIRBuilder &builder) {
mtm.AddMetaTransform(std::make_unique<plugin_name>(builder));
});
}
};
}

简单写一个 FunctionPass(完整代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "cangjie/Basic/Print.h"
#include "cangjie/CHIR/IR/CHIRBuilder.h"
#include "cangjie/MetaTransformation/MetaTransform.h"

using namespace Cangjie;

class PrintFuncNamePass final : public MetaTransform<CHIR::Func> {
public:
explicit PrintFuncNamePass(CHIR::CHIRBuilder &builder) : builder(builder) {
}

void Run(CHIR::Func &func) override {
Infoln("PrintFuncNamePass:", func.GetRawMangledName());
}

private:
CHIR::CHIRBuilder &builder;
};

CHIR_PLUGIN(PrintFuncNamePass)

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cmake -S . -B b
cmake --build b
cjc test.cj --plugin=b/libskeleton.so
info: PrintFuncNamePass:8std.core5Array<1>S
info: PrintFuncNamePass:8std.core5Array<1>S
info: PrintFuncNamePass:8std.core5Array<1>S
info: PrintFuncNamePass:8std.core5Range<1>S
info: PrintFuncNamePass:8std.core5Range<1>S
info: PrintFuncNamePass:8std.core5Range<1>S
info: PrintFuncNamePass:8std.core5Array<1>S
info: PrintFuncNamePass:8std.core5Array<1>S
info: PrintFuncNamePass:8std.core5Array<1>S
......
info: PrintFuncNamePass:7default12split_normalF6String6String$$9ArrayList<6String>
info: PrintFuncNamePass:7default5splitF6String6String$$9ArrayList<6String>
info: PrintFuncNamePass:7default4main

Windows环境呢

拼尽全力能编过,但拼尽全力加载一直失败,不清楚原因。。。

总结

还是很简单很容易上手的,完整代码就在文中,直接复制即可。