llvm ir中函数调用由callinst指令表示,需用dyn_cast判断;通过getcalledfunction()获取被调用函数,arg_size()获取参数个数,getargoperand(i)获取各参数值。

怎么判断一条指令是不是函数调用
LLVM IR 中,函数调用由 CallInst 指令表示,不是所有带括号的都算——比如 InvokeInst 是带异常分发的调用,CallBrInst 是带分支的调用,它们都需单独处理。最常见且默认要捕获的是 CallInst。
在 FunctionPass::runOnFunction 里遍历每个基本块、再遍历每条指令时,用 dyn_cast<callinst></callinst> 判断即可:
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (CallInst *CI = dyn_cast<callinst>(&I)) {
// 这就是一次函数调用
}
}
}</callinst>
注意:不能只靠 I.getOpcode() == Instruction::Call,因为 CallInst 是子类,某些优化后可能被转成 InvokeInst 或内联掉;而 dyn_cast 安全且语义准确。
如何从 CallInst 提取被调用函数和参数列表
CallInst 提供两个关键接口:getCalledFunction() 和 getArgOperand(i)。前者返回 Function*(仅当是直接调用时非空),后者按索引取参数值(Value* 类型)。
- 如果
getCalledFunction()返回nullptr,说明是间接调用(比如函数指针或虚函数),此时应检查getCalledValue()并尝试dyn_cast<function>(...)</function> - 参数数量用
CI->arg_size()获取,不要硬写CI->getNumOperands()—— 因为操作数包含被调用值本身,实际参数从第 0 个 operand 开始,但总数是arg_size() - 参数值可能是常量、寄存器(
Argument)、全局变量,甚至另一个CallInst,需根据用途决定是否递归分析
示例:打印调用名和参数个数
if (Function *Callee = CI->getCalledFunction()) {
errs() getName() arg_size() getCalledValue()
<h3>为什么 getOperand(0) 不等于第一个参数</h3>
<p>这是新手最容易踩的坑。<code>CallInst</code> 的 operand 列表结构是:<code>[callee, arg0, arg1, ..., argN]</code>,所以 <code>CI->getOperand(0)</code> 是被调用目标(函数或指针),而第一个真正参数是 <code>CI->getOperand(1)</code>。</p>
<p>正确做法永远用 <code>CI->getArgOperand(i)</code>(i 从 0 开始),它自动跳过 callee operand。否则:</p>
- 对直接调用,
getOperand(0)是Function*,getArgOperand(0)才是第一个实参 - 对间接调用,
getOperand(0)是函数指针表达式(如load @fp),getArgOperand(0)仍指向第一个参数 - 一旦涉及
invoke或callbr,operand 布局更复杂,getArgOperand是唯一可移植方式
参数类型和调试信息怎么对应源码位置
CallInst 本身不存源码行号,但可通过 CI->getDebugLoc() 获取 DebugLoc,再调用 .getLine() / .getCol() 提取位置信息(前提是编译时加了 -g 且未被优化抹除)。
参数的类型信息得从 Value* 向上追溯:
- 若参数是
Argument(来自其他函数入口),可用arg->getType() - 若参数是常量(
ConstantInt等),getType()直接有效 - 若参数是某条指令的结果(如
AllocaInst),需注意其 type 是指针类型,真实数据类型要看 pointee type:cast<pointertype>(val->getType())->getElementType()</pointertype>
没有 debug info 时,仅靠 IR 无法 100% 还原形参名,但类型和调用序号是确定的。别指望 CallInst 自带参数名——IR 层面只有位置,没有符号名。











