LLVM Loop Vectorizer未矢量化自定义IR,求解决方法
我用IRBuilder生成了LLVM IR代码,尝试用LoopVectorizer对其中的循环进行矢量化,但没能成功。以下是ModulePassManager运行后的LLVM IR代码:
; Function Attrs: argmemonly nofree norecurse nosync nounwind define void @batch_function_0(ptr nocapture readonly %parameters, ptr nocapture readnone %series, ptr nocapture %state_vars, ptr nocapture readnone %solver_workspace, ptr nocapture readnone %date_time, double %fractional_step) local_unnamed_addr #1 { entry: br label %loop loop: ; preds = %loop, %entry %index = phi i64 [ 0, %entry ], [ %next_iter, %loop ] %subtemp = add nuw nsw i64 %index, -100 %var_ptr = getelementptr double, ptr %state_vars, i64 %subtemp %var = load double, ptr %var_ptr, align 8 %addtemp = add nuw nsw i64 %index, 2 %par_ptr = getelementptr double, ptr %parameters, i64 %addtemp %par = load double, ptr %par_ptr, align 8 %fmultemp = fmul fast double %par, %var %var_ptr1 = getelementptr double, ptr %state_vars, i64 %index store double %fmultemp, ptr %var_ptr1, align 8 %next_iter = add nuw nsw i64 %index, 1 %loopcond.not = icmp eq i64 %next_iter, 100 br i1 %loopcond.not, label %afterloop, label %loop afterloop: ; preds = %loop ret void }
我的优化Pass配置如下:
llvm::LoopAnalysisManager lam; llvm::FunctionAnalysisManager fam; llvm::CGSCCAnalysisManager cgam; llvm::ModuleAnalysisManager mam; llvm::PassBuilder pb; pb.registerModuleAnalyses(mam); pb.registerCGSCCAnalyses(cgam); pb.registerFunctionAnalyses(fam); pb.registerLoopAnalyses(lam); pb.crossRegisterProxies(lam, fam, cgam, mam); llvm::ModulePassManager mpm = pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2); mpm.addPass(llvm::createModuleToFunctionPassAdaptor(llvm::LoopVectorizePass())); mpm.run(*data->module, mam);
对应的等价C代码如下(该代码用Clang -O2编译时可被矢量化,我并非从C生成原始IR):
void testfun(double *state_var, double *par) { for(int i = 0; i < 100; ++i) { state_var[i] = state_var[i-100]*par[i+2]; } }
解决方案
调整Pass执行顺序:默认O2优化流水线已经包含LoopVectorizePass,你在其后重复添加可能导致Pass执行时机不对。可以尝试移除手动添加的LoopVectorizePass,直接依赖默认O2流水线;或者手动构建Pass序列,确保LoopVectorizer在循环简化、别名分析等前置优化之后运行。
解决内存访问的别名与对齐问题:
- 给
state_vars和parameters指针添加noalias属性,明确告知编译器两个指针指向的内存区域无重叠,消除存储-加载的依赖顾虑。 - 确保内存访问对齐,比如给
state_vars和parameters添加align 64(适配多数平台的向量宽度)属性,矢量化要求内存访问符合向量对齐标准。
- 给
优化循环终止条件的形式:将当前的终止条件
icmp eq i64 %next_iter, 100改为更符合LoopVectorizer预期的icmp slt i64 %index, 100,调整后的循环结构更贴近常见的循环模式,便于矢量化分析。明确反向内存访问的安全性:循环中
state_var[i-100]的反向访问可能让LoopVectorizer产生顾虑,可通过添加llvm::AssumeInst来告知编译器该访问是安全的,不存在越界或未定义行为;或者确保state_vars指针指向的内存区域包含i-100对应的偏移位置。强制启用矢量化(用于测试):创建LoopVectorizePass时启用强制矢量化选项,验证是否是启发式判断导致的不矢量化:
mpm.addPass(llvm::createModuleToFunctionPassAdaptor( llvm::LoopVectorizePass(llvm::LoopVectorizeOptions().setForce(true))));
内容的提问来源于stack exchange,提问作者Magnus Dahler Norling

