1*0b57cec5SDimitry Andric //===--- ExpandReductions.cpp - Expand experimental reduction intrinsics --===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This pass implements IR expansion for reduction intrinsics, allowing targets 10*0b57cec5SDimitry Andric // to enable the experimental intrinsics until just before codegen. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include "llvm/CodeGen/ExpandReductions.h" 15*0b57cec5SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h" 16*0b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 17*0b57cec5SDimitry Andric #include "llvm/IR/Function.h" 18*0b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h" 19*0b57cec5SDimitry Andric #include "llvm/IR/InstIterator.h" 20*0b57cec5SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 21*0b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h" 22*0b57cec5SDimitry Andric #include "llvm/IR/Module.h" 23480093f4SDimitry Andric #include "llvm/InitializePasses.h" 24*0b57cec5SDimitry Andric #include "llvm/Pass.h" 25*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h" 26*0b57cec5SDimitry Andric 27*0b57cec5SDimitry Andric using namespace llvm; 28*0b57cec5SDimitry Andric 29*0b57cec5SDimitry Andric namespace { 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric unsigned getOpcode(Intrinsic::ID ID) { 32*0b57cec5SDimitry Andric switch (ID) { 33*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fadd: 34*0b57cec5SDimitry Andric return Instruction::FAdd; 35*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fmul: 36*0b57cec5SDimitry Andric return Instruction::FMul; 37*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_add: 38*0b57cec5SDimitry Andric return Instruction::Add; 39*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_mul: 40*0b57cec5SDimitry Andric return Instruction::Mul; 41*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_and: 42*0b57cec5SDimitry Andric return Instruction::And; 43*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_or: 44*0b57cec5SDimitry Andric return Instruction::Or; 45*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_xor: 46*0b57cec5SDimitry Andric return Instruction::Xor; 47*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 48*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 49*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 50*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 51*0b57cec5SDimitry Andric return Instruction::ICmp; 52*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 53*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: 54*0b57cec5SDimitry Andric return Instruction::FCmp; 55*0b57cec5SDimitry Andric default: 56*0b57cec5SDimitry Andric llvm_unreachable("Unexpected ID"); 57*0b57cec5SDimitry Andric } 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric RecurrenceDescriptor::MinMaxRecurrenceKind getMRK(Intrinsic::ID ID) { 61*0b57cec5SDimitry Andric switch (ID) { 62*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 63*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_SIntMax; 64*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 65*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_SIntMin; 66*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 67*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_UIntMax; 68*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 69*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_UIntMin; 70*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 71*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_FloatMax; 72*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: 73*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_FloatMin; 74*0b57cec5SDimitry Andric default: 75*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_Invalid; 76*0b57cec5SDimitry Andric } 77*0b57cec5SDimitry Andric } 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric bool expandReductions(Function &F, const TargetTransformInfo *TTI) { 80*0b57cec5SDimitry Andric bool Changed = false; 81*0b57cec5SDimitry Andric SmallVector<IntrinsicInst *, 4> Worklist; 82480093f4SDimitry Andric for (auto &I : instructions(F)) { 83480093f4SDimitry Andric if (auto *II = dyn_cast<IntrinsicInst>(&I)) { 84480093f4SDimitry Andric switch (II->getIntrinsicID()) { 85480093f4SDimitry Andric default: break; 86480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fadd: 87480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fmul: 88480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_add: 89480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_mul: 90480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_and: 91480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_or: 92480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_xor: 93480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 94480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 95480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 96480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 97480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 98480093f4SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: 99480093f4SDimitry Andric if (TTI->shouldExpandReduction(II)) 100*0b57cec5SDimitry Andric Worklist.push_back(II); 101*0b57cec5SDimitry Andric 102480093f4SDimitry Andric break; 103480093f4SDimitry Andric } 104480093f4SDimitry Andric } 105480093f4SDimitry Andric } 106*0b57cec5SDimitry Andric 107480093f4SDimitry Andric for (auto *II : Worklist) { 108*0b57cec5SDimitry Andric FastMathFlags FMF = 109*0b57cec5SDimitry Andric isa<FPMathOperator>(II) ? II->getFastMathFlags() : FastMathFlags{}; 110*0b57cec5SDimitry Andric Intrinsic::ID ID = II->getIntrinsicID(); 111*0b57cec5SDimitry Andric RecurrenceDescriptor::MinMaxRecurrenceKind MRK = getMRK(ID); 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric Value *Rdx = nullptr; 114*0b57cec5SDimitry Andric IRBuilder<> Builder(II); 115*0b57cec5SDimitry Andric IRBuilder<>::FastMathFlagGuard FMFGuard(Builder); 116*0b57cec5SDimitry Andric Builder.setFastMathFlags(FMF); 117*0b57cec5SDimitry Andric switch (ID) { 118480093f4SDimitry Andric default: llvm_unreachable("Unexpected intrinsic!"); 119*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fadd: 120*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fmul: { 121*0b57cec5SDimitry Andric // FMFs must be attached to the call, otherwise it's an ordered reduction 122*0b57cec5SDimitry Andric // and it can't be handled by generating a shuffle sequence. 123*0b57cec5SDimitry Andric Value *Acc = II->getArgOperand(0); 124*0b57cec5SDimitry Andric Value *Vec = II->getArgOperand(1); 125*0b57cec5SDimitry Andric if (!FMF.allowReassoc()) 126*0b57cec5SDimitry Andric Rdx = getOrderedReduction(Builder, Acc, Vec, getOpcode(ID), MRK); 127*0b57cec5SDimitry Andric else { 1285ffd83dbSDimitry Andric if (!isPowerOf2_32( 1295ffd83dbSDimitry Andric cast<FixedVectorType>(Vec->getType())->getNumElements())) 130480093f4SDimitry Andric continue; 131480093f4SDimitry Andric 132*0b57cec5SDimitry Andric Rdx = getShuffleReduction(Builder, Vec, getOpcode(ID), MRK); 133*0b57cec5SDimitry Andric Rdx = Builder.CreateBinOp((Instruction::BinaryOps)getOpcode(ID), 134*0b57cec5SDimitry Andric Acc, Rdx, "bin.rdx"); 135*0b57cec5SDimitry Andric } 136480093f4SDimitry Andric break; 137480093f4SDimitry Andric } 138*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_add: 139*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_mul: 140*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_and: 141*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_or: 142*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_xor: 143*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 144*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 145*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 146*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 147*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 148*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: { 149*0b57cec5SDimitry Andric Value *Vec = II->getArgOperand(0); 1505ffd83dbSDimitry Andric if (!isPowerOf2_32( 1515ffd83dbSDimitry Andric cast<FixedVectorType>(Vec->getType())->getNumElements())) 152*0b57cec5SDimitry Andric continue; 153480093f4SDimitry Andric 154480093f4SDimitry Andric Rdx = getShuffleReduction(Builder, Vec, getOpcode(ID), MRK); 155480093f4SDimitry Andric break; 156480093f4SDimitry Andric } 157*0b57cec5SDimitry Andric } 158*0b57cec5SDimitry Andric II->replaceAllUsesWith(Rdx); 159*0b57cec5SDimitry Andric II->eraseFromParent(); 160*0b57cec5SDimitry Andric Changed = true; 161*0b57cec5SDimitry Andric } 162*0b57cec5SDimitry Andric return Changed; 163*0b57cec5SDimitry Andric } 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric class ExpandReductions : public FunctionPass { 166*0b57cec5SDimitry Andric public: 167*0b57cec5SDimitry Andric static char ID; 168*0b57cec5SDimitry Andric ExpandReductions() : FunctionPass(ID) { 169*0b57cec5SDimitry Andric initializeExpandReductionsPass(*PassRegistry::getPassRegistry()); 170*0b57cec5SDimitry Andric } 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric bool runOnFunction(Function &F) override { 173*0b57cec5SDimitry Andric const auto *TTI =&getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); 174*0b57cec5SDimitry Andric return expandReductions(F, TTI); 175*0b57cec5SDimitry Andric } 176*0b57cec5SDimitry Andric 177*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 178*0b57cec5SDimitry Andric AU.addRequired<TargetTransformInfoWrapperPass>(); 179*0b57cec5SDimitry Andric AU.setPreservesCFG(); 180*0b57cec5SDimitry Andric } 181*0b57cec5SDimitry Andric }; 182*0b57cec5SDimitry Andric } 183*0b57cec5SDimitry Andric 184*0b57cec5SDimitry Andric char ExpandReductions::ID; 185*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(ExpandReductions, "expand-reductions", 186*0b57cec5SDimitry Andric "Expand reduction intrinsics", false, false) 187*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) 188*0b57cec5SDimitry Andric INITIALIZE_PASS_END(ExpandReductions, "expand-reductions", 189*0b57cec5SDimitry Andric "Expand reduction intrinsics", false, false) 190*0b57cec5SDimitry Andric 191*0b57cec5SDimitry Andric FunctionPass *llvm::createExpandReductionsPass() { 192*0b57cec5SDimitry Andric return new ExpandReductions(); 193*0b57cec5SDimitry Andric } 194*0b57cec5SDimitry Andric 195*0b57cec5SDimitry Andric PreservedAnalyses ExpandReductionsPass::run(Function &F, 196*0b57cec5SDimitry Andric FunctionAnalysisManager &AM) { 197*0b57cec5SDimitry Andric const auto &TTI = AM.getResult<TargetIRAnalysis>(F); 198*0b57cec5SDimitry Andric if (!expandReductions(F, &TTI)) 199*0b57cec5SDimitry Andric return PreservedAnalyses::all(); 200*0b57cec5SDimitry Andric PreservedAnalyses PA; 201*0b57cec5SDimitry Andric PA.preserveSet<CFGAnalyses>(); 202*0b57cec5SDimitry Andric return PA; 203*0b57cec5SDimitry Andric } 204