Basic
OpcodeCounter 是一个 analysis pass, 可以统计输入文件对应的 module 中每个函数使用的各个 LLVM IR opcode 的数量。
为了可视化 OpcodeCounter 的分析结果,引入了一个专门输出结果的 transformation pass: OpcodeCounterPrinter。 按照惯例, 这样的 printing pass 命令行选项为 print<analysis-pass-name>。 所以这里的printing pass 命令行选项为print<OpcodeCounter>
OpcodeCounter 类
作为一个 analysis pass ,需要继承 AnalysisInfoMixin
struct OpcodeCounter : public llvm::AnalysisInfoMixin<OpcodeCounter> {
...
};
属性
private
static llvm::AnalysisKey Key; | 类似于 ID, 可以认证这个特殊的 analysis pass 类型 |
注意这个 Key 要在 cpp 文件中初始化。 如果不初始化, 则会出现类似以下报错:
LLVM ERROR: Could not load library './lib/libOpcodeCounter.dylib': dlopen(./lib/libOpcodeCounter.dylib, 0x0009): symbol not found in flat namespace '__ZN13OpcodeCounter3KeyE'
generateOpcodeMap()
用于统计一个函数的 opcode 数量的辅助函数。
OpcodeCounter::Result OpcodeCounter::generateOpcodeMap(llvm::Function &Func) {
OpcodeCounter::Result OpcodeMap;
for (auto &BB : Func) {
for (auto &Inst : BB) {
StringRef Name = Inst.getOpcodeName();
if (OpcodeMap.find(Name) == OpcodeMap.end()) {
OpcodeMap[Inst.getOpcodeName()] = 1;
} else {
OpcodeMap[Inst.getOpcodeName()]++;
}
}
}
return OpcodeMap;
}
返回值类型实质上是 llvm::StringMap<unsigned>, 即字符串 string 到 unsigned 映射。
这个函数的逻辑还是很清楚的。 值得注意的是对 Function 直接做范围循环就可以遍历所有BasicBlock, 对 BasicBlock 直接做范围循环就可以遍历所有 Instrument。
run()
pass 的入口函数, 这里将 OpcodeCounter 实现为了一个 Function Analysis。
OpcodeCounter::Result OpcodeCounter::run(llvm::Function &Func,
llvm::FunctionAnalysisManager &) {
return generateOpcodeMap(Func);
}
OpcodeCounterPrinter 类
作为一个 transformer pass 继承了 PassInfoMixin
class OpcodeCounterPrinter : public llvm::PassInfoMixin<OpcodeCounterPrinter> {
...
};
属性
private
llvm::raw_ostream &OS; 对外输入流的引用
run()
OpcodeCounterPrinter 同样实现为了 function transformer pass。
PreservedAnalyses OpcodeCounterPrinter::run(Function &Func,
FunctionAnalysisManager &FAM) {
auto &OpcodeMap = FAM.getResult<OpcodeCounter>(Func);
// In the legacy PM, the following string is printed automatically by the
// pass manager. For the sake of consistency, we're adding this here so that
// it's also printed when using the new PM.
OS << "Printing analysis 'OpcodeCounter Pass' for function '"
<< Func.getName() << "':\n";
printOpcodeCounterResult(OS, OpcodeMap);
return PreservedAnalyses::all();
}
使用 FunctionAnalysisManager 的 getResult 方法获取 analysis 结果。之后更是输出结果逻辑, 比较简单, 不分析了。
注册 pass plugin
使用了与 HelloWorld 一致的注册写法, 先写一个辅助的 cpp 函数处理注册时逻辑, 再实现 llvmGetPassPluginInfo 这个纯 C 函数接口。
lvm::PassPluginLibraryInfo getOpcodeCounterPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "OpcodeCounter", LLVM_VERSION_STRING,
// 回调函数
}
};
}
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return getOpcodeCounterPluginInfo();
}
getOpcodeCounterPluginInfo()
llvm::PassPluginLibraryInfo getOpcodeCounterPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "OpcodeCounter", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
// #1 对应命令行中使用 "opt -passes=print<opcode-counter>" 的情况
// 注册为 registerPipelineParsingCallback 的回调函数
PB.registerPipelineParsingCallback(
[&](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "print<opcode-counter>") {
FPM.addPass(OpcodeCounterPrinter(llvm::errs()));
return true;
}
return false;
});
// #2 对应命令行中使用 "-O{1|2|3|s}" pipeline 的情况
// 注册为 registerVectorizerStartEPCallback 的回调函数
// 这意着 OpcodeCounterPrinter 会在使用 vectoriser (矢量化) 时被调用
// ( 也就是在命令行中调用 -O{1|2|3|s} 的情况下 )
PB.registerVectorizerStartEPCallback(
[](llvm::FunctionPassManager &PM,
llvm::OptimizationLevel Level) {
PM.addPass(OpcodeCounterPrinter(llvm::errs()));
});
// #3 向 FunctionAnalysisManager 注册 OpcodeCounter 的构造函数
// 这样向 FunctionAnalysisManager 调用 FAM.getResult<OpcodeCounter>(Func)时才能正确处理
PB.registerAnalysisRegistrationCallback(
[](FunctionAnalysisManager &FAM) {
FAM.registerPass([&] { return OpcodeCounter(); });
});
}
};
}
相关类
AnalysisInfoMixin
/// 继承了PassInfoMixin, 使用 CRTP 混合技术
/// 为 analysis pass 提供相比普通 pass 额外的必要的 API: ID()
template <typename DerivedT>
struct AnalysisInfoMixin : PassInfoMixin<DerivedT> {
/// 为该 analysis type 提供独立的 ID
/// 该 ID 是一个指针类型
/// 要求子类提供一个静态的 AnalysisKey 名为 Key
static AnalysisKey *ID() {
static_assert(std::is_base_of<AnalysisInfoMixin, DerivedT>::value,
"Must pass the derived type as the template argument!");
return &DerivedT::Key;
}
};
MISC
vectoriser 矢量化 from ChatGPT
在计算机编程领域,"vectorization"(翻译为中文可能是"矢量化")通常是指使用向量(或数组)操作来替代标量操作,以便能够在并行处理的硬件上获得更好的性能。这在许多编程语言和编译器中都是一个重要的优化技术。
"编译中的vectorizer"指的是编译器中的一个优化器,其主要任务是将程序中的标量操作转换为向量操作,以充分利用硬件的并行性。这种优化可以在现代CPU和GPU等硬件上提高程序的执行效率。
例如,在循环中执行的一系列标量操作可能会被编译器自动转换为使用SIMD(Single Instruction, Multiple Data)指令执行的向量操作。这样可以同时处理多个数据,提高计算效率。
不同的编程语言和编译器可能有不同的实现方式和优化策略,但总体思想是通过矢量化操作来提高程序性能。