LLVM 24.0.0git
ScalarizeMaskedMemIntrin.cpp
Go to the documentation of this file.
1//===- ScalarizeMaskedMemIntrin.cpp - Scalarize unsupported masked mem ----===//
2// intrinsics
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This pass replaces masked memory intrinsics - when unsupported by the target
11// - with a chain of basic blocks, that deal with the elements one-by-one if the
12// appropriate mask bit is set.
13//
14//===----------------------------------------------------------------------===//
15
17#include "llvm/ADT/Twine.h"
21#include "llvm/IR/BasicBlock.h"
22#include "llvm/IR/Constant.h"
23#include "llvm/IR/Constants.h"
25#include "llvm/IR/Dominators.h"
26#include "llvm/IR/Function.h"
27#include "llvm/IR/IRBuilder.h"
28#include "llvm/IR/Instruction.h"
31#include "llvm/IR/Metadata.h"
33#include "llvm/IR/Type.h"
34#include "llvm/IR/Value.h"
36#include "llvm/Pass.h"
40#include <cassert>
41#include <optional>
42
43using namespace llvm;
44
45#define DEBUG_TYPE "scalarize-masked-mem-intrin"
46
47namespace {
48
49class ScalarizeMaskedMemIntrinLegacyPass : public FunctionPass {
50public:
51 static char ID; // Pass identification, replacement for typeid
52
53 explicit ScalarizeMaskedMemIntrinLegacyPass() : FunctionPass(ID) {
56 }
57
58 bool runOnFunction(Function &F) override;
59
60 StringRef getPassName() const override {
61 return "Scalarize Masked Memory Intrinsics";
62 }
63
64 void getAnalysisUsage(AnalysisUsage &AU) const override {
67 }
68};
69
70} // end anonymous namespace
71
72static bool optimizeBlock(BasicBlock &BB, bool &ModifiedDT,
73 const TargetTransformInfo &TTI, const DataLayout &DL,
74 bool HasBranchDivergence, DomTreeUpdater *DTU);
75static bool optimizeCallInst(CallInst *CI, bool &ModifiedDT,
77 const DataLayout &DL, bool HasBranchDivergence,
78 DomTreeUpdater *DTU);
79
80char ScalarizeMaskedMemIntrinLegacyPass::ID = 0;
81
82INITIALIZE_PASS_BEGIN(ScalarizeMaskedMemIntrinLegacyPass, DEBUG_TYPE,
83 "Scalarize unsupported masked memory intrinsics", false,
84 false)
87INITIALIZE_PASS_END(ScalarizeMaskedMemIntrinLegacyPass, DEBUG_TYPE,
88 "Scalarize unsupported masked memory intrinsics", false,
89 false)
90
92 return new ScalarizeMaskedMemIntrinLegacyPass();
93}
94
95static bool isConstantIntVector(Value *Mask) {
97 if (!C)
98 return false;
99
100 unsigned NumElts = cast<FixedVectorType>(Mask->getType())->getNumElements();
101 for (unsigned i = 0; i != NumElts; ++i) {
102 Constant *CElt = C->getAggregateElement(i);
103 if (!CElt || !isa<ConstantInt>(CElt))
104 return false;
105 }
106
107 return true;
108}
109
110static unsigned adjustForEndian(const DataLayout &DL, unsigned VectorWidth,
111 unsigned Idx) {
112 return DL.isBigEndian() ? VectorWidth - 1 - Idx : Idx;
113}
114
115static void copyMemCacheHint(Instruction &Dest, const Instruction &Source,
116 unsigned SourcePtrOperand,
117 unsigned DestPtrOperand) {
118 MDNode *CacheHint = Source.getMetadata(LLVMContext::MD_mem_cache_hint);
119 // These intrinsics have a single memory operand.
120 if (!CacheHint || CacheHint->getNumOperands() != 2)
121 return;
122
123 auto *OperandNo = mdconst::extract<ConstantInt>(CacheHint->getOperand(0));
124 if (OperandNo->getZExtValue() != SourcePtrOperand)
125 return;
126
127 Metadata *DestOperandNo = ConstantAsMetadata::get(
128 ConstantInt::get(Type::getInt32Ty(Dest.getContext()), DestPtrOperand));
129 Dest.setMetadata(LLVMContext::MD_mem_cache_hint,
130 MDNode::get(Dest.getContext(),
131 {DestOperandNo, CacheHint->getOperand(1)}));
132}
133
135 Instruction &Dest, const Instruction &Source, const DataLayout &DL,
136 unsigned SourcePtrOperand, unsigned DestPtrOperand, Type *AccessType,
137 bool IsWholeAccess, std::optional<size_t> ByteOffset) {
138 // Only propagate metadata that is valid on each constituent memory access.
139 // In particular, do not copy metadata whose meaning is tied to the call,
140 // such as !prof or !callsite.
141 Dest.copyMetadata(Source,
142 {LLVMContext::MD_nontemporal,
143 LLVMContext::MD_mem_parallel_loop_access,
144 LLVMContext::MD_access_group, LLVMContext::MD_annotation,
145 LLVMContext::MD_nosanitize, LLVMContext::MD_mmra});
146
147 AAMDNodes AANodes = Source.getAAMetadata();
148 if (IsWholeAccess)
149 Dest.setAAMetadata(AANodes);
150 else if (ByteOffset)
151 Dest.setAAMetadata(AANodes.adjustForAccess(*ByteOffset, AccessType, DL));
152 else {
153 // The packed address is runtime-dependent. The other AA metadata remains
154 // applicable, but !tbaa.struct cannot be adjusted to a known byte range.
155 AANodes.TBAAStruct = nullptr;
156 Dest.setAAMetadata(AANodes);
157 }
158 copyMemCacheHint(Dest, Source, SourcePtrOperand, DestPtrOperand);
159}
160
162 const Instruction &Source,
163 const DataLayout &DL,
164 unsigned SourcePtrOperand,
165 std::optional<size_t> ByteOffset) {
166 copyMetadataForMemoryAccess(Dest, Source, DL, SourcePtrOperand,
167 Dest.getPointerOperandIndex(), Dest.getType(),
168 Dest.getType() == Source.getType(), ByteOffset);
169
170 // !range applies element-wise to vectors, so the same range describes each
171 // scalar result. The other metadata here also describes the loaded result.
172 Dest.copyMetadata(Source, {LLVMContext::MD_fpmath, LLVMContext::MD_range,
173 LLVMContext::MD_invariant_load});
174}
175
177 const Instruction &Source,
178 const DataLayout &DL,
179 unsigned SourcePtrOperand,
180 std::optional<size_t> ByteOffset) {
182 Dest, Source, DL, SourcePtrOperand, Dest.getPointerOperandIndex(),
183 Dest.getValueOperand()->getType(),
184 Dest.getValueOperand()->getType() == Source.getOperand(0)->getType(),
185 ByteOffset);
186}
187
188// Translate a masked load intrinsic like
189// <16 x i32 > @llvm.masked.load( <16 x i32>* %addr,
190// <16 x i1> %mask, <16 x i32> %passthru)
191// to a chain of basic blocks, with loading element one-by-one if
192// the appropriate mask bit is set
193//
194// %1 = bitcast i8* %addr to i32*
195// %2 = extractelement <16 x i1> %mask, i32 0
196// br i1 %2, label %cond.load, label %else
197//
198// cond.load: ; preds = %0
199// %3 = getelementptr i32* %1, i32 0
200// %4 = load i32* %3
201// %5 = insertelement <16 x i32> %passthru, i32 %4, i32 0
202// br label %else
203//
204// else: ; preds = %0, %cond.load
205// %res.phi.else = phi <16 x i32> [ %5, %cond.load ], [ poison, %0 ]
206// %6 = extractelement <16 x i1> %mask, i32 1
207// br i1 %6, label %cond.load1, label %else2
208//
209// cond.load1: ; preds = %else
210// %7 = getelementptr i32* %1, i32 1
211// %8 = load i32* %7
212// %9 = insertelement <16 x i32> %res.phi.else, i32 %8, i32 1
213// br label %else2
214//
215// else2: ; preds = %else, %cond.load1
216// %res.phi.else3 = phi <16 x i32> [ %9, %cond.load1 ], [ %res.phi.else, %else
217// ] %10 = extractelement <16 x i1> %mask, i32 2 br i1 %10, label %cond.load4,
218// label %else5
219//
220static void scalarizeMaskedLoad(const DataLayout &DL, bool HasBranchDivergence,
221 CallInst *CI, DomTreeUpdater *DTU,
222 bool &ModifiedDT) {
223 Value *Ptr = CI->getArgOperand(0);
224 Value *Mask = CI->getArgOperand(1);
225 Value *Src0 = CI->getArgOperand(2);
226
227 const Align AlignVal = CI->getParamAlign(0).valueOrOne();
228 VectorType *VecType = cast<FixedVectorType>(CI->getType());
229
230 Type *EltTy = VecType->getElementType();
231
232 Instruction *InsertPt = CI;
233 IRBuilder<> Builder(InsertPt);
234 Builder.disableGEPCanonicalization();
235 BasicBlock *IfBlock = CI->getParent();
236
237 // Short-cut if the mask is all-true.
238 if (isa<Constant>(Mask) && cast<Constant>(Mask)->isAllOnesValue()) {
239 LoadInst *NewI = Builder.CreateAlignedLoad(VecType, Ptr, AlignVal);
240 copyMetadataForScalarizedLoad(*NewI, *CI, DL, /*SourcePtrOperand=*/0,
241 std::nullopt);
242 NewI->takeName(CI);
243 CI->replaceAllUsesWith(NewI);
244 CI->eraseFromParent();
245 return;
246 }
247
248 // Adjust alignment for the scalar instruction.
249 const Align AdjustedAlignVal =
250 commonAlignment(AlignVal, EltTy->getPrimitiveSizeInBits() / 8);
251 unsigned VectorWidth = cast<FixedVectorType>(VecType)->getNumElements();
252
253 // The result vector
254 Value *VResult = Src0;
255
256 if (isConstantIntVector(Mask)) {
257 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
258 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
259 continue;
260 Value *Gep = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, Idx);
261 LoadInst *Load = Builder.CreateAlignedLoad(EltTy, Gep, AdjustedAlignVal);
263 *Load, *CI, DL, /*SourcePtrOperand=*/0,
264 Idx * DL.getTypeAllocSize(EltTy).getFixedValue());
265 VResult = Builder.CreateInsertElement(VResult, Load, Idx);
266 }
267 CI->replaceAllUsesWith(VResult);
268 CI->eraseFromParent();
269 return;
270 }
271
272 // Optimize the case where the "masked load" is a predicated load - that is,
273 // where the mask is the splat of a non-constant scalar boolean. In that case,
274 // use that splated value as the guard on a conditional vector load.
275 if (isSplatValue(Mask, /*Index=*/0)) {
276 Value *Predicate = Builder.CreateExtractElement(Mask, uint64_t(0ull),
277 Mask->getName() + ".first");
278 // We mark the branch weights as explicitly unknown given they would only
279 // be derivable from the mask which we do not have VP information for.
280 Instruction *ThenTerm =
281 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
283 *CI->getFunction(), DEBUG_TYPE),
284 DTU);
285
286 BasicBlock *CondBlock = ThenTerm->getParent();
287 CondBlock->setName("cond.load");
288 Builder.SetInsertPoint(CondBlock->getTerminator());
289 LoadInst *Load = Builder.CreateAlignedLoad(VecType, Ptr, AlignVal,
290 CI->getName() + ".cond.load");
291 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
292 std::nullopt);
293
294 BasicBlock *PostLoad = ThenTerm->getSuccessor(0);
295 Builder.SetInsertPoint(PostLoad->begin());
296 PHINode *Phi = Builder.CreatePHI(VecType, /*NumReservedValues=*/2);
297 Phi->addIncoming(Load, CondBlock);
298 Phi->addIncoming(Src0, IfBlock);
299 Phi->takeName(CI);
300
301 CI->replaceAllUsesWith(Phi);
302 CI->eraseFromParent();
303 ModifiedDT = true;
304 return;
305 }
306 // If the mask is not v1i1, use scalar bit test operations. This generates
307 // better results on X86 at least. However, don't do this on GPUs and other
308 // machines with divergence, as there each i1 needs a vector register.
309 Value *SclrMask = nullptr;
310 if (VectorWidth != 1 && !HasBranchDivergence) {
311 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
312 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
313 }
314
315 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
316 // Fill the "else" block, created in the previous iteration
317 //
318 // %res.phi.else3 = phi <16 x i32> [ %11, %cond.load1 ], [ %res.phi.else,
319 // %else ] %mask_1 = and i16 %scalar_mask, i32 1 << Idx %cond = icmp ne i16
320 // %mask_1, 0 br i1 %mask_1, label %cond.load, label %else
321 //
322 // On GPUs, use
323 // %cond = extrectelement %mask, Idx
324 // instead
326 if (SclrMask != nullptr) {
327 Value *Mask = Builder.getInt(APInt::getOneBitSet(
328 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
329 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
330 Builder.getIntN(VectorWidth, 0));
331 } else {
332 Predicate = Builder.CreateExtractElement(Mask, Idx);
333 }
334
335 // Create "cond" block
336 //
337 // %EltAddr = getelementptr i32* %1, i32 0
338 // %Elt = load i32* %EltAddr
339 // VResult = insertelement <16 x i32> VResult, i32 %Elt, i32 Idx
340 //
341 // We mark the branch weights as explicitly unknown given they would only
342 // be derivable from the mask which we do not have VP information for.
343 Instruction *ThenTerm =
344 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
346 *CI->getFunction(), DEBUG_TYPE),
347 DTU);
348
349 BasicBlock *CondBlock = ThenTerm->getParent();
350 CondBlock->setName("cond.load");
351
352 Builder.SetInsertPoint(CondBlock->getTerminator());
353 Value *Gep = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, Idx);
354 LoadInst *Load = Builder.CreateAlignedLoad(EltTy, Gep, AdjustedAlignVal);
356 *Load, *CI, DL, /*SourcePtrOperand=*/0,
357 Idx * DL.getTypeAllocSize(EltTy).getFixedValue());
358 Value *NewVResult = Builder.CreateInsertElement(VResult, Load, Idx);
359
360 // Create "else" block, fill it in the next iteration
361 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
362 NewIfBlock->setName("else");
363 BasicBlock *PrevIfBlock = IfBlock;
364 IfBlock = NewIfBlock;
365
366 // Create the phi to join the new and previous value.
367 Builder.SetInsertPoint(NewIfBlock->begin());
368 PHINode *Phi = Builder.CreatePHI(VecType, 2, "res.phi.else");
369 Phi->addIncoming(NewVResult, CondBlock);
370 Phi->addIncoming(VResult, PrevIfBlock);
371 VResult = Phi;
372 }
373
374 CI->replaceAllUsesWith(VResult);
375 CI->eraseFromParent();
376
377 ModifiedDT = true;
378}
379
380// Translate a masked store intrinsic, like
381// void @llvm.masked.store(<16 x i32> %src, <16 x i32>* %addr,
382// <16 x i1> %mask)
383// to a chain of basic blocks, that stores element one-by-one if
384// the appropriate mask bit is set
385//
386// %1 = bitcast i8* %addr to i32*
387// %2 = extractelement <16 x i1> %mask, i32 0
388// br i1 %2, label %cond.store, label %else
389//
390// cond.store: ; preds = %0
391// %3 = extractelement <16 x i32> %val, i32 0
392// %4 = getelementptr i32* %1, i32 0
393// store i32 %3, i32* %4
394// br label %else
395//
396// else: ; preds = %0, %cond.store
397// %5 = extractelement <16 x i1> %mask, i32 1
398// br i1 %5, label %cond.store1, label %else2
399//
400// cond.store1: ; preds = %else
401// %6 = extractelement <16 x i32> %val, i32 1
402// %7 = getelementptr i32* %1, i32 1
403// store i32 %6, i32* %7
404// br label %else2
405// . . .
406static void scalarizeMaskedStore(const DataLayout &DL, bool HasBranchDivergence,
407 CallInst *CI, DomTreeUpdater *DTU,
408 bool &ModifiedDT) {
409 Value *Src = CI->getArgOperand(0);
410 Value *Ptr = CI->getArgOperand(1);
411 Value *Mask = CI->getArgOperand(2);
412
413 const Align AlignVal = CI->getParamAlign(1).valueOrOne();
414 auto *VecType = cast<VectorType>(Src->getType());
415
416 Type *EltTy = VecType->getElementType();
417
418 Instruction *InsertPt = CI;
419 IRBuilder<> Builder(InsertPt);
420 Builder.disableGEPCanonicalization();
421
422 // Short-cut if the mask is all-true.
423 if (isa<Constant>(Mask) && cast<Constant>(Mask)->isAllOnesValue()) {
424 StoreInst *Store = Builder.CreateAlignedStore(Src, Ptr, AlignVal);
425 Store->takeName(CI);
426 copyMetadataForScalarizedStore(*Store, *CI, DL, /*SourcePtrOperand=*/1,
427 std::nullopt);
428 // This is a one-to-one replacement, so the assignment link remains valid.
429 Store->copyMetadata(*CI, LLVMContext::MD_DIAssignID);
430 CI->eraseFromParent();
431 return;
432 }
433
434 // Adjust alignment for the scalar instruction.
435 const Align AdjustedAlignVal =
436 commonAlignment(AlignVal, EltTy->getPrimitiveSizeInBits() / 8);
437 unsigned VectorWidth = cast<FixedVectorType>(VecType)->getNumElements();
438
439 if (isConstantIntVector(Mask)) {
440 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
441 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
442 continue;
443 Value *OneElt = Builder.CreateExtractElement(Src, Idx);
444 Value *Gep = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, Idx);
446 Builder.CreateAlignedStore(OneElt, Gep, AdjustedAlignVal);
448 *Store, *CI, DL, /*SourcePtrOperand=*/1,
449 Idx * DL.getTypeAllocSize(EltTy).getFixedValue());
450 }
451 CI->eraseFromParent();
452 return;
453 }
454
455 // Optimize the case where the "masked store" is a predicated store - that is,
456 // when the mask is the splat of a non-constant scalar boolean. In that case,
457 // optimize to a conditional store.
458 if (isSplatValue(Mask, /*Index=*/0)) {
459 Value *Predicate = Builder.CreateExtractElement(Mask, uint64_t(0ull),
460 Mask->getName() + ".first");
461 // We mark the branch weights as explicitly unknown given they would only
462 // be derivable from the mask which we do not have VP information for.
463 Instruction *ThenTerm =
464 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
466 *CI->getFunction(), DEBUG_TYPE),
467 DTU);
468 BasicBlock *CondBlock = ThenTerm->getParent();
469 CondBlock->setName("cond.store");
470 Builder.SetInsertPoint(CondBlock->getTerminator());
471
472 StoreInst *Store = Builder.CreateAlignedStore(Src, Ptr, AlignVal);
473 Store->takeName(CI);
474 copyMetadataForScalarizedStore(*Store, *CI, DL, /*SourcePtrOperand=*/1,
475 std::nullopt);
476 // This is a one-to-one replacement, so the assignment link remains valid.
477 Store->copyMetadata(*CI, LLVMContext::MD_DIAssignID);
478
479 CI->eraseFromParent();
480 ModifiedDT = true;
481 return;
482 }
483
484 // If the mask is not v1i1, use scalar bit test operations. This generates
485 // better results on X86 at least. However, don't do this on GPUs or other
486 // machines with branch divergence, as there each i1 takes up a register.
487 Value *SclrMask = nullptr;
488 if (VectorWidth != 1 && !HasBranchDivergence) {
489 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
490 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
491 }
492
493 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
494 // Fill the "else" block, created in the previous iteration
495 //
496 // %mask_1 = and i16 %scalar_mask, i32 1 << Idx
497 // %cond = icmp ne i16 %mask_1, 0
498 // br i1 %mask_1, label %cond.store, label %else
499 //
500 // On GPUs, use
501 // %cond = extrectelement %mask, Idx
502 // instead
504 if (SclrMask != nullptr) {
505 Value *Mask = Builder.getInt(APInt::getOneBitSet(
506 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
507 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
508 Builder.getIntN(VectorWidth, 0));
509 } else {
510 Predicate = Builder.CreateExtractElement(Mask, Idx);
511 }
512
513 // Create "cond" block
514 //
515 // %OneElt = extractelement <16 x i32> %Src, i32 Idx
516 // %EltAddr = getelementptr i32* %1, i32 0
517 // %store i32 %OneElt, i32* %EltAddr
518 //
519 // We mark the branch weights as explicitly unknown given they would only
520 // be derivable from the mask which we do not have VP information for.
521 Instruction *ThenTerm =
522 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
524 *CI->getFunction(), DEBUG_TYPE),
525 DTU);
526
527 BasicBlock *CondBlock = ThenTerm->getParent();
528 CondBlock->setName("cond.store");
529
530 Builder.SetInsertPoint(CondBlock->getTerminator());
531 Value *OneElt = Builder.CreateExtractElement(Src, Idx);
532 Value *Gep = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, Idx);
534 Builder.CreateAlignedStore(OneElt, Gep, AdjustedAlignVal);
536 *Store, *CI, DL, /*SourcePtrOperand=*/1,
537 Idx * DL.getTypeAllocSize(EltTy).getFixedValue());
538
539 // Create "else" block, fill it in the next iteration
540 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
541 NewIfBlock->setName("else");
542
543 Builder.SetInsertPoint(NewIfBlock->begin());
544 }
545 CI->eraseFromParent();
546
547 ModifiedDT = true;
548}
549
550// Translate a masked gather intrinsic like
551// <16 x i32 > @llvm.masked.gather.v16i32( <16 x i32*> %Ptrs, i32 4,
552// <16 x i1> %Mask, <16 x i32> %Src)
553// to a chain of basic blocks, with loading element one-by-one if
554// the appropriate mask bit is set
555//
556// %Ptrs = getelementptr i32, i32* %base, <16 x i64> %ind
557// %Mask0 = extractelement <16 x i1> %Mask, i32 0
558// br i1 %Mask0, label %cond.load, label %else
559//
560// cond.load:
561// %Ptr0 = extractelement <16 x i32*> %Ptrs, i32 0
562// %Load0 = load i32, i32* %Ptr0, align 4
563// %Res0 = insertelement <16 x i32> poison, i32 %Load0, i32 0
564// br label %else
565//
566// else:
567// %res.phi.else = phi <16 x i32>[%Res0, %cond.load], [poison, %0]
568// %Mask1 = extractelement <16 x i1> %Mask, i32 1
569// br i1 %Mask1, label %cond.load1, label %else2
570//
571// cond.load1:
572// %Ptr1 = extractelement <16 x i32*> %Ptrs, i32 1
573// %Load1 = load i32, i32* %Ptr1, align 4
574// %Res1 = insertelement <16 x i32> %res.phi.else, i32 %Load1, i32 1
575// br label %else2
576// . . .
577// %Result = select <16 x i1> %Mask, <16 x i32> %res.phi.select, <16 x i32> %Src
578// ret <16 x i32> %Result
580 bool HasBranchDivergence, CallInst *CI,
581 DomTreeUpdater *DTU, bool &ModifiedDT) {
582 Value *Ptrs = CI->getArgOperand(0);
583 Value *Mask = CI->getArgOperand(1);
584 Value *Src0 = CI->getArgOperand(2);
585
586 auto *VecType = cast<FixedVectorType>(CI->getType());
587 Type *EltTy = VecType->getElementType();
588
589 Instruction *InsertPt = CI;
590 IRBuilder<> Builder(InsertPt);
591 BasicBlock *IfBlock = CI->getParent();
592 Align AlignVal = CI->getParamAlign(0).valueOrOne();
593
594 // The result vector
595 Value *VResult = Src0;
596 unsigned VectorWidth = VecType->getNumElements();
597
598 // Shorten the way if the mask is a vector of constants.
599 if (isConstantIntVector(Mask)) {
600 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
601 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
602 continue;
603 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
604 LoadInst *Load =
605 Builder.CreateAlignedLoad(EltTy, Ptr, AlignVal, "Load" + Twine(Idx));
606 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
607 /*ByteOffset=*/0);
608 VResult =
609 Builder.CreateInsertElement(VResult, Load, Idx, "Res" + Twine(Idx));
610 }
611 CI->replaceAllUsesWith(VResult);
612 CI->eraseFromParent();
613 return;
614 }
615
616 // If the mask is not v1i1, use scalar bit test operations. This generates
617 // better results on X86 at least. However, don't do this on GPUs or other
618 // machines with branch divergence, as there, each i1 takes up a register.
619 Value *SclrMask = nullptr;
620 if (VectorWidth != 1 && !HasBranchDivergence) {
621 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
622 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
623 }
624
625 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
626 // Fill the "else" block, created in the previous iteration
627 //
628 // %Mask1 = and i16 %scalar_mask, i32 1 << Idx
629 // %cond = icmp ne i16 %mask_1, 0
630 // br i1 %Mask1, label %cond.load, label %else
631 //
632 // On GPUs, use
633 // %cond = extrectelement %mask, Idx
634 // instead
635
637 if (SclrMask != nullptr) {
638 Value *Mask = Builder.getInt(APInt::getOneBitSet(
639 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
640 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
641 Builder.getIntN(VectorWidth, 0));
642 } else {
643 Predicate = Builder.CreateExtractElement(Mask, Idx, "Mask" + Twine(Idx));
644 }
645
646 // Create "cond" block
647 //
648 // %EltAddr = getelementptr i32* %1, i32 0
649 // %Elt = load i32* %EltAddr
650 // VResult = insertelement <16 x i32> VResult, i32 %Elt, i32 Idx
651 //
652 // We mark the branch weights as explicitly unknown given they would only
653 // be derivable from the mask which we do not have VP information for.
654 Instruction *ThenTerm =
655 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
657 *CI->getFunction(), DEBUG_TYPE),
658 DTU);
659
660 BasicBlock *CondBlock = ThenTerm->getParent();
661 CondBlock->setName("cond.load");
662
663 Builder.SetInsertPoint(CondBlock->getTerminator());
664 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
665 LoadInst *Load =
666 Builder.CreateAlignedLoad(EltTy, Ptr, AlignVal, "Load" + Twine(Idx));
667 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
668 /*ByteOffset=*/0);
669 Value *NewVResult =
670 Builder.CreateInsertElement(VResult, Load, Idx, "Res" + Twine(Idx));
671
672 // Create "else" block, fill it in the next iteration
673 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
674 NewIfBlock->setName("else");
675 BasicBlock *PrevIfBlock = IfBlock;
676 IfBlock = NewIfBlock;
677
678 // Create the phi to join the new and previous value.
679 Builder.SetInsertPoint(NewIfBlock->begin());
680 PHINode *Phi = Builder.CreatePHI(VecType, 2, "res.phi.else");
681 Phi->addIncoming(NewVResult, CondBlock);
682 Phi->addIncoming(VResult, PrevIfBlock);
683 VResult = Phi;
684 }
685
686 CI->replaceAllUsesWith(VResult);
687 CI->eraseFromParent();
688
689 ModifiedDT = true;
690}
691
692// Translate a masked scatter intrinsic, like
693// void @llvm.masked.scatter.v16i32(<16 x i32> %Src, <16 x i32*>* %Ptrs, i32 4,
694// <16 x i1> %Mask)
695// to a chain of basic blocks, that stores element one-by-one if
696// the appropriate mask bit is set.
697//
698// %Ptrs = getelementptr i32, i32* %ptr, <16 x i64> %ind
699// %Mask0 = extractelement <16 x i1> %Mask, i32 0
700// br i1 %Mask0, label %cond.store, label %else
701//
702// cond.store:
703// %Elt0 = extractelement <16 x i32> %Src, i32 0
704// %Ptr0 = extractelement <16 x i32*> %Ptrs, i32 0
705// store i32 %Elt0, i32* %Ptr0, align 4
706// br label %else
707//
708// else:
709// %Mask1 = extractelement <16 x i1> %Mask, i32 1
710// br i1 %Mask1, label %cond.store1, label %else2
711//
712// cond.store1:
713// %Elt1 = extractelement <16 x i32> %Src, i32 1
714// %Ptr1 = extractelement <16 x i32*> %Ptrs, i32 1
715// store i32 %Elt1, i32* %Ptr1, align 4
716// br label %else2
717// . . .
719 bool HasBranchDivergence, CallInst *CI,
720 DomTreeUpdater *DTU, bool &ModifiedDT) {
721 Value *Src = CI->getArgOperand(0);
722 Value *Ptrs = CI->getArgOperand(1);
723 Value *Mask = CI->getArgOperand(2);
724
725 auto *SrcFVTy = cast<FixedVectorType>(Src->getType());
726
727 assert(
728 isa<VectorType>(Ptrs->getType()) &&
729 isa<PointerType>(cast<VectorType>(Ptrs->getType())->getElementType()) &&
730 "Vector of pointers is expected in masked scatter intrinsic");
731
732 Instruction *InsertPt = CI;
733 IRBuilder<> Builder(InsertPt);
734
735 Align AlignVal = CI->getParamAlign(1).valueOrOne();
736 unsigned VectorWidth = SrcFVTy->getNumElements();
737
738 // Shorten the way if the mask is a vector of constants.
739 if (isConstantIntVector(Mask)) {
740 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
741 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
742 continue;
743 Value *OneElt =
744 Builder.CreateExtractElement(Src, Idx, "Elt" + Twine(Idx));
745 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
746 StoreInst *Store = Builder.CreateAlignedStore(OneElt, Ptr, AlignVal);
748 /*SourcePtrOperand=*/1,
749 /*ByteOffset=*/0);
750 }
751 CI->eraseFromParent();
752 return;
753 }
754
755 // If the mask is not v1i1, use scalar bit test operations. This generates
756 // better results on X86 at least.
757 Value *SclrMask = nullptr;
758 if (VectorWidth != 1 && !HasBranchDivergence) {
759 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
760 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
761 }
762
763 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
764 // Fill the "else" block, created in the previous iteration
765 //
766 // %Mask1 = and i16 %scalar_mask, i32 1 << Idx
767 // %cond = icmp ne i16 %mask_1, 0
768 // br i1 %Mask1, label %cond.store, label %else
769 //
770 // On GPUs, use
771 // %cond = extrectelement %mask, Idx
772 // instead
774 if (SclrMask != nullptr) {
775 Value *Mask = Builder.getInt(APInt::getOneBitSet(
776 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
777 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
778 Builder.getIntN(VectorWidth, 0));
779 } else {
780 Predicate = Builder.CreateExtractElement(Mask, Idx, "Mask" + Twine(Idx));
781 }
782
783 // Create "cond" block
784 //
785 // %Elt1 = extractelement <16 x i32> %Src, i32 1
786 // %Ptr1 = extractelement <16 x i32*> %Ptrs, i32 1
787 // %store i32 %Elt1, i32* %Ptr1
788 //
789 // We mark the branch weights as explicitly unknown given they would only
790 // be derivable from the mask which we do not have VP information for.
791 Instruction *ThenTerm =
792 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
794 *CI->getFunction(), DEBUG_TYPE),
795 DTU);
796
797 BasicBlock *CondBlock = ThenTerm->getParent();
798 CondBlock->setName("cond.store");
799
800 Builder.SetInsertPoint(CondBlock->getTerminator());
801 Value *OneElt = Builder.CreateExtractElement(Src, Idx, "Elt" + Twine(Idx));
802 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
803 StoreInst *Store = Builder.CreateAlignedStore(OneElt, Ptr, AlignVal);
805 /*SourcePtrOperand=*/1,
806 /*ByteOffset=*/0);
807
808 // Create "else" block, fill it in the next iteration
809 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
810 NewIfBlock->setName("else");
811
812 Builder.SetInsertPoint(NewIfBlock->begin());
813 }
814 CI->eraseFromParent();
815
816 ModifiedDT = true;
817}
818
820 bool HasBranchDivergence, CallInst *CI,
821 DomTreeUpdater *DTU, bool &ModifiedDT) {
822 Value *Ptr = CI->getArgOperand(0);
823 Value *Mask = CI->getArgOperand(1);
824 Value *PassThru = CI->getArgOperand(2);
825 Align Alignment = CI->getParamAlign(0).valueOrOne();
826
827 auto *VecType = cast<FixedVectorType>(CI->getType());
828
829 Type *EltTy = VecType->getElementType();
830
831 Instruction *InsertPt = CI;
832 IRBuilder<> Builder(InsertPt);
833 Builder.disableGEPCanonicalization();
834 BasicBlock *IfBlock = CI->getParent();
835
836 unsigned VectorWidth = VecType->getNumElements();
837
838 // The result vector
839 Value *VResult = PassThru;
840
841 // Adjust alignment for the scalar instruction.
842 const Align AdjustedAlignment =
843 commonAlignment(Alignment, EltTy->getPrimitiveSizeInBits() / 8);
844
845 // Shorten the way if the mask is a vector of constants.
846 // Create a build_vector pattern, with loads/poisons as necessary and then
847 // shuffle blend with the pass through value.
848 if (isConstantIntVector(Mask)) {
849 unsigned MemIndex = 0;
850 VResult = PoisonValue::get(VecType);
851 SmallVector<int, 16> ShuffleMask(VectorWidth, PoisonMaskElem);
852 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
853 Value *InsertElt;
854 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue()) {
855 InsertElt = PoisonValue::get(EltTy);
856 ShuffleMask[Idx] = Idx + VectorWidth;
857 } else {
858 Value *NewPtr =
859 Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, MemIndex);
860 LoadInst *Load = Builder.CreateAlignedLoad(
861 EltTy, NewPtr, AdjustedAlignment, "Load" + Twine(Idx));
863 *Load, *CI, DL, /*SourcePtrOperand=*/0,
864 MemIndex * DL.getTypeAllocSize(EltTy).getFixedValue());
865 InsertElt = Load;
866 ShuffleMask[Idx] = Idx;
867 ++MemIndex;
868 }
869 VResult = Builder.CreateInsertElement(VResult, InsertElt, Idx,
870 "Res" + Twine(Idx));
871 }
872 VResult = Builder.CreateShuffleVector(VResult, PassThru, ShuffleMask);
873 CI->replaceAllUsesWith(VResult);
874 CI->eraseFromParent();
875 return;
876 }
877
878 // If the mask is not v1i1, use scalar bit test operations. This generates
879 // better results on X86 at least. However, don't do this on GPUs or other
880 // machines with branch divergence, as there, each i1 takes up a register.
881 Value *SclrMask = nullptr;
882 if (VectorWidth != 1 && !HasBranchDivergence) {
883 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
884 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
885 }
886
887 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
888 // Fill the "else" block, created in the previous iteration
889 //
890 // %res.phi.else3 = phi <16 x i32> [ %11, %cond.load1 ], [ %res.phi.else,
891 // %else ] %mask_1 = extractelement <16 x i1> %mask, i32 Idx br i1 %mask_1,
892 // label %cond.load, label %else
893 //
894 // On GPUs, use
895 // %cond = extrectelement %mask, Idx
896 // instead
897
899 if (SclrMask != nullptr) {
900 Value *Mask = Builder.getInt(APInt::getOneBitSet(
901 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
902 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
903 Builder.getIntN(VectorWidth, 0));
904 } else {
905 Predicate = Builder.CreateExtractElement(Mask, Idx, "Mask" + Twine(Idx));
906 }
907
908 // Create "cond" block
909 //
910 // %EltAddr = getelementptr i32* %1, i32 0
911 // %Elt = load i32* %EltAddr
912 // VResult = insertelement <16 x i32> VResult, i32 %Elt, i32 Idx
913 //
914 // We mark the branch weights as explicitly unknown given they would only
915 // be derivable from the mask which we do not have VP information for.
916 Instruction *ThenTerm =
917 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
919 *CI->getFunction(), DEBUG_TYPE),
920 DTU);
921
922 BasicBlock *CondBlock = ThenTerm->getParent();
923 CondBlock->setName("cond.load");
924
925 Builder.SetInsertPoint(CondBlock->getTerminator());
926 LoadInst *Load = Builder.CreateAlignedLoad(EltTy, Ptr, AdjustedAlignment);
927 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
928 std::nullopt);
929 Value *NewVResult = Builder.CreateInsertElement(VResult, Load, Idx);
930
931 // Move the pointer if there are more blocks to come.
932 Value *NewPtr;
933 if ((Idx + 1) != VectorWidth)
934 NewPtr = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, 1);
935
936 // Create "else" block, fill it in the next iteration
937 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
938 NewIfBlock->setName("else");
939 BasicBlock *PrevIfBlock = IfBlock;
940 IfBlock = NewIfBlock;
941
942 // Create the phi to join the new and previous value.
943 Builder.SetInsertPoint(NewIfBlock->begin());
944 PHINode *ResultPhi = Builder.CreatePHI(VecType, 2, "res.phi.else");
945 ResultPhi->addIncoming(NewVResult, CondBlock);
946 ResultPhi->addIncoming(VResult, PrevIfBlock);
947 VResult = ResultPhi;
948
949 // Add a PHI for the pointer if this isn't the last iteration.
950 if ((Idx + 1) != VectorWidth) {
951 PHINode *PtrPhi = Builder.CreatePHI(Ptr->getType(), 2, "ptr.phi.else");
952 PtrPhi->addIncoming(NewPtr, CondBlock);
953 PtrPhi->addIncoming(Ptr, PrevIfBlock);
954 Ptr = PtrPhi;
955 }
956 }
957
958 CI->replaceAllUsesWith(VResult);
959 CI->eraseFromParent();
960
961 ModifiedDT = true;
962}
963
965 bool HasBranchDivergence, CallInst *CI,
966 DomTreeUpdater *DTU,
967 bool &ModifiedDT) {
968 Value *Src = CI->getArgOperand(0);
969 Value *Ptr = CI->getArgOperand(1);
970 Value *Mask = CI->getArgOperand(2);
971 Align Alignment = CI->getParamAlign(1).valueOrOne();
972
973 auto *VecType = cast<FixedVectorType>(Src->getType());
974
975 Instruction *InsertPt = CI;
976 IRBuilder<> Builder(InsertPt);
977 Builder.disableGEPCanonicalization();
978 BasicBlock *IfBlock = CI->getParent();
979
980 Type *EltTy = VecType->getElementType();
981
982 // Adjust alignment for the scalar instruction.
983 const Align AdjustedAlignment =
984 commonAlignment(Alignment, EltTy->getPrimitiveSizeInBits() / 8);
985
986 unsigned VectorWidth = VecType->getNumElements();
987
988 // Shorten the way if the mask is a vector of constants.
989 if (isConstantIntVector(Mask)) {
990 unsigned MemIndex = 0;
991 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
992 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
993 continue;
994 Value *OneElt =
995 Builder.CreateExtractElement(Src, Idx, "Elt" + Twine(Idx));
996 Value *NewPtr = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, MemIndex);
998 Builder.CreateAlignedStore(OneElt, NewPtr, AdjustedAlignment);
1000 *Store, *CI, DL, /*SourcePtrOperand=*/1,
1001 MemIndex * DL.getTypeAllocSize(EltTy).getFixedValue());
1002 ++MemIndex;
1003 }
1004 CI->eraseFromParent();
1005 return;
1006 }
1007
1008 // If the mask is not v1i1, use scalar bit test operations. This generates
1009 // better results on X86 at least. However, don't do this on GPUs or other
1010 // machines with branch divergence, as there, each i1 takes up a register.
1011 Value *SclrMask = nullptr;
1012 if (VectorWidth != 1 && !HasBranchDivergence) {
1013 Type *SclrMaskTy = Builder.getIntNTy(VectorWidth);
1014 SclrMask = Builder.CreateBitCast(Mask, SclrMaskTy, "scalar_mask");
1015 }
1016
1017 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
1018 // Fill the "else" block, created in the previous iteration
1019 //
1020 // %mask_1 = extractelement <16 x i1> %mask, i32 Idx
1021 // br i1 %mask_1, label %cond.store, label %else
1022 //
1023 // On GPUs, use
1024 // %cond = extrectelement %mask, Idx
1025 // instead
1027 if (SclrMask != nullptr) {
1028 Value *Mask = Builder.getInt(APInt::getOneBitSet(
1029 VectorWidth, adjustForEndian(DL, VectorWidth, Idx)));
1030 Predicate = Builder.CreateICmpNE(Builder.CreateAnd(SclrMask, Mask),
1031 Builder.getIntN(VectorWidth, 0));
1032 } else {
1033 Predicate = Builder.CreateExtractElement(Mask, Idx, "Mask" + Twine(Idx));
1034 }
1035
1036 // Create "cond" block
1037 //
1038 // %OneElt = extractelement <16 x i32> %Src, i32 Idx
1039 // %EltAddr = getelementptr i32* %1, i32 0
1040 // %store i32 %OneElt, i32* %EltAddr
1041 //
1042 // We mark the branch weights as explicitly unknown given they would only
1043 // be derivable from the mask which we do not have VP information for.
1044 Instruction *ThenTerm =
1045 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
1047 *CI->getFunction(), DEBUG_TYPE),
1048 DTU);
1049
1050 BasicBlock *CondBlock = ThenTerm->getParent();
1051 CondBlock->setName("cond.store");
1052
1053 Builder.SetInsertPoint(CondBlock->getTerminator());
1054 Value *OneElt = Builder.CreateExtractElement(Src, Idx);
1055 StoreInst *Store =
1056 Builder.CreateAlignedStore(OneElt, Ptr, AdjustedAlignment);
1057 copyMetadataForScalarizedStore(*Store, *CI, DL, /*SourcePtrOperand=*/1,
1058 std::nullopt);
1059
1060 // Move the pointer if there are more blocks to come.
1061 Value *NewPtr;
1062 if ((Idx + 1) != VectorWidth)
1063 NewPtr = Builder.CreateConstInBoundsGEP1_32(EltTy, Ptr, 1);
1064
1065 // Create "else" block, fill it in the next iteration
1066 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
1067 NewIfBlock->setName("else");
1068 BasicBlock *PrevIfBlock = IfBlock;
1069 IfBlock = NewIfBlock;
1070
1071 Builder.SetInsertPoint(NewIfBlock->begin());
1072
1073 // Add a PHI for the pointer if this isn't the last iteration.
1074 if ((Idx + 1) != VectorWidth) {
1075 PHINode *PtrPhi = Builder.CreatePHI(Ptr->getType(), 2, "ptr.phi.else");
1076 PtrPhi->addIncoming(NewPtr, CondBlock);
1077 PtrPhi->addIncoming(Ptr, PrevIfBlock);
1078 Ptr = PtrPhi;
1079 }
1080 }
1081 CI->eraseFromParent();
1082
1083 ModifiedDT = true;
1084}
1085
1087 DomTreeUpdater *DTU,
1088 bool &ModifiedDT) {
1089 // If we extend histogram to return a result someday (like the updated vector)
1090 // then we'll need to support it here.
1091 assert(CI->getType()->isVoidTy() && "Histogram with non-void return.");
1092 Value *Ptrs = CI->getArgOperand(0);
1093 Value *Inc = CI->getArgOperand(1);
1094 Value *Mask = CI->getArgOperand(2);
1095
1096 auto *AddrType = cast<FixedVectorType>(Ptrs->getType());
1097 Type *EltTy = Inc->getType();
1098
1099 Instruction *InsertPt = CI;
1100 IRBuilder<> Builder(InsertPt);
1101
1102 // FIXME: Do we need to add an alignment parameter to the intrinsic?
1103 unsigned VectorWidth = AddrType->getNumElements();
1104 auto CreateHistogramUpdateValue = [&](IntrinsicInst *CI, Value *Load,
1105 Value *Inc) -> Value * {
1106 Value *UpdateOp;
1107 switch (CI->getIntrinsicID()) {
1108 case Intrinsic::experimental_vector_histogram_add:
1109 UpdateOp = Builder.CreateAdd(Load, Inc);
1110 break;
1111 case Intrinsic::experimental_vector_histogram_uadd_sat:
1112 UpdateOp =
1113 Builder.CreateIntrinsic(Intrinsic::uadd_sat, {EltTy}, {Load, Inc});
1114 break;
1115 case Intrinsic::experimental_vector_histogram_umin:
1116 UpdateOp = Builder.CreateIntrinsic(Intrinsic::umin, {EltTy}, {Load, Inc});
1117 break;
1118 case Intrinsic::experimental_vector_histogram_umax:
1119 UpdateOp = Builder.CreateIntrinsic(Intrinsic::umax, {EltTy}, {Load, Inc});
1120 break;
1121
1122 default:
1123 llvm_unreachable("Unexpected histogram intrinsic");
1124 }
1125 return UpdateOp;
1126 };
1127
1128 // Shorten the way if the mask is a vector of constants.
1129 if (isConstantIntVector(Mask)) {
1130 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
1131 if (cast<Constant>(Mask)->getAggregateElement(Idx)->isNullValue())
1132 continue;
1133 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
1134 LoadInst *Load = Builder.CreateLoad(EltTy, Ptr, "Load" + Twine(Idx));
1135 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
1136 /*ByteOffset=*/0);
1137 Value *Update =
1138 CreateHistogramUpdateValue(cast<IntrinsicInst>(CI), Load, Inc);
1139 StoreInst *Store = Builder.CreateStore(Update, Ptr);
1141 /*SourcePtrOperand=*/0,
1142 /*ByteOffset=*/0);
1143 }
1144 CI->eraseFromParent();
1145 return;
1146 }
1147
1148 for (unsigned Idx = 0; Idx < VectorWidth; ++Idx) {
1149 Value *Predicate =
1150 Builder.CreateExtractElement(Mask, Idx, "Mask" + Twine(Idx));
1151
1152 // We mark the branch weights as explicitly unknown given they would only
1153 // be derivable from the mask which we do not have VP information for.
1154 Instruction *ThenTerm =
1155 SplitBlockAndInsertIfThen(Predicate, InsertPt, /*Unreachable=*/false,
1157 *CI->getFunction(), DEBUG_TYPE),
1158 DTU);
1159
1160 BasicBlock *CondBlock = ThenTerm->getParent();
1161 CondBlock->setName("cond.histogram.update");
1162
1163 Builder.SetInsertPoint(CondBlock->getTerminator());
1164 Value *Ptr = Builder.CreateExtractElement(Ptrs, Idx, "Ptr" + Twine(Idx));
1165 LoadInst *Load = Builder.CreateLoad(EltTy, Ptr, "Load" + Twine(Idx));
1166 copyMetadataForScalarizedLoad(*Load, *CI, DL, /*SourcePtrOperand=*/0,
1167 /*ByteOffset=*/0);
1168 Value *UpdateOp =
1169 CreateHistogramUpdateValue(cast<IntrinsicInst>(CI), Load, Inc);
1170 StoreInst *Store = Builder.CreateStore(UpdateOp, Ptr);
1172 /*SourcePtrOperand=*/0,
1173 /*ByteOffset=*/0);
1174
1175 // Create "else" block, fill it in the next iteration
1176 BasicBlock *NewIfBlock = ThenTerm->getSuccessor(0);
1177 NewIfBlock->setName("else");
1178 Builder.SetInsertPoint(NewIfBlock->begin());
1179 }
1180
1181 CI->eraseFromParent();
1182 ModifiedDT = true;
1183}
1184
1186 DominatorTree *DT) {
1187 std::optional<DomTreeUpdater> DTU;
1188 if (DT)
1189 DTU.emplace(DT, DomTreeUpdater::UpdateStrategy::Lazy);
1190
1191 bool EverMadeChange = false;
1192 bool MadeChange = true;
1193 auto &DL = F.getDataLayout();
1194 bool HasBranchDivergence = TTI.hasBranchDivergence(&F);
1195 while (MadeChange) {
1196 MadeChange = false;
1198 bool ModifiedDTOnIteration = false;
1199 MadeChange |= optimizeBlock(BB, ModifiedDTOnIteration, TTI, DL,
1200 HasBranchDivergence, DTU ? &*DTU : nullptr);
1201
1202 // Restart BB iteration if the dominator tree of the Function was changed
1203 if (ModifiedDTOnIteration)
1204 break;
1205 }
1206
1207 EverMadeChange |= MadeChange;
1208 }
1209 return EverMadeChange;
1210}
1211
1212bool ScalarizeMaskedMemIntrinLegacyPass::runOnFunction(Function &F) {
1213 auto &TTI = getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
1214 DominatorTree *DT = nullptr;
1215 if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
1216 DT = &DTWP->getDomTree();
1217 return runImpl(F, TTI, DT);
1218}
1219
1220PreservedAnalyses
1231
1232static bool optimizeBlock(BasicBlock &BB, bool &ModifiedDT,
1233 const TargetTransformInfo &TTI, const DataLayout &DL,
1234 bool HasBranchDivergence, DomTreeUpdater *DTU) {
1235 bool MadeChange = false;
1236
1237 BasicBlock::iterator CurInstIterator = BB.begin();
1238 while (CurInstIterator != BB.end()) {
1239 if (CallInst *CI = dyn_cast<CallInst>(&*CurInstIterator++))
1240 MadeChange |=
1241 optimizeCallInst(CI, ModifiedDT, TTI, DL, HasBranchDivergence, DTU);
1242 if (ModifiedDT)
1243 return true;
1244 }
1245
1246 return MadeChange;
1247}
1248
1249static bool optimizeCallInst(CallInst *CI, bool &ModifiedDT,
1250 const TargetTransformInfo &TTI,
1251 const DataLayout &DL, bool HasBranchDivergence,
1252 DomTreeUpdater *DTU) {
1254 if (II) {
1255 // The scalarization code below does not work for scalable vectors.
1256 if (isa<ScalableVectorType>(II->getType()) ||
1257 any_of(II->args(),
1258 [](Value *V) { return isa<ScalableVectorType>(V->getType()); }))
1259 return false;
1260 switch (II->getIntrinsicID()) {
1261 default:
1262 break;
1263 case Intrinsic::experimental_vector_histogram_add:
1264 case Intrinsic::experimental_vector_histogram_uadd_sat:
1265 case Intrinsic::experimental_vector_histogram_umin:
1266 case Intrinsic::experimental_vector_histogram_umax:
1267 if (TTI.isLegalMaskedVectorHistogram(CI->getArgOperand(0)->getType(),
1268 CI->getArgOperand(1)->getType()))
1269 return false;
1270 scalarizeMaskedVectorHistogram(DL, CI, DTU, ModifiedDT);
1271 return true;
1272 case Intrinsic::masked_load:
1273 // Scalarize unsupported vector masked load
1274 if (TTI.isLegalMaskedLoad(
1275 CI->getType(), CI->getParamAlign(0).valueOrOne(),
1277 ->getAddressSpace(),
1281 return false;
1282 scalarizeMaskedLoad(DL, HasBranchDivergence, CI, DTU, ModifiedDT);
1283 return true;
1284 case Intrinsic::masked_store:
1285 if (TTI.isLegalMaskedStore(
1286 CI->getArgOperand(0)->getType(),
1287 CI->getParamAlign(1).valueOrOne(),
1289 ->getAddressSpace(),
1293 return false;
1294 scalarizeMaskedStore(DL, HasBranchDivergence, CI, DTU, ModifiedDT);
1295 return true;
1296 case Intrinsic::masked_gather: {
1297 Align Alignment = CI->getParamAlign(0).valueOrOne();
1298 Type *LoadTy = CI->getType();
1299 if (TTI.isLegalMaskedGather(LoadTy, Alignment) &&
1300 !TTI.forceScalarizeMaskedGather(cast<VectorType>(LoadTy), Alignment))
1301 return false;
1302 scalarizeMaskedGather(DL, HasBranchDivergence, CI, DTU, ModifiedDT);
1303 return true;
1304 }
1305 case Intrinsic::masked_scatter: {
1306 Align Alignment = CI->getParamAlign(1).valueOrOne();
1307 Type *StoreTy = CI->getArgOperand(0)->getType();
1308 if (TTI.isLegalMaskedScatter(StoreTy, Alignment) &&
1309 !TTI.forceScalarizeMaskedScatter(cast<VectorType>(StoreTy),
1310 Alignment))
1311 return false;
1312 scalarizeMaskedScatter(DL, HasBranchDivergence, CI, DTU, ModifiedDT);
1313 return true;
1314 }
1315 case Intrinsic::masked_expandload:
1316 if (TTI.isLegalMaskedExpandLoad(
1317 CI->getType(),
1318 CI->getAttributes().getParamAttrs(0).getAlignment().valueOrOne()))
1319 return false;
1320 scalarizeMaskedExpandLoad(DL, HasBranchDivergence, CI, DTU, ModifiedDT);
1321 return true;
1322 case Intrinsic::masked_compressstore:
1323 if (TTI.isLegalMaskedCompressStore(
1324 CI->getArgOperand(0)->getType(),
1325 CI->getAttributes().getParamAttrs(1).getAlignment().valueOrOne()))
1326 return false;
1327 scalarizeMaskedCompressStore(DL, HasBranchDivergence, CI, DTU,
1328 ModifiedDT);
1329 return true;
1330 }
1331 }
1332
1333 return false;
1334}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static bool runImpl(MachineFunction &MF)
Definition CFIFixup.cpp:304
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
#define F(x, y, z)
Definition MD5.cpp:54
This file contains the declarations for metadata subclasses.
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file contains the declarations for profiling metadata utility functions.
static void scalarizeMaskedExpandLoad(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static void scalarizeMaskedVectorHistogram(const DataLayout &DL, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static void copyMemCacheHint(Instruction &Dest, const Instruction &Source, unsigned SourcePtrOperand, unsigned DestPtrOperand)
static void copyMetadataForScalarizedStore(StoreInst &Dest, const Instruction &Source, const DataLayout &DL, unsigned SourcePtrOperand, std::optional< size_t > ByteOffset)
static bool optimizeBlock(BasicBlock &BB, bool &ModifiedDT, const TargetTransformInfo &TTI, const DataLayout &DL, bool HasBranchDivergence, DomTreeUpdater *DTU)
static void scalarizeMaskedScatter(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static unsigned adjustForEndian(const DataLayout &DL, unsigned VectorWidth, unsigned Idx)
static bool optimizeCallInst(CallInst *CI, bool &ModifiedDT, const TargetTransformInfo &TTI, const DataLayout &DL, bool HasBranchDivergence, DomTreeUpdater *DTU)
static void copyMetadataForScalarizedLoad(LoadInst &Dest, const Instruction &Source, const DataLayout &DL, unsigned SourcePtrOperand, std::optional< size_t > ByteOffset)
static void scalarizeMaskedStore(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static void scalarizeMaskedCompressStore(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static void scalarizeMaskedGather(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
static void copyMetadataForMemoryAccess(Instruction &Dest, const Instruction &Source, const DataLayout &DL, unsigned SourcePtrOperand, unsigned DestPtrOperand, Type *AccessType, bool IsWholeAccess, std::optional< size_t > ByteOffset)
static bool runImpl(Function &F, const TargetTransformInfo &TTI, DominatorTree *DT)
static bool isConstantIntVector(Value *Mask)
static void scalarizeMaskedLoad(const DataLayout &DL, bool HasBranchDivergence, CallInst *CI, DomTreeUpdater *DTU, bool &ModifiedDT)
This pass exposes codegen information to IR-level passes.
static APInt getOneBitSet(unsigned numBits, unsigned BitNo)
Return an APInt with exactly one bit set in the result.
Definition APInt.h:235
PassT::Result * getCachedResult(IRUnitT &IR) const
Get the cached result of an analysis pass for a given IR unit.
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
iterator end()
Definition BasicBlock.h:453
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:440
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
MaybeAlign getParamAlign(unsigned ArgNo) const
Extract the alignment for a call or parameter (0=unknown).
Value * getArgOperand(unsigned i) const
LLVM_ABI Intrinsic::ID getIntrinsicID() const
Returns the intrinsic ID of the intrinsic called or Intrinsic::not_intrinsic if the called function i...
AttributeList getAttributes() const
Return the attributes for this call.
This class represents a function call, abstracting a target machine's calling convention.
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:548
This is an important base class in LLVM.
Definition Constant.h:44
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
Legacy analysis pass which computes a DominatorTree.
Definition Dominators.h:277
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2987
LLVM_ABI void setAAMetadata(const AAMDNodes &N)
Sets the AA metadata on this instruction from the AAMDNodes structure.
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
LLVM_ABI void copyMetadata(const Instruction &SrcInst, ArrayRef< unsigned > WL=ArrayRef< unsigned >())
Copy metadata from SrcInst to this instruction.
A wrapper class for inspecting calls to intrinsic functions.
An instruction for reading from memory.
static unsigned getPointerOperandIndex()
Metadata node.
Definition Metadata.h:1081
const MDOperand & getOperand(unsigned I) const
Definition Metadata.h:1437
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
unsigned getNumOperands() const
Return number of MDNode operands.
Definition Metadata.h:1443
Root of the metadata hierarchy.
Definition Metadata.h:64
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
Value * getValueOperand()
static unsigned getPointerOperandIndex()
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Analysis pass providing the TargetTransformInfo.
Wrapper pass for TargetTransformInfo.
This pass provides access to the codegen interfaces that are needed for IR-level transformations.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
LLVM_ABI TypeSize getPrimitiveSizeInBits() const LLVM_READONLY
Return the basic size of this type if it is a primitive type.
Definition Type.cpp:187
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:303
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:389
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:548
LLVMContext & getContext() const
All values hold a context through their type.
Definition Value.h:260
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:314
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:395
const ParentTy * getParent() const
Definition ilist_node.h:34
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > extract(Y &&MD)
Extract a Value from Metadata.
Definition Metadata.h:679
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
LLVM_ABI FunctionPass * createScalarizeMaskedMemIntrinLegacyPass()
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1762
LLVM_ABI bool isSplatValue(const Value *V, int Index=-1, unsigned Depth=0)
Return true if each element of the vector value V is poisoned or equal to every other non-poisoned el...
LLVM_ABI void initializeScalarizeMaskedMemIntrinLegacyPassPass(PassRegistry &)
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
LLVM_ABI MDNode * getExplicitlyUnknownBranchWeightsIfProfiled(Function &F, StringRef PassName)
Returns a metadata node containing unknown branch weights if the function has an entry count,...
constexpr int PoisonMaskElem
TargetTransformInfo TTI
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
Align commonAlignment(Align A, uint64_t Offset)
Returns the alignment that satisfies both alignments.
Definition Alignment.h:206
LLVM_ABI Instruction * SplitBlockAndInsertIfThen(Value *Cond, BasicBlock::iterator SplitBefore, bool Unreachable, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, BasicBlock *ThenBlock=nullptr)
Split the containing block at the specified instruction - everything before SplitBefore stays in the ...
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
A collection of metadata nodes that might be associated with a memory access used by the alias-analys...
Definition Metadata.h:774
MDNode * TBAAStruct
The tag for type-based alias analysis (tbaa struct).
Definition Metadata.h:794
LLVM_ABI AAMDNodes adjustForAccess(unsigned AccessSize)
Create a new AAMDNode for accessing AccessSize bytes of this AAMDNode.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
Align valueOrOne() const
For convenience, returns a valid alignment or 1 if undefined.
Definition Alignment.h:135
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)