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:
    1. 若 %evl 有效且需要折叠,则先用 icmp ult/get_active_lane_mask 将 %evl 转为与 %mask 的合并新掩码;
    2. 对每个元素插入 select(mask, a, 0)(安全中立值);
    3. 生成一个普通的 add <8 x i32> 或者逐元素的 vector_add;
    4. 删除原 llvm.vp_add 调用,替换成上述无 predication 的 IR。
      该 Pass 能够统计:
  • 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(或执行掩码)可以屏蔽某些线程的读写;