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