LLVM新Pass管理器自定义分析供Pass调用的问题排查
问题:LLVM新Pass管理器中自定义Function Analysis的崩溃与断言错误
我正在LLVM新Pass管理器中编写一个Function Analysis,希望让分析结果能被自定义Function Pass和Loop Pass获取。但因为新Pass管理器文档不足,遇到了问题。下面是最简示例代码,目标是让Function Analysis返回整数,供两个Pass打印。
#include <llvm/IR/BasicBlock.h> #include <llvm/IR/CFG.h> #include <llvm/IR/Constants.h> #include <llvm/IR/Function.h> #include <llvm/IR/Instruction.h> #include <llvm/IR/PassManager.h> #include <llvm/IR/ValueMap.h> #include <llvm/Pass.h> #include <llvm/Passes/PassBuilder.h> #include <llvm/Passes/PassPlugin.h> #include <llvm/Support/raw_ostream.h> using namespace llvm; using namespace std; class FunctionAnalysis : public AnalysisInfoMixin<FunctionAnalysis> { public: static AnalysisKey Key; struct Result { bool invalidate(Function& F, const PreservedAnalyses& PA, FunctionAnalysisManager::Invalidator& Inv) { outs() << "Invalidating...\n"; llvm_unreachable("idk what to do here tbh"); return false; } int dummy_data; }; Result run(Function& F, FunctionAnalysisManager& AM) { outs() << "Function analysis has been run...\n"; return {.dummy_data = 5}; } }; AnalysisKey FunctionAnalysis::Key; class TestFunctionPass : public PassInfoMixin<TestFunctionPass> { public: PreservedAnalyses run(Function& func, FunctionAnalysisManager& AM) { FunctionAnalysis::Result& func_analysis = AM.getResult<FunctionAnalysis>(func); outs() << "Result: " << func_analysis.dummy_data << '\n'; return PreservedAnalyses::all(); } }; class TestLoopPass : public PassInfoMixin<TestLoopPass> { public: PreservedAnalyses run(Loop& loop, LoopAnalysisManager& AM, LoopStandardAnalysisResults& res, LPMUpdater& updater) { const auto& FAMProxy = AM.getResult<FunctionAnalysisManagerLoopProxy>(loop, res); FunctionAnalysis::Result* func_analysis = FAMProxy.getCachedResult<FunctionAnalysis>(*loop.getLoopPreheader()->getParent()); outs() << "Result: " << func_analysis->dummy_data << '\n'; return PreservedAnalyses::all(); } }; //-------------------- Plugin Entry Point -------------------- extern "C" LLVM_ATTRIBUTE_WEAK __attribute__((visibility("default"))) PassPluginLibraryInfo llvmGetPassPluginInfo() { return {LLVM_PLUGIN_API_VERSION, "Test", "v0.1", [](PassBuilder& PB) { PB.registerPipelineParsingCallback( [](StringRef Name, FunctionPassManager& FPM, ArrayRef<PassBuilder::PipelineElement>) -> bool { if (Name == "test-function-pass") { FPM.addPass(TestFunctionPass()); return true; } return false; }); PB.registerPipelineParsingCallback( [](StringRef Name, LoopPassManager& LPM, ArrayRef<PassBuilder::PipelineElement>) -> bool { if (Name == "test-loop-pass") { LPM.addPass(TestLoopPass()); return true; } return false; }); PB.registerAnalysisRegistrationCallback([](FunctionAnalysisManager& FAM) { FAM.registerPass([]() { outs() << "\nDefinitely registered...\n\n"; return FunctionAnalysis(); }); }); }}; }
执行函数Pass时的崩溃信息
Stack dump: 0. Program arguments: opt -bugpoint-enable-legacy-pm=1 -load-pass-plugin=build/test.so -passes=test-function-pass,loop(test-loop-pass) build/tests/LICM/provided-pre.bc -o build/tests/BLAH.bc 1. Running pass "function(TestFunctionPass,loop(TestLoopPass))" on module "build/tests/LICM/provided-pre.bc" 2. Running pass "TestFunctionPass" on function "test" #0 0x00005575d2862c50 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/bin/opt+0x2d3ec50) #1 0x00005575d286022e SignalHandler(int) Signals.cpp:0:0 #2 0x00007fe28e3c8520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520) #3 0x00007fe28dfdf75d llvm::AnalysisManager<llvm::Function>::getResultImpl(llvm::AnalysisKey*, llvm::Function&) (build/test.so+0x42975d) #4 0x00007fe28dfdf658 llvm::AnalysisManager<llvm::Function>::getResultImpl(llvm::AnalysisKey*, llvm::Function&) (build/test.so+0x429658) #5 0x00007fe28dc7cd84 FunctionAnalysis::Result& llvm::AnalysisManager<llvm::Function>::getResult<FunctionAnalysis>(llvm::Function&) /usr/local/include/llvm/IR/PassManager.h:409:22 #6 0x00007fe28dc7c09e TestFunctionPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /workspaces/hw3/test.cpp:40:75 ... Segmentation fault (core dumped)
执行循环Pass时的断言错误
opt: /usr/local/include/llvm/IR/PassManager.h:406: ...: Assertion `AnalysisPasses.count(PassT::ID()) && "This analysis pass was not registered prior to being queried"' failed. PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. Stack dump: 0. Program arguments: opt -bugpoint-enable-legacy-pm=1 -load-pass-plugin=build/test.so -passes=test-loop-pass build/tests/LICM/provided-pre.bc -o build/tests/BLAH.bc 1. Running pass "function(loop(TestLoopPass))" on module "build/tests/LICM/provided-pre.bc" 2. Running pass "loop(TestLoopPass)" on function "test" #0 0x000055e009bfbc50 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/bin/opt+0x2d3ec50) #1 0x000055e009bf922e SignalHandler(int) Signals.cpp:0:0 #2 0x00007f2b38df7520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520) #3 0x00007f2b38e4b9fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76 #4 0x00007f2b38e4b9fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10 #5 0x00007f2b38e4b9fc pthread_kill ./nptl/pthread_kill.c:89:10 #6 0x00007f2b38df7476 gsignal ./signal/../sysdeps/posix/raise.c:27:6 #7 0x00007f2b38ddd7f3 abort ./stdlib/abort.c:81:7 #8 0x00007f2b38ddd71b _nl_load_domain ./intl/loadmsgcat.c:1177:9 #9 0x00007f2b38deee96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96) #10 0x00007f2b386abdef llvm::OuterAnalysisManagerProxy<llvm::AnalysisManager<llvm::Function>, llvm::Loop, llvm::LoopStandardAnalysisResults&>::Result& llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>::getResult<llvm::OuterAnalysisManagerProxy<llvm::AnalysisManager<llvm::Function>, llvm::Loop, llvm::LoopStandardAnalysisResults&>>(llvm::Loop&, llvm::LoopStandardAnalysisResults&) /usr/local/include/llvm/IR/PassManager.h:409:22 #11 0x00007f2b386ab14c TestLoopPass::run(llvm::Loop&, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>&, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&) /workspaces/hw3/test.cpp:50:72 ...
错误原因与修复方案
1. Analysis的invalidate方法错误
你的Result::invalidate方法中调用了llvm_unreachable,这会直接触发程序崩溃。正确的逻辑应该是判断当前分析是否被保留:如果分析的Key在PreservedAnalyses中,则不需要失效,返回false;否则返回true。修改后的方法:
bool invalidate(Function& F, const PreservedAnalyses& PA, FunctionAnalysisManager::Invalidator& Inv) { return !PA.preserved(FunctionAnalysis::Key); }
2. Loop Pass中获取分析结果的方式错误
getCachedResult只会返回已经缓存的分析结果,如果分析还没运行,会返回nullptr,直接访问会导致空指针崩溃。应该通过FAM代理获取FunctionAnalysisManager,再调用getResult确保分析被执行:
PreservedAnalyses run(Loop& loop, LoopAnalysisManager& AM, LoopStandardAnalysisResults& res, LPMUpdater& updater) { auto& FAMProxy = AM.getResult<FunctionAnalysisManagerLoopProxy>(loop, res); Function& F = *loop.getLoopPreheader()->getParent(); // 获取FAM并调用getResult确保分析运行 FunctionAnalysis::Result& func_analysis = FAMProxy.getManager().getResult<FunctionAnalysis>(F); outs() << "Result: " << func_analysis.dummy_data << '\n'; return PreservedAnalyses::all(); }
3. 命令行参数冲突
你使用了-bugpoint-enable-legacy-pm=1,这会强制启用旧Pass管理器,而你的代码是针对新Pass管理器编写的。去掉该参数,使用纯新PM的执行命令:
opt -load-pass-plugin=build/test.so -passes="test-function-pass,loop(test-loop-pass)" build/tests/LICM/provided-pre.bc -o build/tests/BLAH.bc
4. 确保分析代理的正确注册
虽然PassBuilder会自动处理大部分代理注册,但如果遇到Loop Pass无法获取FAM的情况,可以手动在PassBuilder回调中注册Loop到Function的分析代理:
PB.registerLoopAnalyses([](LoopAnalysisManager& LAM, FunctionAnalysisManager& FAM) { LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); }); });
修改后的完整代码
#include <llvm/IR/BasicBlock.h> #include <llvm/IR/CFG.h> #include <llvm/IR/Constants.h> #include <llvm/IR/Function.h> #include <llvm/IR/Instruction.h> #include <llvm/IR/PassManager.h> #include <llvm/IR/ValueMap.h> #include <llvm/Pass.h> #include <llvm/Passes/PassBuilder.h> #include <llvm/Passes/PassPlugin.h> #include <llvm/Support/raw_ostream.h> using namespace llvm; class FunctionAnalysis : public AnalysisInfoMixin<FunctionAnalysis> { public: static AnalysisKey Key; struct Result { bool invalidate(Function& F, const PreservedAnalyses& PA, FunctionAnalysisManager::Invalidator& Inv) { return !PA.preserved(FunctionAnalysis::Key); } int dummy_data; }; Result run(Function& F, FunctionAnalysisManager& AM) { outs() << "Function analysis has been run...\n"; return {.dummy_data = 5}; } }; AnalysisKey FunctionAnalysis::Key; class TestFunctionPass : public PassInfoMixin<TestFunctionPass> { public: PreservedAnalyses run(Function& func, FunctionAnalysisManager& AM) { FunctionAnalysis::Result& func_analysis = AM.getResult<FunctionAnalysis>(func); outs() << "Result from Function Pass: " << func_analysis.dummy_data << '\n'; return PreservedAnalyses::all(); } }; class TestLoopPass : public PassInfoMixin<TestLoopPass> { public: PreservedAnalyses run(Loop& loop, LoopAnalysisManager& AM, LoopStandardAnalysisResults& res, LPMUpdater& updater) { auto& FAMProxy = AM.getResult<FunctionAnalysisManagerLoopProxy>(loop, res); Function& F = *loop.getLoopPreheader()->getParent(); FunctionAnalysis::Result& func_analysis = FAMProxy.getManager().getResult<FunctionAnalysis>(F); outs() << "Result from Loop Pass: " << func_analysis.dummy_data << '\n'; return PreservedAnalyses::all(); } }; //-------------------- Plugin Entry Point -------------------- extern "C" LLVM_ATTRIBUTE_WEAK __attribute__((visibility("default"))) PassPluginLibraryInfo llvmGetPassPluginInfo() { return {LLVM_PLUGIN_API_VERSION, "Test", "v0.1", [](PassBuilder& PB) { PB.registerPipelineParsingCallback( [](StringRef Name, FunctionPassManager& FPM, ArrayRef<PassBuilder::PipelineElement>) -> bool { if (Name == "test-function-pass") { FPM.addPass(TestFunctionPass()); return true; } return false; }); PB.registerPipelineParsingCallback( [](StringRef Name, LoopPassManager& LPM, ArrayRef<PassBuilder::PipelineElement>) -> bool { if (Name == "test-loop-pass") { LPM.addPass(TestLoopPass()); return true; } return false; }); PB.registerAnalysisRegistrationCallback([](FunctionAnalysisManager& FAM) { FAM.registerPass([]() { return FunctionAnalysis(); }); }); // 手动注册Loop到Function的分析代理(可选,部分LLVM版本可能需要) PB.registerLoopAnalyses([](LoopAnalysisManager& LAM, FunctionAnalysisManager& FAM) { LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); }); }); }}; }
内容的提问来源于stack exchange,提问作者Otto von Bisquick
相关产品推荐
相关产品推荐

