| 1 | //===----- CodeGen/ExpandVectorPredication.cpp - Expand VP intrinsics -----===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements IR expansion for vector predication intrinsics, allowing |
| 10 | // targets to enable vector predication until just before codegen. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "llvm/CodeGen/ExpandVectorPredication.h" |
| 15 | #include "llvm/ADT/Statistic.h" |
| 16 | #include "llvm/Analysis/TargetTransformInfo.h" |
| 17 | #include "llvm/Analysis/ValueTracking.h" |
| 18 | #include "llvm/Analysis/VectorUtils.h" |
| 19 | #include "llvm/IR/Constants.h" |
| 20 | #include "llvm/IR/Function.h" |
| 21 | #include "llvm/IR/IRBuilder.h" |
| 22 | #include "llvm/IR/Instructions.h" |
| 23 | #include "llvm/IR/IntrinsicInst.h" |
| 24 | #include "llvm/IR/Intrinsics.h" |
| 25 | #include "llvm/Support/CommandLine.h" |
| 26 | #include "llvm/Support/Compiler.h" |
| 27 | #include "llvm/Support/Debug.h" |
| 28 | #include "llvm/Transforms/Utils/LoopUtils.h" |
| 29 | #include <optional> |
| 30 | |
| 31 | using namespace llvm; |
| 32 | |
| 33 | using VPLegalization = TargetTransformInfo::VPLegalization; |
| 34 | using VPTransform = TargetTransformInfo::VPLegalization::VPTransform; |
| 35 | |
| 36 | // Keep this in sync with TargetTransformInfo::VPLegalization. |
| 37 | #define VPINTERNAL_VPLEGAL_CASES \ |
| 38 | VPINTERNAL_CASE(Legal) \ |
| 39 | VPINTERNAL_CASE(Discard) \ |
| 40 | VPINTERNAL_CASE(Convert) |
| 41 | |
| 42 | #define VPINTERNAL_CASE(X) "|" #X |
| 43 | |
| 44 | // Override options. |
| 45 | static cl::opt<std::string> EVLTransformOverride( |
| 46 | "expandvp-override-evl-transform" , cl::init(Val: "" ), cl::Hidden, |
| 47 | cl::desc("Options: <empty>" VPINTERNAL_VPLEGAL_CASES |
| 48 | ". If non-empty, ignore " |
| 49 | "TargetTransformInfo and " |
| 50 | "always use this transformation for the %evl parameter (Used in " |
| 51 | "testing)." )); |
| 52 | |
| 53 | static cl::opt<std::string> MaskTransformOverride( |
| 54 | "expandvp-override-mask-transform" , cl::init(Val: "" ), cl::Hidden, |
| 55 | cl::desc("Options: <empty>" VPINTERNAL_VPLEGAL_CASES |
| 56 | ". If non-empty, Ignore " |
| 57 | "TargetTransformInfo and " |
| 58 | "always use this transformation for the %mask parameter (Used in " |
| 59 | "testing)." )); |
| 60 | |
| 61 | #undef VPINTERNAL_CASE |
| 62 | #define VPINTERNAL_CASE(X) .Case(#X, VPLegalization::X) |
| 63 | |
| 64 | static VPTransform parseOverrideOption(const std::string &TextOpt) { |
| 65 | return StringSwitch<VPTransform>(TextOpt) VPINTERNAL_VPLEGAL_CASES; |
| 66 | } |
| 67 | |
| 68 | #undef VPINTERNAL_VPLEGAL_CASES |
| 69 | |
| 70 | // Whether any override options are set. |
| 71 | static bool anyExpandVPOverridesSet() { |
| 72 | return !EVLTransformOverride.empty() || !MaskTransformOverride.empty(); |
| 73 | } |
| 74 | |
| 75 | #define DEBUG_TYPE "expandvp" |
| 76 | |
| 77 | STATISTIC(NumFoldedVL, "Number of folded vector length params" ); |
| 78 | STATISTIC(NumLoweredVPOps, "Number of folded vector predication operations" ); |
| 79 | |
| 80 | ///// Helpers { |
| 81 | |
| 82 | /// \returns Whether the vector mask \p MaskVal has all lane bits set. |
| 83 | static bool isAllTrueMask(Value *MaskVal) { |
| 84 | if (Value *SplattedVal = getSplatValue(V: MaskVal)) |
| 85 | if (auto *ConstValue = dyn_cast<Constant>(Val: SplattedVal)) |
| 86 | return ConstValue->isAllOnesValue(); |
| 87 | |
| 88 | return false; |
| 89 | } |
| 90 | |
| 91 | /// \returns A non-excepting divisor constant for this type. |
| 92 | static Constant *getSafeDivisor(Type *DivTy) { |
| 93 | assert(DivTy->isIntOrIntVectorTy() && "Unsupported divisor type" ); |
| 94 | return ConstantInt::get(Ty: DivTy, V: 1u, IsSigned: false); |
| 95 | } |
| 96 | |
| 97 | /// Transfer operation properties from \p OldVPI to \p NewVal. |
| 98 | static void transferDecorations(Value &NewVal, VPIntrinsic &VPI) { |
| 99 | auto *NewInst = dyn_cast<Instruction>(Val: &NewVal); |
| 100 | if (!NewInst || !isa<FPMathOperator>(Val: NewVal)) |
| 101 | return; |
| 102 | |
| 103 | auto *OldFMOp = dyn_cast<FPMathOperator>(Val: &VPI); |
| 104 | if (!OldFMOp) |
| 105 | return; |
| 106 | |
| 107 | NewInst->setFastMathFlags(OldFMOp->getFastMathFlags()); |
| 108 | } |
| 109 | |
| 110 | /// Transfer all properties from \p OldOp to \p NewOp and replace all uses. |
| 111 | /// OldVP gets erased. |
| 112 | static void replaceOperation(Value &NewOp, VPIntrinsic &OldOp) { |
| 113 | transferDecorations(NewVal&: NewOp, VPI&: OldOp); |
| 114 | |
| 115 | if (isa<Instruction>(Val: NewOp) && !NewOp.hasName() && OldOp.hasName()) |
| 116 | NewOp.takeName(V: &OldOp); |
| 117 | |
| 118 | OldOp.replaceAllUsesWith(V: &NewOp); |
| 119 | OldOp.eraseFromParent(); |
| 120 | } |
| 121 | |
| 122 | static bool maySpeculateLanes(VPIntrinsic &VPI) { |
| 123 | // The result of VP reductions depends on the mask and evl. |
| 124 | if (isa<VPReductionIntrinsic>(Val: VPI)) |
| 125 | return false; |
| 126 | // Fallback to whether the intrinsic is speculatable. |
| 127 | if (auto IntrID = VPI.getFunctionalIntrinsicID()) |
| 128 | return Intrinsic::getFnAttributes(C&: VPI.getContext(), id: *IntrID) |
| 129 | .hasAttribute(Kind: Attribute::AttrKind::Speculatable); |
| 130 | if (auto Opc = VPI.getFunctionalOpcode()) |
| 131 | return isSafeToSpeculativelyExecuteWithOpcode(Opcode: *Opc, Inst: &VPI); |
| 132 | return false; |
| 133 | } |
| 134 | |
| 135 | //// } Helpers |
| 136 | |
| 137 | namespace { |
| 138 | |
| 139 | // Expansion pass state at function scope. |
| 140 | struct CachingVPExpander { |
| 141 | const TargetTransformInfo &TTI; |
| 142 | |
| 143 | /// \returns A bitmask that is true where the lane position is less-than \p |
| 144 | /// EVLParam |
| 145 | /// |
| 146 | /// \p Builder |
| 147 | /// Used for instruction creation. |
| 148 | /// \p VLParam |
| 149 | /// The explicit vector length parameter to test against the lane |
| 150 | /// positions. |
| 151 | /// \p ElemCount |
| 152 | /// Static (potentially scalable) number of vector elements. |
| 153 | Value *convertEVLToMask(IRBuilder<> &Builder, Value *EVLParam, |
| 154 | ElementCount ElemCount); |
| 155 | |
| 156 | /// If needed, folds the EVL in the mask operand and discards the EVL |
| 157 | /// parameter. Returns true if the mask was actually folded. |
| 158 | bool foldEVLIntoMask(VPIntrinsic &VPI); |
| 159 | |
| 160 | /// "Remove" the %evl parameter of \p PI by setting it to the static vector |
| 161 | /// length of the operation. Returns true if the %evl (if any) was effectively |
| 162 | /// changed. |
| 163 | bool discardEVLParameter(VPIntrinsic &PI); |
| 164 | |
| 165 | /// Lower this VP binary operator to a unpredicated binary operator. |
| 166 | bool expandPredicationInBinaryOperator(IRBuilder<> &Builder, VPIntrinsic &PI); |
| 167 | |
| 168 | /// Lower this VP reduction to a call to an unpredicated reduction intrinsic. |
| 169 | bool expandPredicationInReduction(IRBuilder<> &Builder, |
| 170 | VPReductionIntrinsic &PI); |
| 171 | |
| 172 | /// Lower this VP memory operation to a non-VP intrinsic. |
| 173 | bool expandPredicationInMemoryIntrinsic(IRBuilder<> &Builder, |
| 174 | VPIntrinsic &VPI); |
| 175 | |
| 176 | /// Query TTI and expand the vector predication in \p P accordingly. |
| 177 | bool expandPredication(VPIntrinsic &PI); |
| 178 | |
| 179 | /// Determine how and whether the VPIntrinsic \p VPI shall be expanded. This |
| 180 | /// overrides TTI with the cl::opts listed at the top of this file. |
| 181 | VPLegalization getVPLegalizationStrategy(const VPIntrinsic &VPI) const; |
| 182 | bool UsingTTIOverrides; |
| 183 | |
| 184 | public: |
| 185 | CachingVPExpander(const TargetTransformInfo &TTI) |
| 186 | : TTI(TTI), UsingTTIOverrides(anyExpandVPOverridesSet()) {} |
| 187 | |
| 188 | /// Expand llvm.vp.* intrinsics as requested by \p TTI. |
| 189 | /// Returns the details of the expansion. |
| 190 | VPExpansionDetails expandVectorPredication(VPIntrinsic &VPI); |
| 191 | }; |
| 192 | |
| 193 | //// CachingVPExpander { |
| 194 | |
| 195 | Value *CachingVPExpander::convertEVLToMask(IRBuilder<> &Builder, |
| 196 | Value *EVLParam, |
| 197 | ElementCount ElemCount) { |
| 198 | // TODO add caching |
| 199 | // Scalable vector %evl conversion. |
| 200 | if (ElemCount.isScalable()) { |
| 201 | Type *BoolVecTy = VectorType::get(ElementType: Builder.getInt1Ty(), EC: ElemCount); |
| 202 | // `get_active_lane_mask` performs an implicit less-than comparison. |
| 203 | Value *ConstZero = Builder.getInt32(C: 0); |
| 204 | return Builder.CreateIntrinsic(ID: Intrinsic::get_active_lane_mask, |
| 205 | OverloadTypes: {BoolVecTy, EVLParam->getType()}, |
| 206 | Args: {ConstZero, EVLParam}); |
| 207 | } |
| 208 | |
| 209 | // Fixed vector %evl conversion. |
| 210 | Type *LaneTy = EVLParam->getType(); |
| 211 | unsigned NumElems = ElemCount.getFixedValue(); |
| 212 | Value *VLSplat = Builder.CreateVectorSplat(NumElts: NumElems, V: EVLParam); |
| 213 | Value *IdxVec = Builder.CreateStepVector(DstType: VectorType::get(ElementType: LaneTy, EC: ElemCount)); |
| 214 | return Builder.CreateICmp(P: CmpInst::ICMP_ULT, LHS: IdxVec, RHS: VLSplat); |
| 215 | } |
| 216 | |
| 217 | bool CachingVPExpander::expandPredicationInBinaryOperator(IRBuilder<> &Builder, |
| 218 | VPIntrinsic &VPI) { |
| 219 | assert((maySpeculateLanes(VPI) || VPI.canIgnoreVectorLengthParam()) && |
| 220 | "Implicitly dropping %evl in non-speculatable operator!" ); |
| 221 | |
| 222 | auto OC = static_cast<Instruction::BinaryOps>(*VPI.getFunctionalOpcode()); |
| 223 | assert(Instruction::isBinaryOp(OC)); |
| 224 | |
| 225 | Value *Op0 = VPI.getOperand(i_nocapture: 0); |
| 226 | Value *Op1 = VPI.getOperand(i_nocapture: 1); |
| 227 | Value *Mask = VPI.getMaskParam(); |
| 228 | |
| 229 | // Blend in safe operands. |
| 230 | if (Mask && !isAllTrueMask(MaskVal: Mask)) { |
| 231 | switch (OC) { |
| 232 | default: |
| 233 | // Can safely ignore the predicate. |
| 234 | break; |
| 235 | |
| 236 | // Division operators need a safe divisor on masked-off lanes (1). |
| 237 | case Instruction::UDiv: |
| 238 | case Instruction::SDiv: |
| 239 | case Instruction::URem: |
| 240 | case Instruction::SRem: |
| 241 | // 2nd operand must not be zero. |
| 242 | Value *SafeDivisor = getSafeDivisor(DivTy: VPI.getType()); |
| 243 | Op1 = Builder.CreateSelect(C: Mask, True: Op1, False: SafeDivisor); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | Value *NewBinOp = Builder.CreateBinOp(Opc: OC, LHS: Op0, RHS: Op1); |
| 248 | |
| 249 | replaceOperation(NewOp&: *NewBinOp, OldOp&: VPI); |
| 250 | return true; |
| 251 | } |
| 252 | |
| 253 | static Value *getNeutralReductionElement(const VPReductionIntrinsic &VPI, |
| 254 | Type *EltTy) { |
| 255 | Intrinsic::ID RdxID = *VPI.getFunctionalIntrinsicID(); |
| 256 | return getReductionIdentity(RdxID, Ty: EltTy, FMF: VPI.getFastMathFlagsOrNone()); |
| 257 | } |
| 258 | |
| 259 | bool CachingVPExpander::expandPredicationInReduction( |
| 260 | IRBuilder<> &Builder, VPReductionIntrinsic &VPI) { |
| 261 | assert((maySpeculateLanes(VPI) || VPI.canIgnoreVectorLengthParam()) && |
| 262 | "Implicitly dropping %evl in non-speculatable operator!" ); |
| 263 | |
| 264 | Value *Mask = VPI.getMaskParam(); |
| 265 | Value *RedOp = VPI.getOperand(i_nocapture: VPI.getVectorParamPos()); |
| 266 | |
| 267 | // Insert neutral element in masked-out positions |
| 268 | if (Mask && !isAllTrueMask(MaskVal: Mask)) { |
| 269 | auto *NeutralElt = getNeutralReductionElement(VPI, EltTy: VPI.getType()); |
| 270 | auto *NeutralVector = Builder.CreateVectorSplat( |
| 271 | EC: cast<VectorType>(Val: RedOp->getType())->getElementCount(), V: NeutralElt); |
| 272 | RedOp = Builder.CreateSelect(C: Mask, True: RedOp, False: NeutralVector); |
| 273 | } |
| 274 | |
| 275 | Value *Reduction; |
| 276 | Value *Start = VPI.getOperand(i_nocapture: VPI.getStartParamPos()); |
| 277 | |
| 278 | switch (VPI.getIntrinsicID()) { |
| 279 | default: |
| 280 | llvm_unreachable("Impossible reduction kind" ); |
| 281 | case Intrinsic::vp_reduce_add: |
| 282 | case Intrinsic::vp_reduce_mul: |
| 283 | case Intrinsic::vp_reduce_and: |
| 284 | case Intrinsic::vp_reduce_or: |
| 285 | case Intrinsic::vp_reduce_xor: { |
| 286 | Intrinsic::ID RedID = *VPI.getFunctionalIntrinsicID(); |
| 287 | unsigned Opc = getArithmeticReductionInstruction(RdxID: RedID); |
| 288 | assert(Instruction::isBinaryOp(Opc)); |
| 289 | Reduction = Builder.CreateUnaryIntrinsic(ID: RedID, Op: RedOp); |
| 290 | Reduction = |
| 291 | Builder.CreateBinOp(Opc: (Instruction::BinaryOps)Opc, LHS: Reduction, RHS: Start); |
| 292 | break; |
| 293 | } |
| 294 | case Intrinsic::vp_reduce_smax: |
| 295 | case Intrinsic::vp_reduce_smin: |
| 296 | case Intrinsic::vp_reduce_umax: |
| 297 | case Intrinsic::vp_reduce_umin: |
| 298 | case Intrinsic::vp_reduce_fmax: |
| 299 | case Intrinsic::vp_reduce_fmin: |
| 300 | case Intrinsic::vp_reduce_fmaximum: |
| 301 | case Intrinsic::vp_reduce_fminimum: { |
| 302 | Intrinsic::ID RedID = *VPI.getFunctionalIntrinsicID(); |
| 303 | Intrinsic::ID ScalarID = getMinMaxReductionIntrinsicOp(RdxID: RedID); |
| 304 | Reduction = Builder.CreateUnaryIntrinsic(ID: RedID, Op: RedOp); |
| 305 | transferDecorations(NewVal&: *Reduction, VPI); |
| 306 | Reduction = Builder.CreateBinaryIntrinsic(ID: ScalarID, LHS: Reduction, RHS: Start); |
| 307 | break; |
| 308 | } |
| 309 | case Intrinsic::vp_reduce_fadd: |
| 310 | Reduction = Builder.CreateFAddReduce(Acc: Start, Src: RedOp); |
| 311 | break; |
| 312 | case Intrinsic::vp_reduce_fmul: |
| 313 | Reduction = Builder.CreateFMulReduce(Acc: Start, Src: RedOp); |
| 314 | break; |
| 315 | } |
| 316 | |
| 317 | replaceOperation(NewOp&: *Reduction, OldOp&: VPI); |
| 318 | return true; |
| 319 | } |
| 320 | |
| 321 | bool CachingVPExpander::expandPredicationInMemoryIntrinsic(IRBuilder<> &Builder, |
| 322 | VPIntrinsic &VPI) { |
| 323 | assert(VPI.canIgnoreVectorLengthParam()); |
| 324 | |
| 325 | const auto &DL = VPI.getDataLayout(); |
| 326 | |
| 327 | Value *MaskParam = VPI.getMaskParam(); |
| 328 | Value *PtrParam = VPI.getMemoryPointerParam(); |
| 329 | Value *DataParam = VPI.getMemoryDataParam(); |
| 330 | bool IsUnmasked = isAllTrueMask(MaskVal: MaskParam); |
| 331 | |
| 332 | MaybeAlign AlignOpt = VPI.getPointerAlignment(); |
| 333 | |
| 334 | Value *NewMemoryInst = nullptr; |
| 335 | switch (VPI.getIntrinsicID()) { |
| 336 | default: |
| 337 | llvm_unreachable("Not a VP memory intrinsic" ); |
| 338 | case Intrinsic::vp_store: |
| 339 | if (IsUnmasked) { |
| 340 | StoreInst *NewStore = |
| 341 | Builder.CreateStore(Val: DataParam, Ptr: PtrParam, /*IsVolatile*/ isVolatile: false); |
| 342 | if (AlignOpt.has_value()) |
| 343 | NewStore->setAlignment(*AlignOpt); |
| 344 | NewMemoryInst = NewStore; |
| 345 | } else |
| 346 | NewMemoryInst = Builder.CreateMaskedStore( |
| 347 | Val: DataParam, Ptr: PtrParam, Alignment: AlignOpt.valueOrOne(), Mask: MaskParam); |
| 348 | |
| 349 | break; |
| 350 | case Intrinsic::vp_load: |
| 351 | if (IsUnmasked) { |
| 352 | LoadInst *NewLoad = |
| 353 | Builder.CreateLoad(Ty: VPI.getType(), Ptr: PtrParam, /*IsVolatile*/ isVolatile: false); |
| 354 | if (AlignOpt.has_value()) |
| 355 | NewLoad->setAlignment(*AlignOpt); |
| 356 | NewMemoryInst = NewLoad; |
| 357 | } else |
| 358 | NewMemoryInst = Builder.CreateMaskedLoad( |
| 359 | Ty: VPI.getType(), Ptr: PtrParam, Alignment: AlignOpt.valueOrOne(), Mask: MaskParam); |
| 360 | |
| 361 | break; |
| 362 | case Intrinsic::vp_scatter: { |
| 363 | auto *ElementType = |
| 364 | cast<VectorType>(Val: DataParam->getType())->getElementType(); |
| 365 | NewMemoryInst = Builder.CreateMaskedScatter( |
| 366 | Val: DataParam, Ptrs: PtrParam, |
| 367 | Alignment: AlignOpt.value_or(u: DL.getPrefTypeAlign(Ty: ElementType)), Mask: MaskParam); |
| 368 | break; |
| 369 | } |
| 370 | case Intrinsic::vp_gather: { |
| 371 | auto *ElementType = cast<VectorType>(Val: VPI.getType())->getElementType(); |
| 372 | NewMemoryInst = Builder.CreateMaskedGather( |
| 373 | Ty: VPI.getType(), Ptrs: PtrParam, |
| 374 | Alignment: AlignOpt.value_or(u: DL.getPrefTypeAlign(Ty: ElementType)), Mask: MaskParam, |
| 375 | PassThru: nullptr); |
| 376 | break; |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | assert(NewMemoryInst); |
| 381 | replaceOperation(NewOp&: *NewMemoryInst, OldOp&: VPI); |
| 382 | return true; |
| 383 | } |
| 384 | |
| 385 | bool CachingVPExpander::discardEVLParameter(VPIntrinsic &VPI) { |
| 386 | LLVM_DEBUG(dbgs() << "Discard EVL parameter in " << VPI << "\n" ); |
| 387 | |
| 388 | if (VPI.canIgnoreVectorLengthParam()) |
| 389 | return false; |
| 390 | |
| 391 | Value *EVLParam = VPI.getVectorLengthParam(); |
| 392 | if (!EVLParam) |
| 393 | return false; |
| 394 | |
| 395 | ElementCount StaticElemCount = VPI.getStaticVectorLength(); |
| 396 | Value *MaxEVL = nullptr; |
| 397 | Type *Int32Ty = Type::getInt32Ty(C&: VPI.getContext()); |
| 398 | if (StaticElemCount.isScalable()) { |
| 399 | // TODO add caching |
| 400 | IRBuilder<> Builder(VPI.getParent(), VPI.getIterator()); |
| 401 | Value *FactorConst = Builder.getInt32(C: StaticElemCount.getKnownMinValue()); |
| 402 | Value *VScale = Builder.CreateVScale(Ty: Int32Ty, Name: "vscale" ); |
| 403 | MaxEVL = Builder.CreateNUWMul(LHS: VScale, RHS: FactorConst, Name: "scalable_size" ); |
| 404 | } else { |
| 405 | MaxEVL = ConstantInt::get(Ty: Int32Ty, V: StaticElemCount.getFixedValue(), IsSigned: false); |
| 406 | } |
| 407 | VPI.setVectorLengthParam(MaxEVL); |
| 408 | return true; |
| 409 | } |
| 410 | |
| 411 | bool CachingVPExpander::foldEVLIntoMask(VPIntrinsic &VPI) { |
| 412 | LLVM_DEBUG(dbgs() << "Folding vlen for " << VPI << '\n'); |
| 413 | |
| 414 | IRBuilder<> Builder(&VPI); |
| 415 | |
| 416 | // Ineffective %evl parameter and so nothing to do here. |
| 417 | if (VPI.canIgnoreVectorLengthParam()) |
| 418 | return false; |
| 419 | |
| 420 | // Only VP intrinsics can have an %evl parameter. |
| 421 | Value *OldMaskParam = VPI.getMaskParam(); |
| 422 | if (!OldMaskParam) { |
| 423 | assert((VPI.getIntrinsicID() == Intrinsic::vp_merge) && |
| 424 | "Unexpected VP intrinsic without mask operand" ); |
| 425 | OldMaskParam = VPI.getArgOperand(i: 0); |
| 426 | } |
| 427 | |
| 428 | Value *OldEVLParam = VPI.getVectorLengthParam(); |
| 429 | assert(OldMaskParam && "no mask param to fold the vl param into" ); |
| 430 | assert(OldEVLParam && "no EVL param to fold away" ); |
| 431 | |
| 432 | LLVM_DEBUG(dbgs() << "OLD evl: " << *OldEVLParam << '\n'); |
| 433 | LLVM_DEBUG(dbgs() << "OLD mask: " << *OldMaskParam << '\n'); |
| 434 | |
| 435 | // Convert the %evl predication into vector mask predication. |
| 436 | ElementCount ElemCount = VPI.getStaticVectorLength(); |
| 437 | Value *VLMask = convertEVLToMask(Builder, EVLParam: OldEVLParam, ElemCount); |
| 438 | Value *NewMaskParam = Builder.CreateAnd(LHS: VLMask, RHS: OldMaskParam); |
| 439 | if (VPI.getIntrinsicID() == Intrinsic::vp_merge) |
| 440 | VPI.setArgOperand(i: 0, v: NewMaskParam); |
| 441 | else |
| 442 | VPI.setMaskParam(NewMaskParam); |
| 443 | |
| 444 | // Drop the %evl parameter. |
| 445 | discardEVLParameter(VPI); |
| 446 | assert(VPI.canIgnoreVectorLengthParam() && |
| 447 | "transformation did not render the evl param ineffective!" ); |
| 448 | |
| 449 | // Reassess the modified instruction. |
| 450 | return true; |
| 451 | } |
| 452 | |
| 453 | bool CachingVPExpander::expandPredication(VPIntrinsic &VPI) { |
| 454 | LLVM_DEBUG(dbgs() << "Lowering to unpredicated op: " << VPI << '\n'); |
| 455 | |
| 456 | IRBuilder<> Builder(&VPI); |
| 457 | |
| 458 | // Try lowering to a LLVM instruction first. |
| 459 | auto OC = VPI.getFunctionalOpcode(); |
| 460 | |
| 461 | if (OC && Instruction::isBinaryOp(Opcode: *OC)) |
| 462 | return expandPredicationInBinaryOperator(Builder, VPI); |
| 463 | |
| 464 | if (auto *VPRI = dyn_cast<VPReductionIntrinsic>(Val: &VPI)) |
| 465 | return expandPredicationInReduction(Builder, VPI&: *VPRI); |
| 466 | |
| 467 | switch (VPI.getIntrinsicID()) { |
| 468 | default: |
| 469 | break; |
| 470 | case Intrinsic::vp_merge: { |
| 471 | assert(maySpeculateLanes(VPI) || VPI.canIgnoreVectorLengthParam()); |
| 472 | Value *NewSelectOp = Builder.CreateSelect( |
| 473 | C: VPI.getOperand(i_nocapture: 0), True: VPI.getOperand(i_nocapture: 1), False: VPI.getOperand(i_nocapture: 2)); |
| 474 | replaceOperation(NewOp&: *NewSelectOp, OldOp&: VPI); |
| 475 | return NewSelectOp; |
| 476 | } |
| 477 | case Intrinsic::vp_load: |
| 478 | case Intrinsic::vp_store: |
| 479 | case Intrinsic::vp_gather: |
| 480 | case Intrinsic::vp_scatter: |
| 481 | return expandPredicationInMemoryIntrinsic(Builder, VPI); |
| 482 | } |
| 483 | |
| 484 | return false; |
| 485 | } |
| 486 | |
| 487 | //// } CachingVPExpander |
| 488 | |
| 489 | void sanitizeStrategy(VPIntrinsic &VPI, VPLegalization &LegalizeStrat) { |
| 490 | // Operations with speculatable lanes do not strictly need predication. |
| 491 | if (maySpeculateLanes(VPI)) { |
| 492 | // Converting a speculatable VP intrinsic means dropping %mask and %evl. |
| 493 | // No need to expand %evl into the %mask only to ignore that code. |
| 494 | if (LegalizeStrat.OpStrategy == VPLegalization::Convert) |
| 495 | LegalizeStrat.EVLParamStrategy = VPLegalization::Discard; |
| 496 | return; |
| 497 | } |
| 498 | |
| 499 | // We have to preserve the predicating effect of %evl for this |
| 500 | // non-speculatable VP intrinsic. |
| 501 | // 1) Never discard %evl. |
| 502 | // 2) If this VP intrinsic will be expanded to non-VP code, make sure that |
| 503 | // %evl gets folded into %mask. |
| 504 | if ((LegalizeStrat.EVLParamStrategy == VPLegalization::Discard) || |
| 505 | (LegalizeStrat.OpStrategy == VPLegalization::Convert)) { |
| 506 | LegalizeStrat.EVLParamStrategy = VPLegalization::Convert; |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | VPLegalization |
| 511 | CachingVPExpander::getVPLegalizationStrategy(const VPIntrinsic &VPI) const { |
| 512 | auto VPStrat = TTI.getVPLegalizationStrategy(PI: VPI); |
| 513 | if (LLVM_LIKELY(!UsingTTIOverrides)) { |
| 514 | // No overrides - we are in production. |
| 515 | return VPStrat; |
| 516 | } |
| 517 | |
| 518 | // Overrides set - we are in testing, the following does not need to be |
| 519 | // efficient. |
| 520 | VPStrat.EVLParamStrategy = parseOverrideOption(TextOpt: EVLTransformOverride); |
| 521 | VPStrat.OpStrategy = parseOverrideOption(TextOpt: MaskTransformOverride); |
| 522 | return VPStrat; |
| 523 | } |
| 524 | |
| 525 | VPExpansionDetails |
| 526 | CachingVPExpander::expandVectorPredication(VPIntrinsic &VPI) { |
| 527 | auto Strategy = getVPLegalizationStrategy(VPI); |
| 528 | sanitizeStrategy(VPI, LegalizeStrat&: Strategy); |
| 529 | |
| 530 | VPExpansionDetails Changed = VPExpansionDetails::IntrinsicUnchanged; |
| 531 | |
| 532 | // Transform the EVL parameter. |
| 533 | switch (Strategy.EVLParamStrategy) { |
| 534 | case VPLegalization::Legal: |
| 535 | break; |
| 536 | case VPLegalization::Discard: |
| 537 | if (discardEVLParameter(VPI)) |
| 538 | Changed = VPExpansionDetails::IntrinsicUpdated; |
| 539 | break; |
| 540 | case VPLegalization::Convert: |
| 541 | if (foldEVLIntoMask(VPI)) { |
| 542 | Changed = VPExpansionDetails::IntrinsicUpdated; |
| 543 | ++NumFoldedVL; |
| 544 | } |
| 545 | break; |
| 546 | } |
| 547 | |
| 548 | // Replace with a non-predicated operation. |
| 549 | switch (Strategy.OpStrategy) { |
| 550 | case VPLegalization::Legal: |
| 551 | break; |
| 552 | case VPLegalization::Discard: |
| 553 | llvm_unreachable("Invalid strategy for operators." ); |
| 554 | case VPLegalization::Convert: |
| 555 | if (expandPredication(VPI)) { |
| 556 | ++NumLoweredVPOps; |
| 557 | Changed = VPExpansionDetails::IntrinsicReplaced; |
| 558 | } |
| 559 | break; |
| 560 | } |
| 561 | |
| 562 | return Changed; |
| 563 | } |
| 564 | } // namespace |
| 565 | |
| 566 | VPExpansionDetails |
| 567 | llvm::expandVectorPredicationIntrinsic(VPIntrinsic &VPI, |
| 568 | const TargetTransformInfo &TTI) { |
| 569 | return CachingVPExpander(TTI).expandVectorPredication(VPI); |
| 570 | } |
| 571 | |