关于llvm::CallBase::getArgOperand()与getOperand()的功能及版本疑问
LLVM中
getArgOperand与getOperand的区别及代码解析 问题背景
我在学习LLVM核心库时,了解到getArgOperand(i)会以llvm::Value*形式返回可调用指令的第i个操作数,但不清楚getOperand()的用途,感觉两者功能相似,却没能在官方文档中找到详细的区分说明。
以下是一段提取cudaMalloc(**void, int)第一个参数的代码:
//This is used for extracting the first argument of cudaMalloc(**void, int) Value *MemAllocInfo::getTarget() { Value *ans = Alloc->getArgOperand(0); if (isa<LoadInst>(ans)) ans = dyn_cast<LoadInst>(ans)->getOperand(0); if (isa<BitCastInst>(ans)) ans = dyn_cast<BitCastInst>(ans)->getOperand(0); return ans; }
我的疑问如下:
getArgOperand()与getOperand()的功能是否等价?- 代码中的两个
if语句作用是什么?
补充测试信息
我编写了一个函数Pass及demo.ll代码:
Pass代码:
llvm::PreservedAnalyses CountIRPass::run( llvm::Function& F,llvm::FunctionAnalysisManager& AM){ for(llvm::BasicBlock& BB:F){ for(llvm::Instruction& I:BB){ if(llvm::isa<llvm::CallInst>(I)){ llvm::CallInst& ci=llvm::cast<llvm::CallInst>(I); llvm::errs() << ci.getNumOperands() << '\n'; llvm::errs() << ci.getArgOperand(0) << '\n'; llvm::errs() << ci.getOperand(0) << '\n'; llvm::errs() << *(ci.getArgOperand(0)) << '\n'; } } } return llvm::PreservedAnalyses::all(); }
demo.ll代码:
define internal i32 @special_func(i32 %a){ ret i32 0 } define dso_local i32 @main(i32 %a){ %b=call i32 @special_func(i32 %a) ret i32 %a }
运行后输出:
2 0x55be3770bf20 0x55be3770bf20 i32 %a
输出显示getArgOperand(0)与getOperand(0)的结果相同,均为i32 %a。我的LLVM版本为15.0.0,这是否是版本差异导致的?
问题解答
1. getArgOperand()与getOperand()的功能是否等价?
两者并不等价,核心区别在于操作数的范围,且行为受LLVM版本影响:
getOperand(i)是Instruction类的通用方法,返回指令的第i个操作数。对于可调用指令(CallInst/InvokeInst),其操作数集合在不同版本中有所变化:- LLVM 12及更早版本:操作数包含被调用的函数本身+函数参数,此时
getOperand(0)是被调用函数,getArgOperand(0)是第一个参数,两者完全不同。 - LLVM 13+版本:对于直接调用(如
call @func(%a)),被调用函数不再作为操作数存储,而是通过getCalledFunction()获取,此时getOperand(i)直接对应第i个参数,和getArgOperand(i)结果一致;但对于间接调用(如call i32 (%i32)* %fp(i32 %a)),getOperand(0)是函数指针%fp,getArgOperand(0)是第一个参数%a,两者仍有明确区别。
- LLVM 12及更早版本:操作数包含被调用的函数本身+函数参数,此时
getArgOperand(i)是CallBase类(CallInst和InvokeInst的父类)的专用方法,语义明确,仅返回函数的第i个参数,不受版本或调用类型影响。
你在LLVM 15.0.0中看到两者结果相同,正是因为测试用例是直接调用,符合LLVM 13+版本的行为。
2. 代码中的两个if语句作用是什么?
这两个if语句用于穿透中间包装指令,获取用户代码中原始的指针变量:
- 处理
LoadInst:当用户代码中写cudaMalloc(&ptr, size)时,LLVM IR会将其拆分为内存分配、加载、调用三步。此时getArgOperand(0)返回的是加载指令(LoadInst),需要通过getOperand(0)获取该加载指令对应的内存指针(即&ptr对应的IR值)。 - 处理
BitCastInst:如果传递给cudaMalloc的参数经过了位转换(比如将i32*转成void*),getArgOperand(0)返回的是位转换指令(BitCastInst),需要获取它的原始操作数,也就是转换前的指针变量。
通过这两步,最终能拿到用户代码中真正用来接收分配内存地址的原始变量,而非中间生成的包装指令。
内容的提问来源于stack exchange,提问作者Virux
相关产品推荐
相关产品推荐

