仓颉语言编译器学习(二):插件加载和运行

省流:先运行动态加载的PASS,再运行预置的PASS。

加载过程

AI真的太好用了,我现在就是个审稿人,大意:

  • 命令行入参允许传入多个plugin.so
  • 每个plugin允许注册多个pass
  • pass共有两类,一类是FunctionPass,一类是ModulePass

参数解析

文件: src/Option/OptionAction.cpp L644-L658
功能: 包含了处理 PLUGIN_PATH 选项的 lambda 函数,将插件路径添加到 GlobalOptionspluginPaths 列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{ Options::ID::PLUGIN_PATH, [](GlobalOptions& opts, const OptionArgInstance& arg) {
auto maybePath = opts.CheckInputFilePath(arg.value);
auto suffix = GlobalOptions::GetSharedLibraryExtension(opts.host.os);
if (maybePath.has_value()) {
auto path = maybePath.value();
if ('.' + FileUtil::GetFileExtension(path) == suffix) {
opts.pluginPaths.emplace_back(maybePath.value());
return true;
}
Errorf("'%s' requires a dynamic library path with '%s' suffix.\n", arg.name.c_str(), suffix.c_str());
return false;
}
Errorf("'%s' only accepts an existing dynamic library path.\n", arg.name.c_str());
return false;
}},

寻找入口

文件: src/Frontend/CompilerInstance.cpp L264-L268, L279-L304, L314-L334
功能: PerformPluginLoad 方法遍历 pluginPaths,加载动态库。其内部调用的 MetaTransformPlugin::Get 方法负责打开动态库,查找 getMetaTransformPluginInfo 符号,并将其封装为 MetaTransformPluginInfo 结构体。随后,PerformPluginLoad 会调用 metaTransformPlugin.RegisterCallbackTo 来注册插件Pass。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace {
class MetaTransformPlugin {
public:
static MetaTransformPlugin Get(const std::string& path);
void RegisterCallbackTo(MetaTransformPluginBuilder& mtm) const;
// ...
MetaTransformPlugin MetaTransformPlugin::Get(const std::string& path)
{
HANDLE handle = nullptr;
#ifdef _WIN32
handle = InvokeRuntime::OpenSymbolTable(path);
#elif defined(__linux__) || defined(__APPLE__)
handle = InvokeRuntime::OpenSymbolTable(path, RTLD_NOW | RTLD_LOCAL); // 打开动态库
#endif
if (!handle) {
CJC_ABORT();
}
void* fPtr = InvokeRuntime::GetMethod(handle, "getMetaTransformPluginInfo"); // 查找符号
if (!fPtr) {
CJC_ABORT();
}
auto pluginInfo = reinterpret_cast<MetaTransformPluginInfo (*)()>(fPtr)(); // 调用函数获取插件信息
return MetaTransformPlugin(path, pluginInfo, handle);
}
// ...
bool CompilerInstance::PerformPluginLoad()
{
for (auto pluginPath : invocation.globalOptions.pluginPaths) { // loop for all plugins
auto metaTransformPlugin = MetaTransformPlugin::Get(pluginPath); // 调用上述Get方法
if (!metaTransformPlugin.IsValid()) {
diag.DiagnoseRefactor(DiagKindRefactor::not_a_valid_plugin, DEFAULT_POSITION, pluginPath);
}
AddPluginHandle(metaTransformPlugin.GetHandle());
metaTransformPlugin.RegisterCallbackTo(metaTransformPluginBuilder); // 注册Pass到builder
}
return true;
}

执行pass

文件: src/CHIR/CHIR.cpp L1271-L1307
功能: PerformPlugin 方法调用 metaTransformPluginBuilder.BuildCHIRPluginManager 来构建 Pass 管理器,然后遍历并执行所有注册的插件Pass。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
bool ToCHIR::PerformPlugin(CHIR::Package& package)
{
bool succeed = true;
bool hasPluginForCHIR = false;
Utils::ProfileRecorder recorder("CHIR", "Plugin Execution");
CHIRPluginManager chirPluginManager = ci.metaTransformPluginBuilder.BuildCHIRPluginManager(builder); // 构建PassManager
chirPluginManager.ForEachMetaTransformConcept([&package, &hasPluginForCHIR](MetaTransformConcept& mtc) {
if (!mtc.IsForCHIR()) {
return;
}
hasPluginForCHIR = true;
if (mtc.IsForFunc()) {
for (auto func : package.GetGlobalFuncs()) {
static_cast<MetaTransform<CHIR::Func>*>(&mtc)->Run(*func); // 执行插件Pass
}
} else if (mtc.IsForPackage()) {
static_cast<MetaTransform<CHIR::Package>*>(&mtc)->Run(package); // 执行插件Pass
} else {
CJC_ASSERT(false && "Should not reach here.");
}
});
if (!succeed) {
diag.DiagnoseRefactor(DiagKindRefactor::plugin_throws_exception, DEFAULT_POSITION);
} else if (hasPluginForCHIR && builder.IsEnableIRCheckerAfterPlugin()) {
DumpCHIRToFile("PLUGIN");
succeed = RunIRChecker(Phase::PLUGIN);
}
return succeed;
}

内置Pass与动态加载Pass的执行顺序

执行顺序由 src/CHIR/CHIR.cpp 文件中的 ToCHIR::Run 方法严格定义。

核心执行流程:ToCHIR::Run 方法

文件: src/CHIR/CHIR.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
bool ToCHIR::Run()
{
// ...

// 步骤 1: 内置Pass - 将AST转换为CHIR
// 这是CHIR阶段的入口,一个核心的内置Pass将前端AST转换为未经优化的“原始”CHIR。
if (!TranslateToCHIR({})) {
return false;
}

// ...

// 步骤 2: 执行所有动态加载的Pass(插件)
// 插件操作的是一个稳定、未经优化的CHIR形态。
if (!PerformPlugin(*chirPkg)) {
return false;
}

// 步骤 3: 执行一系列核心内置Pass
// 在所有插件执行完毕后,编译器才开始运行自己的规范化、检查和优化流程。

// ...

RunOptimizationPass();
DoClosureConversion();

// ... 后续其他内置Pass ...

return true;
}

总结

简洁明了,一气呵成