Implementing a new pass – Optimizing IR-3
- Now, we are ready to declare the functions. There is nothing unusual here: first, the function type is created, followed by the functions: Type *VoidTy = Type::getVoidTy(M.getContext());
PointerType *PtrTy =
PointerType::getUnqual(M.getContext());
FunctionType *EnterExitFty =
FunctionType::get(VoidTy, {PtrTy}, false);
Function *EnterFn = Function::Create(
EnterExitFty, GlobalValue::ExternalLinkage,
“__ppp_enter”, M);
Function *ExitFn = Function::Create(
EnterExitFty, GlobalValue::ExternalLinkage,
“__ppp_exit”, M); - All we need to do now is loop over all the functions of the module and instrument the found functions by calling our instrument() method. Of course, we need to ignore function declarations, which are just prototypes. There can also be functions without a name, which does not work well with our approach. We’ll filter out those functions too: for (auto &F : M.functions()) {
if (!F.isDeclaration() && F.hasName())
instrument(F, EnterFn, ExitFn);
} - Lastly, we must declare that we did not preserve any analysis. This is most likely too pessimistic but we are on the safe side by doing so: return PreservedAnalyses::none();
}
The functionality of our new pass is now implemented. To be able to use our pass, we need to register it with the PassBuilder object. This can happen in two ways: statically or dynamically. If the plugin is statically linked, then it needs to provide a function called getPluginInfo(). To use dynamic linking, the llvmGetPassPluginInfo() function needs to be provided. In both cases, an instance of the PassPluginLibraryInfo struct is returned, which provides some basic information about a plugin. Most importantly, this structure contains a pointer to the function that registers the pass. Let’s add this to our source.
- In the RegisterCB() function, we register a Lambda function that is called when a pass pipeline string is parsed. If the name of the pass is ppprofiler, then we add our pass to the module pass manager. These callbacks will be expanded upon in the next section:
void RegisterCB(PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, ModulePassManager &MPM,
ArrayRef) {
if (Name == “ppprofiler”) {
MPM.addPass(PPProfilerIRPass());
return true;
}
return false;
});
}
- The getPPProfilerPluginInfo() function is called when the plugin is statically linked. It returns some basic information about the plugin:
llvm::PassPluginLibraryInfo getPPProfilerPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, “PPProfiler”, “v0.1”,
RegisterCB};
}
- Finally, if the plugin is dynamically linked, then the llvmGetPassPluginInfo() function is called when the plugin is loaded. However, when linking this code statically into a tool, you might end up with linker errors because that function could be defined in several source files. The solution is to guard the function with a macro:
ifndef LLVM_PPPROFILER_LINK_INTO_TOOLS
extern “C” LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return getPPProfilerPluginInfo();
}
endif
With that, we’ve implemented the pass plugin. Before we look at how to use the new plugin, let’s examine what needs to be changed if we want to add the pass plugin to the LLVM source tree.