如何使用LLVM C++ API实现函数指针?含IR代码生成需求
解决函数指针的LLVM C++ API实现问题
我来帮你搞定这个函数指针的LLVM API实现问题!你已经找对了对应的IR指令,现在只需要用LLVM IRBuilder的对应API把这些指令生成出来就行,我一步步给你拆解:
步骤1:分配函数指针的内存空间
要生成%p = alloca i32 (i8*)*, align 8,首先得明确函数指针的类型:你已经定义了putsType是i32(i8*)类型的函数,那函数指针的类型就是这个函数类型的指针,也就是putsType->getPointerTo()。然后用IRBuilder::CreateAlloca分配这个类型的变量,指定64位系统常用的8字节对齐:
// 获取函数指针的类型:指向i32(i8*)的指针 llvm::Type* funcPtrType = putsType->getPointerTo(); // 分配函数指针变量p,对齐8字节 llvm::Value* p = builder.CreateAlloca(funcPtrType, nullptr, "p");
步骤2:将puts函数地址存储到指针变量
对应IR里的store i32 (i8*)* @puts, i32 (i8*)** %p, align 8,直接用IRBuilder::CreateStore把putFunction(也就是@puts的常量地址)存储到p指向的内存即可:
// 将puts的地址存储到p变量中 builder.CreateStore(putFunction, p, /*对齐值*/ 8);
步骤3:通过函数指针调用puts
原来的代码是直接调用putFunction,现在需要先加载p里的函数指针,再用这个指针发起调用:
// 加载p变量中的函数指针 llvm::Value* loadedFuncPtr = builder.CreateLoad(funcPtrType, p, "loaded_func_ptr"); // 通过函数指针调用puts,传入helloWorld参数 builder.CreateCall(loadedFuncPtr, helloWorld);
完整修改后的代码
把这些步骤整合到你的代码里,完整可运行的代码如下:
#include "llvm/Support/raw_ostream.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Function.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Type.h" #include "llvm/IR/Constants.h" int main() { llvm::LLVMContext context; llvm::IRBuilder<> builder(context); llvm::Module *module = new llvm::Module("top", context); // 定义main函数的类型:无参数,返回i32 llvm::FunctionType *functionType = llvm::FunctionType::get(builder.getInt32Ty(), false); llvm::Function *mainFunction = llvm::Function::Create(functionType, llvm::Function::ExternalLinkage, "main", module); llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunction); builder.SetInsertPoint(entry); // 创建字符串常量 llvm::Value *helloWorld = builder.CreateGlobalStringPtr("Hello World!\n"); // 定义puts函数的类型:参数i8*,返回i32 std::vector<llvm::Type *> putArgs; putArgs.push_back(builder.getInt8Ty()->getPointerTo()); llvm::ArrayRef<llvm::Type *> argsRef(putArgs); llvm::FunctionType *putsType = llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false); llvm::Constant *putFunction = module->getOrInsertFunction("puts", putsType); // --- 新增的函数指针实现代码 --- // 1. 分配函数指针变量p llvm::Type* funcPtrType = putsType->getPointerTo(); llvm::Value* p = builder.CreateAlloca(funcPtrType, nullptr, "p"); // 2. 将puts地址存储到p中 builder.CreateStore(putFunction, p, 8); // 3. 加载函数指针并调用 llvm::Value* loadedFuncPtr = builder.CreateLoad(funcPtrType, p, "loaded_func_ptr"); builder.CreateCall(loadedFuncPtr, helloWorld); // --- 新增代码结束 --- builder.CreateRet(llvm::ConstantInt::get(builder.getInt32Ty(), 0)); module->print(llvm::errs(), nullptr); // 释放模块内存(好习惯,程序退出时也会自动回收) delete module; return 0; }
生成的IR验证
运行这段代码后,生成的IR会包含你需要的核心部分:
%p = alloca i32 (i8*)*, align 8 store i32 (i8*)* @puts, i32 (i8*)** %p, align 8 %loaded_func_ptr = load i32 (i8*)*, i32 (i8*)** %p, align 8 %call = call i32 %loaded_func_ptr(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @0, i32 0, i32 0))
这和你期望的C代码对应的IR完全一致。
另外补充一句:你推测的llvm::Constant* putFunction确实是@puts的地址常量,也就是函数指针的常量形式,直接存储到分配的指针变量里就完成了C代码中p = puts;的赋值操作。
内容的提问来源于stack exchange,提问作者user2269707
相关产品推荐
相关产品推荐

