You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

LLVM新手编写Pass遇问题:循环内函数调用被重复打印

解决LLVM Pass中循环内函数调用重复打印的问题

嘿,作为刚踩过LLVM Pass各种坑的过来人,我太懂你这种函数调用被重复打印的烦恼了!结合你给出的代码片段来看,这个问题大概率是循环嵌套导致的BasicBlock重复遍历——LLVM里父循环的getBlocks()会包含所有子循环的BasicBlock,当你递归处理父循环和子循环时,同一个函数调用所在的块会被遍历两次,自然就打印两次了。

下面给你两个实用的解决思路,直接就能套进你的代码里:

思路1:标记已处理的BasicBlock

用一个集合记录已经检查过的块,每次处理循环时跳过已标记的块,从根源上避免重复遍历。

修改你的BlocksInLoop函数,加上LLVM自带的高效集合DenseSet作为参数:

#include "llvm/ADT/DenseSet.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Instructions.h"

void BlocksInLoop(Loop *L, unsigned nlvl, DenseSet<BasicBlock*> &ProcessedBlocks) {
  errs() << "Loop level " << nlvl << " {\n";
  BasicBlock* h = L->getHeader();
  
  // 遍历当前循环的所有BasicBlock
  for (auto *BB : L->getBlocks()) {
    // 如果块已经被处理过,直接跳过
    if (ProcessedBlocks.count(BB)) continue;
    ProcessedBlocks.insert(BB);

    // 检查当前块中的函数调用
    for (auto &Inst : *BB) {
      if (auto *Call = dyn_cast<CallBase>(&Inst)) {
        errs() << "Found function call: " << Call->getCalledFunction()->getName() 
               << " in loop level " << nlvl << "\n";
      }
    }
  }

  // 递归处理子循环
  for (auto *SubLoop : L->getSubLoops()) {
    BlocksInLoop(SubLoop, nlvl + 1, ProcessedBlocks);
  }
  errs() << "}\n";
}

然后在Pass的runOnFunction方法里初始化这个集合:

bool runOnFunction(Function &F) override {
  LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
  DenseSet<BasicBlock*> ProcessedBlocks;

  for (auto *L : LI) {
    BlocksInLoop(L, 1, ProcessedBlocks);
  }
  return false;
}

思路2:只处理循环独有的BasicBlock

如果你更关心函数调用所在的最内层循环,可以让父循环跳过属于子循环的块,把这些块交给子循环去处理。

修改后的BlocksInLoop函数:

void BlocksInLoop(Loop *L, unsigned nlvl) {
  errs() << "Loop level " << nlvl << " {\n";
  LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
  BasicBlock* h = L->getHeader();
  
  // 遍历当前循环的所有BasicBlock
  for (auto *BB : L->getBlocks()) {
    // 检查当前块是否属于当前循环的子循环
    bool isInSubLoop = false;
    for (auto *SubLoop : L->getSubLoops()) {
      if (SubLoop->contains(BB)) {
        isInSubLoop = true;
        break;
      }
    }
    // 如果属于子循环,跳过,交给子循环处理
    if (isInSubLoop) continue;

    // 检查当前块中的函数调用
    for (auto &Inst : *BB) {
      if (auto *Call = dyn_cast<CallBase>(&Inst)) {
        errs() << "Found function call: " << Call->getCalledFunction()->getName() 
               << " in loop level " << nlvl << "\n";
      }
    }
  }

  // 递归处理子循环
  for (auto *SubLoop : L->getSubLoops()) {
    BlocksInLoop(SubLoop, nlvl + 1);
  }
  errs() << "}\n";
}

额外提醒

  • 记得在Pass的初始化部分声明依赖,比如加上INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass),确保能正确获取LoopInfo分析结果。
  • 如果你的Pass还用到了ScalarEvolution,也要记得在getAnalysisUsage里添加对应的分析依赖:
void getAnalysisUsage(AnalysisUsage &AU) const override {
  AU.addRequired<LoopInfoWrapperPass>();
  AU.addRequired<ScalarEvolutionWrapperPass>();
  AU.setPreservesAll();
}

内容的提问来源于stack exchange,提问作者guillau4

火山引擎 最新活动