方舟编译器学习笔记14 DriverRunner源码分析
Posted 方舟编译器开源
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了方舟编译器学习笔记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 begin
ADD_PHASE("ssatab", true)
ADD_PHASE("aliasclass", true)
ADD_PHASE("ssa", true)
ADD_PHASE("analyzerc", true)
ADD_PHASE("rclowering", true)
ADD_PHASE("emit", true)
// mephase end
ADD_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在整个编译过程中的使用,也还要进一步的去分析。
以上是关于方舟编译器学习笔记14 DriverRunner源码分析的主要内容,如果未能解决你的问题,请参考以下文章