LLVM类型合法化Pass实现:如何修改<6 x i3>加法指令的值与类型
实现LLVM向量类型拓宽的合法化Pass
针对你要做的<6 x i3>到<8 x i3>的类型拓宽需求,下面一步步拆解怎么在LLVM Pass里修改Value、替换指令:
核心思路:LLVM中修改Value的本质
LLVM的IR指令是不可变对象,没法直接修改现有指令的操作数或类型。正确的做法是:
- 创建符合新类型要求的新指令/Value
- 将原指令的所有使用点替换为新的Value
- 标记原指令待删除(避免迭代时出错)
具体实现步骤(结合你的代码片段)
1. 识别目标Binary指令
首先要过滤出你需要处理的add指令,且操作数类型是<6 x i3>:
for (auto &block : function) { std::vector<BinaryOperator *> binInstsToErase; for (auto &inst : block) { BinaryOperator *binaryOpInst = dyn_cast<BinaryOperator>(&inst); if (!binaryOpInst || binaryOpInst->getOpcode() != Instruction::Add) { continue; // 只处理add指令 } // 检查操作数类型是否是<6 x i3> VectorType *origVecTy = dyn_cast<VectorType>(binaryOpInst->getType()); if (!origVecTy || origVecTy->getNumElements() != 6 || origVecTy->getElementType()->getBitWidth() != 3) { continue; } Value *vector1 = binaryOpInst->getOperand(0); Value *vector2 = binaryOpInst->getOperand(1); // 接下来处理类型拓宽 // ... } // 批量删除标记的原指令(放在block遍历末尾) for (auto *inst : binInstsToErase) { inst->eraseFromParent(); } }
2. 创建目标拓宽类型<8 x i3>
用LLVM的VectorType工具类生成目标类型:
LLVMContext &ctx = function.getContext(); Type *elemTy = Type::getInt3Ty(ctx); VectorType *targetVecTy = VectorType::get(elemTy, 8);
3. 生成向量拓宽的指令
要把<6 x i3>的向量扩展为<8 x i3>,我们可以通过插入两个i3类型的0元素来实现,这里提供两种方式:
方法1:循环插入0元素(直观易懂)
// 准备填充用的0常量 Constant *zero = ConstantInt::get(elemTy, 0); // 拓宽vector1 Value *widenedVec1 = vector1; for (unsigned i = 6; i < 8; ++i) { widenedVec1 = InsertElementInst::Create(widenedVec1, zero, ConstantInt::get(Type::getInt32Ty(ctx), i), "widened.vec1", &inst); } // 同理拓宽vector2 Value *widenedVec2 = vector2; for (unsigned i = 6; i < 8; ++i) { widenedVec2 = InsertElementInst::Create(widenedVec2, zero, ConstantInt::get(Type::getInt32Ty(ctx), i), "widened.vec2", &inst); }
方法2:用ShuffleVector一次性完成(更简洁高效)
// 创建一个mask:原6个元素留前6位,后两位用0填充 SmallVector<int, 8> mask; for (int i = 0; i < 6; ++i) mask.push_back(i); mask.push_back(-1); // -1表示取第二个操作数的对应位置(这里是全0向量) mask.push_back(-1); Constant *zeroVec = ConstantVector::getSplat(8, ConstantInt::get(elemTy, 0)); Value *widenedVec1 = ShuffleVectorInst::Create(vector1, zeroVec, ConstantInt::getVector(Type::getInt32Ty(ctx), mask), "widened.vec1", &inst); Value *widenedVec2 = ShuffleVectorInst::Create(vector2, zeroVec, ConstantInt::getVector(Type::getInt32Ty(ctx), mask), "widened.vec2", &inst);
4. 创建新的add指令并替换原指令
用拓宽后的向量创建新的add指令,然后替换原指令的所有使用:
// 创建新的add指令,插入到原指令之前 BinaryOperator *newAddInst = BinaryOperator::CreateAdd(widenedVec1, widenedVec2, "widened.add", binaryOpInst); // 替换原指令的所有使用点 binaryOpInst->replaceAllUsesWith(newAddInst); // 标记原指令待删除 binInstsToErase.push_back(binaryOpInst);
关键注意事项
- 绝对不要在遍历指令的过程中直接删除指令,一定要先收集到待删除列表,遍历结束后再统一删除,否则会破坏迭代器的有效性。
- 如果后续还有其他处理逻辑(比如对拓宽后的向量做额外操作),可以在创建
newAddInst之后继续添加。 - 所有类型和常量都要基于同一个
LLVMContext创建,避免出现上下文不匹配的错误。
内容的提问来源于stack exchange,提问作者matcha latte




