后端Pass简介——ExpandVectorPredication
ExpandVectorPredication
将 LLVM IR 中的向量 predication(llvm.vp.*)内建展开为等价的、无 predication 的 LLVM IR 或者原生指令序列,以便让不支持 predication 的后端也能正确生成代码。
涉及的概念如下:
- 向量 predication(Vector Predication):通过==掩码(mask)和可选的显式向量长度(evl)==控制向量操作在哪些 lane 上生效。
- VPIntrinsic:LLVM 对 predication 各类操作(算术、比较、内存、cast、reduction 等)在 IR 级别的封装。
- 合法化策略(VPLegalization):由 TargetTransformInfo 提供的三元组策略,指定如何处理 evl(Legal/Discard/Convert)和如何处理操作本身(Legal 或 Convert)。
- 展开(Expansion):依据策略,可能先将 %evl 折叠进 mask,再把 VPIntrinsic 转为普通 binop、icmp/fcmp、load/store、shufflevector 归约、或针对浮点/整数的原生 intrinsic 调用。
举例:
%res = call <8 x i32> @llvm.vp_add(<8 x i32> %a, <8 x i32> %b, <8 x i1> %mask, i32 %evl)
- 若策略为 Convert:
- 若 %evl 有效且需要折叠,则先用
icmp ult/get_active_lane_mask
将 %evl 转为与 %mask 的合并新掩码; - 对每个元素插入 select(mask, a, 0)(安全中立值);
- 生成一个普通的
add <8 x i32>
或者逐元素的 vector_add; - 删除原
llvm.vp_add
调用,替换成上述无 predication 的 IR。
该 Pass 能够统计:
- 若 %evl 有效且需要折叠,则先用
- NumFoldedVL:成功将 %evl 折叠进 %mask 的次数;
- NumLoweredVPOps:真正将 VPIntrinsic 展开为非 predicated 操作的次数。
该 Pass 600 多行。可以处理的有:
case Intrinsic::vp_abs:
case Intrinsic::vp_smax:
case Intrinsic::vp_smin:
case Intrinsic::vp_umax:
case Intrinsic::vp_umin:
case Intrinsic::vp_bswap:
case Intrinsic::vp_bitreverse:
case Intrinsic::vp_ctpop:
case Intrinsic::vp_ctlz:
case Intrinsic::vp_cttz:
case Intrinsic::vp_sadd_sat:
case Intrinsic::vp_uadd_sat:
case Intrinsic::vp_ssub_sat:
case Intrinsic::vp_usub_sat:
case Intrinsic::vp_fshl:
case Intrinsic::vp_fshr:
return expandPredicationToIntCall(Builder, VPI);
case Intrinsic::vp_fabs:
case Intrinsic::vp_sqrt:
case Intrinsic::vp_maxnum:
case Intrinsic::vp_minnum:
case Intrinsic::vp_maximum:
case Intrinsic::vp_minimum:
case Intrinsic::vp_fma:
case Intrinsic::vp_fmuladd:
return expandPredicationToFPCall(Builder, VPI,
VPI.getFunctionalIntrinsicID().value());
case Intrinsic::vp_load:
case Intrinsic::vp_store:
case Intrinsic::vp_gather:
case Intrinsic::vp_scatter:
理解:对一个向量里的某些元素进行某些操作,而其他不动。
然后要么由后端 Intrinsic 来处理,要么展开成,select/masked_load/masked_store 等
类比:GPU 在一个 warp/ wavefront 里所有线程是 lockstep 执行的,但通过 predicate register(或执行掩码)可以屏蔽某些线程的读写;
评论