方舟编译器学习笔记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;InitPhases(mgr, phases);mgr.Run();theModule->Emit(vtableImplFile);timer.Stop();LogInfo::MapleLogger() << "Mpl2mpl&mplme consumed " << timer.Elapsed() << "s" << std::endl;}}
另外,这里在InitPass之前,有一个
这个文件位于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 filestd::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在整个编译过程中的使用,也还要进一步的去分析。
