vlambda博客
学习文章列表

方舟编译器学习笔记14 DriverRunner源码分析

学习笔记12学习笔记13,分析了phase相关的内容,基本理清了phase管理体系。但是,还有一个和phase有关的内容,那就是DriverRunner,本文就DriverRunner进行简单的分析和介绍。

官方文档“方舟编译器phase设计介绍”中,提到“使用者除了可以使用上面的方式自行添加phase外,还可以借助InterleavedManager和DriverRunner组成的框架对phase进行更有效的管理。”,我们在前文中也进行过分析,InterleavedManager对phase的管理,并没有绕开phase manager,而是基于phase manager进行实现的。

DriverRunner包含了从一个mpl文件到优化结果文件的所有过程。那么DriverRunner所要做的就像其名字所传递的直接含义,是一个驱动和运行的这么一个角色,它所要驱动的内容包含了一系列的phase,这些phase是从mpl文件到优化结果文件的过程总所必不可少的。现在对其主要的几个方法做介绍和分析。(DriverRunner的源码位于src/maple_driver/include/driver_runner.h和src/maple_driver/src/driver_runner.cpp)

1、DriverRunner的ParseInput方法负责解析mpl文件,这是为了后续的mpl2mpl做准备工作,也就是为了phase的工作做铺垫。

bool DriverRunner::ParseInput(std::string outputFile, std::string originBaseName) const { CHECK_MODULE(1);
LogInfo::MapleLogger() << "Starting parse input" << std::endl; MPLTimer timer; timer.Start();
MIRParser parser(*theModule); bool parsed = parser.ParseMIR(0, 0, false, true); if (!parsed) { parser.EmitError(outputFile); } timer.Stop(); LogInfo::MapleLogger() << "Parse consumed " << timer.Elapsed() << "s" << std::endl;
return parsed;}

从代码中看出来这是使用了MIRParser 的ParseMIR方法进行分析。

2、ProcessMpl2mplAndMePhases方法通过InterleavedManager负责phase的管理和运行。所以,其可以被视为是基于InterleavedManager进行的管理,这种情况下就相当于在我们之前的管理上加上了一层,变成了一个四级管理体系: DriverRunner->InterleavedManager->PhaseManager->Phase。

void DriverRunner::ProcessMpl2mplAndMePhases(std::string outputFile, std::string vtableImplFile) const { CHECK_MODULE();
if (mpl2mplOptions || meOptions) { LogInfo::MapleLogger() << "Processing mpl2mpl&mplme" << std::endl; MPLTimer timer; timer.Start();
InterleavedManager mgr(optMp, theModule, meInput, timePhases); std::vector<std::string> phases;#include "phases.def" InitPhases(mgr, phases); mgr.Run();
theModule->Emit(vtableImplFile);
timer.Stop(); LogInfo::MapleLogger() << "Mpl2mpl&mplme consumed " << timer.Elapsed() << "s" << std::endl; }}

另外,这里在InitPass之前,有一个

#include "phases.def"

这个文件位于src/maple_driver/defs/目录之下,其中包含了DriverRunner所要用的一系列phase,具体内容如下:

ADD_PHASE("classhierarchy", true)ADD_PHASE("vtableanalysis", true)ADD_PHASE("reflectionanalysis", true)ADD_PHASE("gencheckcast", true)ADD_PHASE("javaintrnlowering", true)// mephase beginADD_PHASE("ssatab", true)ADD_PHASE("aliasclass", true)ADD_PHASE("ssa", true)ADD_PHASE("analyzerc", true)ADD_PHASE("rclowering", true)ADD_PHASE("emit", true)// mephase endADD_PHASE("GenNativeStubFunc", true)ADD_PHASE("clinit", true)ADD_PHASE("VtableImpl", true)ADD_PHASE("javaehlower", true)ADD_PHASE("MUIDReplacement", true)

中间一部分,是我们之前提到的从MeFuncPhase继承来的phase,这里的phase的列表,是me_phases.def中的子集,也就是说me_phases.def并没有都用在从一个mpl文件到优化结果文件的过程中。剩下的phase都是从ModulePhase继承而来的phase,正好module_phases.def的phase对的上。但是需要说明的是这些phase的定位位置都不同,可能是和其具体做的事情有关,后续会专门撰文分析。

3、InitPhases方法被ProcessMpl2mplAndMePhases方法调用,它会将添加的phase拆解到InterleavedManager的phase manager集合里。

void DriverRunner::InitPhases(InterleavedManager &mgr, std::vector<std::string> &phases) const { if (phases.empty()) { return; }
const PhaseManager *curManager = nullptr; std::vector<std::string> curPhases;
for (std::string phase : phases) { auto temp = mgr.GetSupportPhaseManager(phase);
if (temp != nullptr) { if (temp != curManager) { AddPhases(mgr, curPhases, curManager); curManager = temp; curPhases.clear(); }
AddPhase(curPhases, phase, curManager); } }
AddPhases(mgr, curPhases, curManager);}

这里以两种不同的形式调用了AddPhases。下文会做介绍。

4、DriverRunner的InitPhases调用了AddPhases和AddPhase方法。(具体内容参见:

小乖他爹:方舟编译器学习笔记50 方舟编译器phase的体系运行机制分析

void DriverRunner::AddPhases(InterleavedManager &mgr, std::vector<std::string> &phases, const PhaseManager *phaseManager) const { if (phaseManager == nullptr) { return; }
if (typeid(*phaseManager) == typeid(ModulePhaseManager)) { mgr.AddPhases(phases, true, timePhases); } else if (typeid(*phaseManager) == typeid(MeFuncPhaseManager)) { mgr.AddPhases(phases, false, timePhases, genMemPl); } else { CHECK_FATAL(false, "Should not reach here, phases should be handled"); }}
void DriverRunner::AddPhase(std::vector<std::string> &phases, std::string phase, const PhaseManager *phaseManager) const { CHECK_FATAL(phaseManager != nullptr, "Invalid phase manager");
if (typeid(*phaseManager) == typeid(ModulePhaseManager)) { if (mpl2mplOptions && Options::skipPhase.compare(phase) != 0) { phases.push_back(phase); } } else if (typeid(*phaseManager) == typeid(MeFuncPhaseManager)) { if (meOptions && meOptions->GetSkipPhases().find(phase) == meOptions->GetSkipPhases().end()) { phases.push_back(phase); } } else { CHECK_FATAL(false, "Should not reach here, phase should be handled"); }}

5、上述的几个重要方法,第1个方法ParseInput是为phase的使用做准备;第2个方法ProcessMpl2mplAndMePhases调用了第3个方法InitPhases;第3个InitPhases方法对第4个方法AddPhases进行了调用。而第一个方法ParseInput和第2个方法ProcessMpl2mplAndMePhases都是被方法Run所调用的。所以,我们上述说了这么多,所有的工作都已经统一到了Run方法中。

int DriverRunner::Run() { CHECK_MODULE(1);
if (exeNames.empty()) { LogInfo::MapleLogger() << "Fatal error: no exe specified" << std::endl; return 1; }
int ret = 0; printOutExe = exeNames[exeNames.size() - 1];
// Prepare output file std::string::size_type lastdot = actualInput.find_last_of("."); std::string baseName = lastdot == std::string::npos ? actualInput : actualInput.substr(0, lastdot); std::string originBaseName = baseName; std::string outputFile = baseName.append(GetPostfix());
bool parsed = ParseInput(outputFile, originBaseName);
if (parsed) { if (mpl2mplOptions || meOptions) { std::string vtableImplFile = originBaseName; vtableImplFile.append(".VtableImpl.mpl"); originBaseName.append(".VtableImpl"); ProcessMpl2mplAndMePhases(outputFile, vtableImplFile); }
} else { ret = 1; }
return ret;}

因为我们最近一直在讨论phase,而Run方法又是Phase类的重要方法,所以看到Run方法的第一反应就是DriverRunner类和Phase等一系列类有什么继承关系,其实并没有,这样的结构体系相对就清楚多了:

class DriverRunner final {

上述内容基本上将DriverRunner所要做的工作做了简要介绍,并结合代码对其主要方法都做了介绍。在这个过程中发现,两大类Phase的具体实现位置,并不统一,而是分散在多个目录,可能是跟其具体的功能有关,后续会有针对性的分析。另外,DriverRunner在整个编译过程中的使用,也还要进一步的去分析。