xref: /src/contrib/llvm-project/llvm/lib/CodeGen/ExpandReductions.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
17fa27ce4SDimitry Andric //===- ExpandReductions.cpp - Expand reduction intrinsics -----------------===//
26b3f41edSDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66b3f41edSDimitry Andric //
76b3f41edSDimitry Andric //===----------------------------------------------------------------------===//
86b3f41edSDimitry Andric //
96b3f41edSDimitry Andric // This pass implements IR expansion for reduction intrinsics, allowing targets
10b60736ecSDimitry Andric // to enable the intrinsics until just before codegen.
116b3f41edSDimitry Andric //
126b3f41edSDimitry Andric //===----------------------------------------------------------------------===//
136b3f41edSDimitry Andric 
146b3f41edSDimitry Andric #include "llvm/CodeGen/ExpandReductions.h"
157ab83427SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h"
166b3f41edSDimitry Andric #include "llvm/CodeGen/Passes.h"
176b3f41edSDimitry Andric #include "llvm/IR/IRBuilder.h"
186b3f41edSDimitry Andric #include "llvm/IR/InstIterator.h"
196b3f41edSDimitry Andric #include "llvm/IR/IntrinsicInst.h"
207ab83427SDimitry Andric #include "llvm/IR/Intrinsics.h"
21706b4fc4SDimitry Andric #include "llvm/InitializePasses.h"
226b3f41edSDimitry Andric #include "llvm/Pass.h"
237ab83427SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h"
246b3f41edSDimitry Andric 
256b3f41edSDimitry Andric using namespace llvm;
266b3f41edSDimitry Andric 
276b3f41edSDimitry Andric namespace {
286b3f41edSDimitry Andric 
expandReductions(Function & F,const TargetTransformInfo * TTI)296b3f41edSDimitry Andric bool expandReductions(Function &F, const TargetTransformInfo *TTI) {
306b3f41edSDimitry Andric   bool Changed = false;
316b3f41edSDimitry Andric   SmallVector<IntrinsicInst *, 4> Worklist;
32706b4fc4SDimitry Andric   for (auto &I : instructions(F)) {
33706b4fc4SDimitry Andric     if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
34706b4fc4SDimitry Andric       switch (II->getIntrinsicID()) {
35706b4fc4SDimitry Andric       default: break;
36b60736ecSDimitry Andric       case Intrinsic::vector_reduce_fadd:
37b60736ecSDimitry Andric       case Intrinsic::vector_reduce_fmul:
38b60736ecSDimitry Andric       case Intrinsic::vector_reduce_add:
39b60736ecSDimitry Andric       case Intrinsic::vector_reduce_mul:
40b60736ecSDimitry Andric       case Intrinsic::vector_reduce_and:
41b60736ecSDimitry Andric       case Intrinsic::vector_reduce_or:
42b60736ecSDimitry Andric       case Intrinsic::vector_reduce_xor:
43b60736ecSDimitry Andric       case Intrinsic::vector_reduce_smax:
44b60736ecSDimitry Andric       case Intrinsic::vector_reduce_smin:
45b60736ecSDimitry Andric       case Intrinsic::vector_reduce_umax:
46b60736ecSDimitry Andric       case Intrinsic::vector_reduce_umin:
47b60736ecSDimitry Andric       case Intrinsic::vector_reduce_fmax:
48b60736ecSDimitry Andric       case Intrinsic::vector_reduce_fmin:
49706b4fc4SDimitry Andric         if (TTI->shouldExpandReduction(II))
506b3f41edSDimitry Andric           Worklist.push_back(II);
516b3f41edSDimitry Andric 
52706b4fc4SDimitry Andric         break;
53706b4fc4SDimitry Andric       }
54706b4fc4SDimitry Andric     }
55706b4fc4SDimitry Andric   }
56e6d15924SDimitry Andric 
57706b4fc4SDimitry Andric   for (auto *II : Worklist) {
58e6d15924SDimitry Andric     FastMathFlags FMF =
59e6d15924SDimitry Andric         isa<FPMathOperator>(II) ? II->getFastMathFlags() : FastMathFlags{};
60e6d15924SDimitry Andric     Intrinsic::ID ID = II->getIntrinsicID();
61ac9a064cSDimitry Andric     RecurKind RK = getMinMaxReductionRecurKind(ID);
62ac9a064cSDimitry Andric     TargetTransformInfo::ReductionShuffle RS =
63ac9a064cSDimitry Andric         TTI->getPreferredExpandedReductionShuffle(II);
64e6d15924SDimitry Andric 
65e6d15924SDimitry Andric     Value *Rdx = nullptr;
666b3f41edSDimitry Andric     IRBuilder<> Builder(II);
67e6d15924SDimitry Andric     IRBuilder<>::FastMathFlagGuard FMFGuard(Builder);
68e6d15924SDimitry Andric     Builder.setFastMathFlags(FMF);
696b3f41edSDimitry Andric     switch (ID) {
70706b4fc4SDimitry Andric     default: llvm_unreachable("Unexpected intrinsic!");
71b60736ecSDimitry Andric     case Intrinsic::vector_reduce_fadd:
72b60736ecSDimitry Andric     case Intrinsic::vector_reduce_fmul: {
736b3f41edSDimitry Andric       // FMFs must be attached to the call, otherwise it's an ordered reduction
74eb11fae6SDimitry Andric       // and it can't be handled by generating a shuffle sequence.
75e6d15924SDimitry Andric       Value *Acc = II->getArgOperand(0);
76e6d15924SDimitry Andric       Value *Vec = II->getArgOperand(1);
77ac9a064cSDimitry Andric       unsigned RdxOpcode = getArithmeticReductionInstruction(ID);
78e6d15924SDimitry Andric       if (!FMF.allowReassoc())
79ac9a064cSDimitry Andric         Rdx = getOrderedReduction(Builder, Acc, Vec, RdxOpcode, RK);
80e6d15924SDimitry Andric       else {
81cfca06d7SDimitry Andric         if (!isPowerOf2_32(
82cfca06d7SDimitry Andric                 cast<FixedVectorType>(Vec->getType())->getNumElements()))
83706b4fc4SDimitry Andric           continue;
84ac9a064cSDimitry Andric         Rdx = getShuffleReduction(Builder, Vec, RdxOpcode, RS, RK);
85ac9a064cSDimitry Andric         Rdx = Builder.CreateBinOp((Instruction::BinaryOps)RdxOpcode, Acc, Rdx,
86ac9a064cSDimitry Andric                                   "bin.rdx");
87e6d15924SDimitry Andric       }
88706b4fc4SDimitry Andric       break;
89706b4fc4SDimitry Andric     }
907fa27ce4SDimitry Andric     case Intrinsic::vector_reduce_and:
917fa27ce4SDimitry Andric     case Intrinsic::vector_reduce_or: {
927fa27ce4SDimitry Andric       // Canonicalize logical or/and reductions:
937fa27ce4SDimitry Andric       // Or reduction for i1 is represented as:
947fa27ce4SDimitry Andric       // %val = bitcast <ReduxWidth x i1> to iReduxWidth
957fa27ce4SDimitry Andric       // %res = cmp ne iReduxWidth %val, 0
967fa27ce4SDimitry Andric       // And reduction for i1 is represented as:
977fa27ce4SDimitry Andric       // %val = bitcast <ReduxWidth x i1> to iReduxWidth
987fa27ce4SDimitry Andric       // %res = cmp eq iReduxWidth %val, 11111
997fa27ce4SDimitry Andric       Value *Vec = II->getArgOperand(0);
1007fa27ce4SDimitry Andric       auto *FTy = cast<FixedVectorType>(Vec->getType());
1017fa27ce4SDimitry Andric       unsigned NumElts = FTy->getNumElements();
1027fa27ce4SDimitry Andric       if (!isPowerOf2_32(NumElts))
1037fa27ce4SDimitry Andric         continue;
1047fa27ce4SDimitry Andric 
1057fa27ce4SDimitry Andric       if (FTy->getElementType() == Builder.getInt1Ty()) {
1067fa27ce4SDimitry Andric         Rdx = Builder.CreateBitCast(Vec, Builder.getIntNTy(NumElts));
1077fa27ce4SDimitry Andric         if (ID == Intrinsic::vector_reduce_and) {
1087fa27ce4SDimitry Andric           Rdx = Builder.CreateICmpEQ(
1097fa27ce4SDimitry Andric               Rdx, ConstantInt::getAllOnesValue(Rdx->getType()));
1107fa27ce4SDimitry Andric         } else {
1117fa27ce4SDimitry Andric           assert(ID == Intrinsic::vector_reduce_or && "Expected or reduction.");
1127fa27ce4SDimitry Andric           Rdx = Builder.CreateIsNotNull(Rdx);
1137fa27ce4SDimitry Andric         }
1147fa27ce4SDimitry Andric         break;
1157fa27ce4SDimitry Andric       }
116ac9a064cSDimitry Andric       unsigned RdxOpcode = getArithmeticReductionInstruction(ID);
117ac9a064cSDimitry Andric       Rdx = getShuffleReduction(Builder, Vec, RdxOpcode, RS, RK);
1187fa27ce4SDimitry Andric       break;
1197fa27ce4SDimitry Andric     }
120b60736ecSDimitry Andric     case Intrinsic::vector_reduce_add:
121b60736ecSDimitry Andric     case Intrinsic::vector_reduce_mul:
122b60736ecSDimitry Andric     case Intrinsic::vector_reduce_xor:
123b60736ecSDimitry Andric     case Intrinsic::vector_reduce_smax:
124b60736ecSDimitry Andric     case Intrinsic::vector_reduce_smin:
125b60736ecSDimitry Andric     case Intrinsic::vector_reduce_umax:
126b60736ecSDimitry Andric     case Intrinsic::vector_reduce_umin: {
127e6d15924SDimitry Andric       Value *Vec = II->getArgOperand(0);
128cfca06d7SDimitry Andric       if (!isPowerOf2_32(
129cfca06d7SDimitry Andric               cast<FixedVectorType>(Vec->getType())->getNumElements()))
1306b3f41edSDimitry Andric         continue;
131ac9a064cSDimitry Andric       unsigned RdxOpcode = getArithmeticReductionInstruction(ID);
132ac9a064cSDimitry Andric       Rdx = getShuffleReduction(Builder, Vec, RdxOpcode, RS, RK);
133b60736ecSDimitry Andric       break;
134b60736ecSDimitry Andric     }
135b60736ecSDimitry Andric     case Intrinsic::vector_reduce_fmax:
136b60736ecSDimitry Andric     case Intrinsic::vector_reduce_fmin: {
137344a3780SDimitry Andric       // We require "nnan" to use a shuffle reduction; "nsz" is implied by the
138344a3780SDimitry Andric       // semantics of the reduction.
139b60736ecSDimitry Andric       Value *Vec = II->getArgOperand(0);
140b60736ecSDimitry Andric       if (!isPowerOf2_32(
141b60736ecSDimitry Andric               cast<FixedVectorType>(Vec->getType())->getNumElements()) ||
142344a3780SDimitry Andric           !FMF.noNaNs())
143b60736ecSDimitry Andric         continue;
144ac9a064cSDimitry Andric       unsigned RdxOpcode = getArithmeticReductionInstruction(ID);
145ac9a064cSDimitry Andric       Rdx = getShuffleReduction(Builder, Vec, RdxOpcode, RS, RK);
146706b4fc4SDimitry Andric       break;
147706b4fc4SDimitry Andric     }
1486b3f41edSDimitry Andric     }
1496b3f41edSDimitry Andric     II->replaceAllUsesWith(Rdx);
1506b3f41edSDimitry Andric     II->eraseFromParent();
1516b3f41edSDimitry Andric     Changed = true;
1526b3f41edSDimitry Andric   }
1536b3f41edSDimitry Andric   return Changed;
1546b3f41edSDimitry Andric }
1556b3f41edSDimitry Andric 
1566b3f41edSDimitry Andric class ExpandReductions : public FunctionPass {
1576b3f41edSDimitry Andric public:
1586b3f41edSDimitry Andric   static char ID;
ExpandReductions()1596b3f41edSDimitry Andric   ExpandReductions() : FunctionPass(ID) {
1606b3f41edSDimitry Andric     initializeExpandReductionsPass(*PassRegistry::getPassRegistry());
1616b3f41edSDimitry Andric   }
1626b3f41edSDimitry Andric 
runOnFunction(Function & F)1636b3f41edSDimitry Andric   bool runOnFunction(Function &F) override {
1646b3f41edSDimitry Andric     const auto *TTI =&getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
1656b3f41edSDimitry Andric     return expandReductions(F, TTI);
1666b3f41edSDimitry Andric   }
1676b3f41edSDimitry Andric 
getAnalysisUsage(AnalysisUsage & AU) const1686b3f41edSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
1696b3f41edSDimitry Andric     AU.addRequired<TargetTransformInfoWrapperPass>();
1706b3f41edSDimitry Andric     AU.setPreservesCFG();
1716b3f41edSDimitry Andric   }
1726b3f41edSDimitry Andric };
1736b3f41edSDimitry Andric }
1746b3f41edSDimitry Andric 
1756b3f41edSDimitry Andric char ExpandReductions::ID;
1766b3f41edSDimitry Andric INITIALIZE_PASS_BEGIN(ExpandReductions, "expand-reductions",
1776b3f41edSDimitry Andric                       "Expand reduction intrinsics", false, false)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)1786b3f41edSDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
1796b3f41edSDimitry Andric INITIALIZE_PASS_END(ExpandReductions, "expand-reductions",
1806b3f41edSDimitry Andric                     "Expand reduction intrinsics", false, false)
1816b3f41edSDimitry Andric 
1826b3f41edSDimitry Andric FunctionPass *llvm::createExpandReductionsPass() {
1836b3f41edSDimitry Andric   return new ExpandReductions();
1846b3f41edSDimitry Andric }
1856b3f41edSDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)1866b3f41edSDimitry Andric PreservedAnalyses ExpandReductionsPass::run(Function &F,
1876b3f41edSDimitry Andric                                             FunctionAnalysisManager &AM) {
1886b3f41edSDimitry Andric   const auto &TTI = AM.getResult<TargetIRAnalysis>(F);
1896b3f41edSDimitry Andric   if (!expandReductions(F, &TTI))
1906b3f41edSDimitry Andric     return PreservedAnalyses::all();
1916b3f41edSDimitry Andric   PreservedAnalyses PA;
1926b3f41edSDimitry Andric   PA.preserveSet<CFGAnalyses>();
1936b3f41edSDimitry Andric   return PA;
1946b3f41edSDimitry Andric }
195