LLVM修改计算逻辑时出现i64类型转换无效错误求助
问题描述
尝试使用LLVM的Create系列API和replaceAllUsesWith方法,将(A+B+C)的计算逻辑修改为(A*B)-C,编写的ReplaceInst.cpp代码如下:
// 遍历模块中的指令 void TraverseModule(void) { for( llvm::Module::iterator ModIter = TheModule->begin(); ModIter != TheModule->end(); ++ModIter ) { llvm::Function* Func = llvm::cast<llvm::Function>(ModIter); for( llvm::Function::iterator FuncIter = Func->begin(); FuncIter != Func->end(); ++FuncIter ) { llvm::BasicBlock* BB = llvm::cast<llvm::BasicBlock>(FuncIter); std::vector< llvm::Instruction* > AddInsts; for( llvm::BasicBlock::iterator BBIter = BB->begin(); BBIter != BB->end(); ++BBIter ) { llvm::Instruction* AddInst = llvm::cast<llvm::Instruction>(BBIter); // if( Inst->isBinaryOp() ) {} if( AddInst->getOpcode() == llvm::Instruction::Add ) { if (AddInst->getNumOperands() != 2) { std::cerr << "Error: Add instruction does not have exactly 2 operands." << std::endl; continue; } llvm::LLVMContext& context = AddInst->getContext(); llvm::Instruction* MulInst = llvm::BinaryOperator::Create( llvm::Instruction::Mul, /* 操作码 */ AddInst->getOperand(0), /* 操作数1 */ AddInst->getOperand(1), /* 操作数2 */ "multmp", /* 名称 */ AddInst /* 插入位置 */ ); // 替换符号扩展(sext) llvm::Instruction* SextInst = llvm::CastInst::Create( llvm::Instruction::SExt, /* 操作码 */ MulInst, /* 源操作数 */ llvm::Type::getInt64Ty(context), /* 目标类型 */ "sextmp", /* 名称 */ AddInst /* 插入位置 */ ); llvm::Value* CPointer = AddInst->getOperand(1); // 替换加载指令 llvm::Instruction* LoadInst = new llvm::LoadInst( llvm::Type::getInt64Ty(context), CPointer, "loadtmp", AddInst ); // 替换减法指令 llvm::Instruction* SubInst = llvm::BinaryOperator::Create( llvm::Instruction::Sub, SextInst, LoadInst, "subtmp", AddInst ); AddInst->replaceAllUsesWith( SubInst ); AddInsts.push_back( AddInst ); } } for( int i=0, Size=AddInsts.size(); i<Size; ++i ) AddInsts[i]->eraseFromParent(); } } }
测试代码Test.c如下:
#include <stdlib.h> #include <stdio.h> int Add(int*arr,int*brr,int*crr,size_t n) { crr[n]= arr[n]+brr[n]+n; return crr[n]; } int main() { int N=0; printf("Input N (0~5)\n"); scanf("%d",&N); int crr[5]={0,0,0,0,0}; int arr[5]={1,2,3,4,5}; int brr[5]={2,3,4,5,6}; int result = Add(arr,brr,crr,N); printf("...(%d MUL %d) SUB %d = %d\n" ,arr[N],brr[N],N,result); }
对比修改前后的.ll文件,结果符合预期,但编译时出现错误:"invalid cast opcode for cast from 'i64' to 'i64'"。尝试移除符号扩展(sext)相关代码,但问题仍未解决。请问是否可以省略符号扩展部分,或者有更优化的解决方案?
解决方案
1. 符号扩展代码的问题与处理
错误invalid cast opcode for cast from 'i64' to 'i64'是因为你尝试对已经是i64类型的值执行符号扩展,属于无意义的冗余操作,LLVM会直接报错。
你的测试代码中,arr、brr是int*类型(多数平台下int对应i32),但你直接将乘法结果强制转为i64;而n是size_t类型,编译后可能本身就是i64,此时执行SExt就会出现类型重复的问题。
结论:可以完全省略符号扩展代码,除非你明确需要将i32类型的值扩展为i64。如果需要处理类型统一,应该先判断源操作数的类型,再决定是否添加扩展指令。
2. 加载指令的错误逻辑
你的代码中直接对AddInst->getOperand(1)执行LoadInst,这是错误的:
- 测试代码中
arr[n]+brr[n]+n的加法操作数都是值类型(i32或i64),不是指针,直接加载会导致非法内存访问或类型错误。 - 你要替换的是
A+B+C为(A*B)-C,这里的C是原表达式中的第三个操作数,而非第二个操作数的指针。
3. 优化后的核心代码思路
正确的逻辑应该是识别连续的加法链((A+B)+C),将整个链替换为(A*B)-C,而非替换每一个加法指令:
if( AddInst->getOpcode() == llvm::Instruction::Add ) { // 检查当前加法是否是另一个加法的操作数,即是否属于(A+B)+C结构中的内层加法 if (auto *OuterAdd = dyn_cast<llvm::BinaryOperator>(AddInst->user_back())) { if (OuterAdd->getOpcode() == llvm::Instruction::Add) { // 提取A、B、C:AddInst是A+B,OuterAdd的另一个操作数是C llvm::Value *A = AddInst->getOperand(0); llvm::Value *B = AddInst->getOperand(1); llvm::Value *C = OuterAdd->getOperand(0) == AddInst ? OuterAdd->getOperand(1) : OuterAdd->getOperand(0); llvm::LLVMContext& context = AddInst->getContext(); // 创建乘法指令:A*B auto *MulInst = llvm::BinaryOperator::CreateMul(A, B, "multmp", OuterAdd); // 创建减法指令:(A*B)-C auto *SubInst = llvm::BinaryOperator::CreateSub(MulInst, C, "subtmp", OuterAdd); // 替换外层加法的所有用途 OuterAdd->replaceAllUsesWith(SubInst); // 标记要删除的冗余指令 AddInsts.push_back(OuterAdd); AddInsts.push_back(AddInst); } } }
4. 其他注意事项
- 使用
dyn_cast而非cast做类型转换,避免类型不匹配时程序崩溃。 - 收集要删除的指令时,确保所有被替换的指令都被标记,避免悬空指针。
- 若操作数类型不统一,需先执行类型转换(如
zext无符号扩展或sext有符号扩展),确保乘法和减法操作的类型一致。
内容的提问来源于stack exchange,提问作者ummmsso
相关产品推荐
相关产品推荐

