You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LLVM Pass开发:如何获取函数实参在.ll文件中的对应名称?

解决LLVM Pass中获取函数调用实参名称的问题

首先得指出你代码里的核心误区:你现在遍历的是被调用函数的形参(fct->arg_begin()),而不是Call/Invoke指令里传递的实参。这就是为什么你拿到的是空字符串——被调用函数的形参可能没有被命名,而你在.ll文件里看到的@_ZN2xi9spawnable2f2E是调用时传递的第一个实参(一个全局变量),和被调用函数的形参完全是两回事。

正确的做法:遍历CallInst/InvokeInst的操作数

CallInst和InvokeInst的操作数列表里,第一个元素是被调用的函数(或函数指针),从第二个元素开始才是传递的实参。你需要遍历这些操作数,然后根据操作数的类型来获取对应的名称:

  • 如果实参是GlobalValue(比如全局变量、函数),可以直接用getName()拿到它的符号名;
  • 如果实参是Instruction(比如寄存器值,像%1、%9),它的名字可能是空,除非IR里显式命名或者有调试信息;
  • 如果是常量(比如i1 zeroext %9里的%9是寄存器,而如果是i1 1这种常量,就直接输出常量值)。

修改后的代码示例

这里给你调整一下原有的代码片段,实现正确获取实参名称的逻辑:

#include "llvm/IR/CallSite.h"

virtual bool runOnFunction(Function &F) {
    LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
    std::string Name = demangle(F.getName());
    outs() << "Function " << *(F.getFunctionType()) << " " << Name << " {\n";

    for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i)
        BlocksInLoop(*i, 0);

    for (Function::iterator b = F.begin(), be = F.end(); b != be; ++b) {
        for (BasicBlock::iterator i = b->begin(), ie = b->end(); i != ie; ++i) {
            CallSite CS(&*i);
            if (!CS) continue;

            if (CS.getInstruction()->getMetadata("seen"))
                continue;

            Function *fct = CS.getCalledFunction();
            if (!fct) {
                // 处理函数指针间接调用的情况,可根据需求扩展逻辑
                continue;
            }

            outs() << "Call " << *(fct->getFunctionType()) << " " << demangle(fct->getName()) << "\n";
            outs() << "Arguments:\n";

            // 遍历所有实参(CallSite自动跳过被调用函数的操作数)
            for (CallSite::arg_iterator argIt = CS.arg_begin(), argEnd = CS.arg_end(); argIt != argEnd; ++argIt) {
                Value *Arg = *argIt;
                outs() << "  ";
                // 输出实参类型
                Arg->getType()->print(outs());
                outs() << " ";

                if (auto *GV = dyn_cast<GlobalValue>(Arg)) {
                    // 全局变量/函数,输出符号名
                    outs() << "@" << GV->getName();
                } else if (auto *Inst = dyn_cast<Instruction>(Arg)) {
                    // 寄存器类型实参,输出名字(若存在)
                    if (!Inst->getName().empty()) {
                        outs() << "%" << Inst->getName();
                    } else {
                        // 无名字时,LLVM会自动显示为%<编号>,直接打印Value即可
                        Arg->print(outs());
                    }
                } else if (Arg->isConstant()) {
                    // 常量类型实参,直接输出值
                    Arg->print(outs());
                } else {
                    // 其他类型Value,直接打印
                    Arg->print(outs());
                }
                outs() << "\n";
            }
        }
    }

    outs() << "}\n";
    return false;
}

补充说明

  • 你看到的.ll文件里的nonnull是实参的属性,可以用CS.getArgAttributes(argIdx)来获取并打印这些属性;
  • 用CallSite可以统一处理CallInst和InvokeInst,避免重复写判断逻辑;
  • 对于无名字的寄存器值,直接调用Value::print(outs())就能得到和.ll文件一致的%N格式输出。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:49:42