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" 23*0b57cec5SDimitry Andric #include "llvm/Pass.h" 24*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h" 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric using namespace llvm; 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric namespace { 29*0b57cec5SDimitry Andric 30*0b57cec5SDimitry Andric unsigned getOpcode(Intrinsic::ID ID) { 31*0b57cec5SDimitry Andric switch (ID) { 32*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fadd: 33*0b57cec5SDimitry Andric return Instruction::FAdd; 34*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fmul: 35*0b57cec5SDimitry Andric return Instruction::FMul; 36*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_add: 37*0b57cec5SDimitry Andric return Instruction::Add; 38*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_mul: 39*0b57cec5SDimitry Andric return Instruction::Mul; 40*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_and: 41*0b57cec5SDimitry Andric return Instruction::And; 42*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_or: 43*0b57cec5SDimitry Andric return Instruction::Or; 44*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_xor: 45*0b57cec5SDimitry Andric return Instruction::Xor; 46*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 47*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 48*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 49*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 50*0b57cec5SDimitry Andric return Instruction::ICmp; 51*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 52*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: 53*0b57cec5SDimitry Andric return Instruction::FCmp; 54*0b57cec5SDimitry Andric default: 55*0b57cec5SDimitry Andric llvm_unreachable("Unexpected ID"); 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric } 58*0b57cec5SDimitry Andric 59*0b57cec5SDimitry Andric RecurrenceDescriptor::MinMaxRecurrenceKind getMRK(Intrinsic::ID ID) { 60*0b57cec5SDimitry Andric switch (ID) { 61*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 62*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_SIntMax; 63*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 64*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_SIntMin; 65*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 66*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_UIntMax; 67*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 68*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_UIntMin; 69*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 70*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_FloatMax; 71*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: 72*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_FloatMin; 73*0b57cec5SDimitry Andric default: 74*0b57cec5SDimitry Andric return RecurrenceDescriptor::MRK_Invalid; 75*0b57cec5SDimitry Andric } 76*0b57cec5SDimitry Andric } 77*0b57cec5SDimitry Andric 78*0b57cec5SDimitry Andric bool expandReductions(Function &F, const TargetTransformInfo *TTI) { 79*0b57cec5SDimitry Andric bool Changed = false; 80*0b57cec5SDimitry Andric SmallVector<IntrinsicInst *, 4> Worklist; 81*0b57cec5SDimitry Andric for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) 82*0b57cec5SDimitry Andric if (auto II = dyn_cast<IntrinsicInst>(&*I)) 83*0b57cec5SDimitry Andric Worklist.push_back(II); 84*0b57cec5SDimitry Andric 85*0b57cec5SDimitry Andric for (auto *II : Worklist) { 86*0b57cec5SDimitry Andric if (!TTI->shouldExpandReduction(II)) 87*0b57cec5SDimitry Andric continue; 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric FastMathFlags FMF = 90*0b57cec5SDimitry Andric isa<FPMathOperator>(II) ? II->getFastMathFlags() : FastMathFlags{}; 91*0b57cec5SDimitry Andric Intrinsic::ID ID = II->getIntrinsicID(); 92*0b57cec5SDimitry Andric RecurrenceDescriptor::MinMaxRecurrenceKind MRK = getMRK(ID); 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric Value *Rdx = nullptr; 95*0b57cec5SDimitry Andric IRBuilder<> Builder(II); 96*0b57cec5SDimitry Andric IRBuilder<>::FastMathFlagGuard FMFGuard(Builder); 97*0b57cec5SDimitry Andric Builder.setFastMathFlags(FMF); 98*0b57cec5SDimitry Andric switch (ID) { 99*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fadd: 100*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_v2_fmul: { 101*0b57cec5SDimitry Andric // FMFs must be attached to the call, otherwise it's an ordered reduction 102*0b57cec5SDimitry Andric // and it can't be handled by generating a shuffle sequence. 103*0b57cec5SDimitry Andric Value *Acc = II->getArgOperand(0); 104*0b57cec5SDimitry Andric Value *Vec = II->getArgOperand(1); 105*0b57cec5SDimitry Andric if (!FMF.allowReassoc()) 106*0b57cec5SDimitry Andric Rdx = getOrderedReduction(Builder, Acc, Vec, getOpcode(ID), MRK); 107*0b57cec5SDimitry Andric else { 108*0b57cec5SDimitry Andric Rdx = getShuffleReduction(Builder, Vec, getOpcode(ID), MRK); 109*0b57cec5SDimitry Andric Rdx = Builder.CreateBinOp((Instruction::BinaryOps)getOpcode(ID), 110*0b57cec5SDimitry Andric Acc, Rdx, "bin.rdx"); 111*0b57cec5SDimitry Andric } 112*0b57cec5SDimitry Andric } break; 113*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_add: 114*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_mul: 115*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_and: 116*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_or: 117*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_xor: 118*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smax: 119*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_smin: 120*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umax: 121*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_umin: 122*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmax: 123*0b57cec5SDimitry Andric case Intrinsic::experimental_vector_reduce_fmin: { 124*0b57cec5SDimitry Andric Value *Vec = II->getArgOperand(0); 125*0b57cec5SDimitry Andric Rdx = getShuffleReduction(Builder, Vec, getOpcode(ID), MRK); 126*0b57cec5SDimitry Andric } break; 127*0b57cec5SDimitry Andric default: 128*0b57cec5SDimitry Andric continue; 129*0b57cec5SDimitry Andric } 130*0b57cec5SDimitry Andric II->replaceAllUsesWith(Rdx); 131*0b57cec5SDimitry Andric II->eraseFromParent(); 132*0b57cec5SDimitry Andric Changed = true; 133*0b57cec5SDimitry Andric } 134*0b57cec5SDimitry Andric return Changed; 135*0b57cec5SDimitry Andric } 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric class ExpandReductions : public FunctionPass { 138*0b57cec5SDimitry Andric public: 139*0b57cec5SDimitry Andric static char ID; 140*0b57cec5SDimitry Andric ExpandReductions() : FunctionPass(ID) { 141*0b57cec5SDimitry Andric initializeExpandReductionsPass(*PassRegistry::getPassRegistry()); 142*0b57cec5SDimitry Andric } 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric bool runOnFunction(Function &F) override { 145*0b57cec5SDimitry Andric const auto *TTI =&getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); 146*0b57cec5SDimitry Andric return expandReductions(F, TTI); 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 150*0b57cec5SDimitry Andric AU.addRequired<TargetTransformInfoWrapperPass>(); 151*0b57cec5SDimitry Andric AU.setPreservesCFG(); 152*0b57cec5SDimitry Andric } 153*0b57cec5SDimitry Andric }; 154*0b57cec5SDimitry Andric } 155*0b57cec5SDimitry Andric 156*0b57cec5SDimitry Andric char ExpandReductions::ID; 157*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(ExpandReductions, "expand-reductions", 158*0b57cec5SDimitry Andric "Expand reduction intrinsics", false, false) 159*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) 160*0b57cec5SDimitry Andric INITIALIZE_PASS_END(ExpandReductions, "expand-reductions", 161*0b57cec5SDimitry Andric "Expand reduction intrinsics", false, false) 162*0b57cec5SDimitry Andric 163*0b57cec5SDimitry Andric FunctionPass *llvm::createExpandReductionsPass() { 164*0b57cec5SDimitry Andric return new ExpandReductions(); 165*0b57cec5SDimitry Andric } 166*0b57cec5SDimitry Andric 167*0b57cec5SDimitry Andric PreservedAnalyses ExpandReductionsPass::run(Function &F, 168*0b57cec5SDimitry Andric FunctionAnalysisManager &AM) { 169*0b57cec5SDimitry Andric const auto &TTI = AM.getResult<TargetIRAnalysis>(F); 170*0b57cec5SDimitry Andric if (!expandReductions(F, &TTI)) 171*0b57cec5SDimitry Andric return PreservedAnalyses::all(); 172*0b57cec5SDimitry Andric PreservedAnalyses PA; 173*0b57cec5SDimitry Andric PA.preserveSet<CFGAnalyses>(); 174*0b57cec5SDimitry Andric return PA; 175*0b57cec5SDimitry Andric } 176