лязг переход
Чтобы понять процесс добавления и выполнения Pass, мы должны сначала понять, как перейти от clang к LLVM в предыдущем разделе компиляции.
Когда AST создан, он передается для обработки подклассу BackendConsumer класса ASTConsumer. Во-первых, преобразование из AST в IR будет производиться через CodeGenerator. HandleTranslationUnit вызывается после завершения перевода
EmbedBitcode(getModule(), CodeGenOpts, llvm::MemoryBufferRef());
EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts,
LangOpts, C.getTargetInfo().getDataLayout(),
getModule(), Action, std::move(AsmOutStream));
Здесь будут вызываться две функции, EmbedBitcode используется для обработки параметра -fembed-bitcode, цель — добавить секцию для хранения биткода в сгенерированный obj файл.
Определение EmitBackendOutput находится в BackendUtil.cpp. В функции будет определен AsmHelper, и будет вызываться EmitAssembly или EmitAssemblyWithNewPassManager.
if (CGOpts.ExperimentalNewPassManager)
AsmHelper.EmitAssemblyWithNewPassManager(Action, std::move(OS));
else
AsmHelper.EmitAssembly(Action, std::move(OS));
Файл BackendUtil.cpp все еще находится в clang, но здесь он довольно близок к LLVM.
Сосредоточьтесь на EmitAssembly
legacy::PassManager PerModulePasses;
PerModulePasses.add(
createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));
legacy::FunctionPassManager PerFunctionPasses(TheModule);
PerFunctionPasses.add(
createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));
CreatePasses(PerModulePasses, PerFunctionPasses);
Здесь генерируются два PassManager, один управляет ModulePass, а другой управляет FunctionPass. Добавляйте и управляйте двумя командами PassManager и Passes в CreatePasses.
В дополнение к PerModulePasses и PerFunctionPasses существует также CodeGenPasses, который добавляет проходы для преобразования и генерации инструкций в соответствии с требованиями компиляции CodeGenOpts.
legacy::PassManager CodeGenPasses;
CodeGenPasses.add(
createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));
std::unique_ptr<raw_fd_ostream> ThinLinkOS;
switch (Action) {
case Backend_EmitNothing:
break;
case Backend_EmitBC:
if (CodeGenOpts.EmitSummaryIndex) {
if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) {
std::error_code EC;
ThinLinkOS.reset(new llvm::raw_fd_ostream(
CodeGenOpts.ThinLinkBitcodeFile, EC,
llvm::sys::fs::F_None));
if (EC) {
Diags.Report(diag::err_fe_unable_to_open_output) << CodeGenOpts.ThinLinkBitcodeFile
<< EC.message();
return;
}
}
PerModulePasses.add(
createWriteThinLTOBitcodePass(*OS, ThinLinkOS.get()));
}
else
PerModulePasses.add(
createBitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists));
break;
case Backend_EmitLL:
PerModulePasses.add(
createPrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists));
break;
default:
if (!AddEmitPasses(CodeGenPasses, Action, *OS))
return;
}
Когда пропуск добавлен, начните по одному
{
PrettyStackTraceString CrashInfo("Per-function optimization");
PerFunctionPasses.doInitialization();
for (Function &F : *TheModule)
if (!F.isDeclaration())
PerFunctionPasses.run(F);
PerFunctionPasses.doFinalization();
}
{
PrettyStackTraceString CrashInfo("Per-module optimization passes");
PerModulePasses.run(*TheModule);
}
{
PrettyStackTraceString CrashInfo("Code generation");
CodeGenPasses.run(*TheModule);
}
Когда Pass начинает выполняться, он официально входит в область действия кода LLVM и возвращается в clang после завершения выполнения.
PassManagerBuilder и addExtension
Оглядываясь назад на содержимое CreatePasses, первое — это оболочка, которая инкапсулирует PassManagerBuilder с LangOptions и CGOpts.
// We need this wrapper to access LangOpts and CGOpts from extension functions
// that we add to the PassManagerBuilder.
class PassManagerBuilderWrapper : public PassManagerBuilder {
public:
PassManagerBuilderWrapper(const Triple &TargetTriple,
const CodeGenOptions &CGOpts,
const LangOptions &LangOpts)
: PassManagerBuilder(), TargetTriple(TargetTriple), CGOpts(CGOpts),
LangOpts(LangOpts) {}
const Triple &getTargetTriple() const { return TargetTriple; }
const CodeGenOptions &getCGOpts() const { return CGOpts; }
const LangOptions &getLangOpts() const { return LangOpts; }
private:
const Triple &TargetTriple;
const CodeGenOptions &CGOpts;
const LangOptions &LangOpts;
};
}
Первое, что нужно сделать в функции CreatePasses, — сгенерировать PassManagerBuilder и связать вместе содержимое CGOpts и LangOpts.
PassManagerBuilderWrapper PMBuilder(TargetTriple, CodeGenOpts, LangOpts);
// At O0 and O1 we only run the always inliner which is more efficient. At
// higher optimization levels we run the normal inliner.
if (CodeGenOpts.OptimizationLevel <= 1) {
bool InsertLifetimeIntrinsics = (CodeGenOpts.OptimizationLevel != 0 &&
!CodeGenOpts.DisableLifetimeMarkers);
PMBuilder.Inliner = createAlwaysInlinerLegacyPass(InsertLifetimeIntrinsics);
} else {
// We do not want to inline hot callsites for SamplePGO module-summary build
// because profile annotation will happen again in ThinLTO backend, and we
// want the IR of the hot path to match the profile.
PMBuilder.Inliner = createFunctionInliningPass(
CodeGenOpts.OptimizationLevel, CodeGenOpts.OptimizeSize,
(!CodeGenOpts.SampleProfileFile.empty() &&
CodeGenOpts.EmitSummaryIndex));
}
PMBuilder.OptLevel = CodeGenOpts.OptimizationLevel;
PMBuilder.SizeLevel = CodeGenOpts.OptimizeSize;
PMBuilder.SLPVectorize = CodeGenOpts.VectorizeSLP;
PMBuilder.LoopVectorize = CodeGenOpts.VectorizeLoop;
PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops;
PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions;
PMBuilder.PrepareForThinLTO = CodeGenOpts.EmitSummaryIndex;
PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO;
PMBuilder.RerollLoops = CodeGenOpts.RerollLoops;
Затем используйте интерфейс addExtension PassManagerBuilder в соответствии с различными LangOpts и CodeGenOpts.
if (CodeGenOpts.DebugInfoForProfiling ||
!CodeGenOpts.SampleProfileFile.empty())
PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
addAddDiscriminatorsPass);
...
if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds)) {
PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
addBoundsCheckingPass);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
addBoundsCheckingPass);
}
...
if (LangOpts.Sanitize.hasOneOf(SanitizerKind::Efficiency)) {
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
addEfficiencySanitizerPass);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
addEfficiencySanitizerPass);
}
...
Первый параметр, который принимает addExtension, — это тип ExtensionPointTy, указывающий позицию, в которую вставляется Pass. ExtensionPointTy — это перечисление со следующим содержимым
enum ExtensionPointTy {
//插入时机尽可能的早,允许在代码从前端出来后就被处理
EP_EarlyAsPossible,
//在模块级优化前
EP_ModuleOptimizerEarly,
//在循环优化后
EP_LoopOptimizerEnd,
//允许插入优化pass在大多数主优化后,在clean-ish优化前
EP_ScalarOptimizerLate,
//在最后
EP_OptimizerLast,
//在vectorizer和其他更高级的平台指定优化之前
EP_VectorizerStart,
//O0下位于inlining pass之后
EP_EnabledOnOptLevel0,
//执行类似于与指令组合的窥孔优化(peephole optimizations),在指令组合pass的每个实例之后
EP_Peephole,
//后期循环的规范化和简化这是最后一个循环优化管道,在循环删除前。
//必须是LoopPass的实例,加入到可以删除循环的地方,例如指定平台的循环方言识别
EP_LateLoopOptimizations,
//通过CGPassManager增加CallGraphSCC pass在主CallGraphSCC pass之后,function simplification passe之前
EP_CGSCCOptimizerLate,
};
Последний параметр — ExtensionFn.
typedef std::function<void(const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM)>
ExtensionFn;
Эта функция в основном вызывает интерфейс добавления PassManager для добавления указанного прохода в очередь.
В PassManagerBuilder есть вектор, в котором хранится функция добавления Pass
std::vector<std::pair<ExtensionPointTy, ExtensionFn>> Extensions;
addExtension — добавить к вектору пару, состоящую из ExtensionPointTy и ExtensionFn.
Последний шаг добавления пропуска
В конце CreatePasses вызовите populate, чтобы завершить процесс добавления расширений в PassManager.
PMBuilder.populateFunctionPassManager(FPM);
PMBuilder.populateModulePassManager(MPM);
Возьмите populateModulePassManager в качестве примера.
void PassManagerBuilder::populateModulePassManager(
legacy::PassManagerBase &MPM) {
...
// If all optimizations are disabled, just run the always-inline pass and,
// if enabled, the function merging pass.
if (OptLevel == 0) {
addPGOInstrPasses(MPM);
if (Inliner) {
MPM.add(Inliner);
Inliner = nullptr;
}
// FIXME: The BarrierNoopPass is a HACK! The inliner pass above implicitly
// creates a CGSCC pass manager, but we don't want to add extensions into
// that pass manager. To prevent this we insert a no-op module pass to reset
// the pass manager to get the same behavior as EP_OptimizerLast in non-O0
// builds. The function merging pass is
if (MergeFunctions)
MPM.add(createMergeFunctionsPass());
else if (GlobalExtensionsNotEmpty() || !Extensions.empty())
MPM.add(createBarrierNoopPass());
addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);
// Rename anon globals to be able to export them in the summary.
// This has to be done after we add the extensions to the pass manager
// as there could be passes (e.g. Adddress sanitizer) which introduce
// new unnamed globals.
if (PrepareForThinLTO)
MPM.add(createNameAnonGlobalPass());
return;
}
...
...
Как видно из команды Extension, CreatePass больше предназначен для классификации содержимого, заданного параметрами CodeGenOpts и LangOpts, а populate — это то место, где Pass наконец попадает в очередь выполнения. Следующий код примерно такой же, прямо вызовите функцию добавления для добавления в соответствии с OptLevel, LibraryInfo и другой информацией.
addExtensionsToPM — это интерфейс для добавления контента в Extensions в PassManager.
void PassManagerBuilder::addExtensionsToPM(ExtensionPointTy ETy,
legacy::PassManagerBase &PM) const {
if (GlobalExtensionsNotEmpty()) {
for (auto &Ext : *GlobalExtensions) {
if (Ext.first == ETy)
Ext.second(*this, PM);
}
}
for (unsigned i = 0, e = Extensions.size(); i != e; ++i)
if (Extensions[i].first == ETy)
Extensions[i].second(*this, PM);
}
Добавление Pass примерно такое же.