186de486dSMatt Arsenault //===-- AMDGPUCodeGenPrepare.cpp ------------------------------------------===//
286de486dSMatt Arsenault //
386de486dSMatt Arsenault //                     The LLVM Compiler Infrastructure
486de486dSMatt Arsenault //
586de486dSMatt Arsenault // This file is distributed under the University of Illinois Open Source
686de486dSMatt Arsenault // License. See LICENSE.TXT for details.
786de486dSMatt Arsenault //
886de486dSMatt Arsenault //===----------------------------------------------------------------------===//
986de486dSMatt Arsenault //
1086de486dSMatt Arsenault /// \file
1186de486dSMatt Arsenault /// This pass does misc. AMDGPU optimizations on IR before instruction
1286de486dSMatt Arsenault /// selection.
1386de486dSMatt Arsenault //
1486de486dSMatt Arsenault //===----------------------------------------------------------------------===//
1586de486dSMatt Arsenault 
1686de486dSMatt Arsenault #include "AMDGPU.h"
17*a1fe17c9SMatt Arsenault #include "AMDGPUIntrinsicInfo.h"
1886de486dSMatt Arsenault #include "AMDGPUSubtarget.h"
19*a1fe17c9SMatt Arsenault #include "AMDGPUTargetMachine.h"
2086de486dSMatt Arsenault 
2186de486dSMatt Arsenault #include "llvm/Analysis/DivergenceAnalysis.h"
2286de486dSMatt Arsenault #include "llvm/CodeGen/Passes.h"
2386de486dSMatt Arsenault #include "llvm/IR/InstVisitor.h"
2486de486dSMatt Arsenault #include "llvm/IR/IRBuilder.h"
2586de486dSMatt Arsenault #include "llvm/Support/Debug.h"
2686de486dSMatt Arsenault #include "llvm/Support/raw_ostream.h"
2786de486dSMatt Arsenault 
2886de486dSMatt Arsenault #define DEBUG_TYPE "amdgpu-codegenprepare"
2986de486dSMatt Arsenault 
3086de486dSMatt Arsenault using namespace llvm;
3186de486dSMatt Arsenault 
3286de486dSMatt Arsenault namespace {
3386de486dSMatt Arsenault 
3486de486dSMatt Arsenault class AMDGPUCodeGenPrepare : public FunctionPass,
35*a1fe17c9SMatt Arsenault                              public InstVisitor<AMDGPUCodeGenPrepare, bool> {
36*a1fe17c9SMatt Arsenault   const GCNTargetMachine *TM;
37*a1fe17c9SMatt Arsenault   const SISubtarget *ST;
3886de486dSMatt Arsenault   DivergenceAnalysis *DA;
39*a1fe17c9SMatt Arsenault   Module *Mod;
40*a1fe17c9SMatt Arsenault   bool HasUnsafeFPMath;
4186de486dSMatt Arsenault 
4286de486dSMatt Arsenault public:
4386de486dSMatt Arsenault   static char ID;
4486de486dSMatt Arsenault   AMDGPUCodeGenPrepare(const TargetMachine *TM = nullptr) :
4586de486dSMatt Arsenault     FunctionPass(ID),
46*a1fe17c9SMatt Arsenault     TM(static_cast<const GCNTargetMachine *>(TM)),
47*a1fe17c9SMatt Arsenault     ST(nullptr),
48*a1fe17c9SMatt Arsenault     DA(nullptr),
49*a1fe17c9SMatt Arsenault     Mod(nullptr),
50*a1fe17c9SMatt Arsenault     HasUnsafeFPMath(false) { }
51*a1fe17c9SMatt Arsenault 
52*a1fe17c9SMatt Arsenault   bool visitFDiv(BinaryOperator &I);
53*a1fe17c9SMatt Arsenault 
54*a1fe17c9SMatt Arsenault   bool visitInstruction(Instruction &I) {
55*a1fe17c9SMatt Arsenault     return false;
56*a1fe17c9SMatt Arsenault   }
5786de486dSMatt Arsenault 
5886de486dSMatt Arsenault   bool doInitialization(Module &M) override;
5986de486dSMatt Arsenault   bool runOnFunction(Function &F) override;
6086de486dSMatt Arsenault 
6186de486dSMatt Arsenault   const char *getPassName() const override {
6286de486dSMatt Arsenault     return "AMDGPU IR optimizations";
6386de486dSMatt Arsenault   }
6486de486dSMatt Arsenault 
6586de486dSMatt Arsenault   void getAnalysisUsage(AnalysisUsage &AU) const override {
6686de486dSMatt Arsenault     AU.addRequired<DivergenceAnalysis>();
6786de486dSMatt Arsenault     AU.setPreservesAll();
6886de486dSMatt Arsenault  }
6986de486dSMatt Arsenault };
7086de486dSMatt Arsenault 
7186de486dSMatt Arsenault } // End anonymous namespace
7286de486dSMatt Arsenault 
73*a1fe17c9SMatt Arsenault static bool shouldKeepFDivF32(Value *Num, bool UnsafeDiv) {
74*a1fe17c9SMatt Arsenault   const ConstantFP *CNum = dyn_cast<ConstantFP>(Num);
75*a1fe17c9SMatt Arsenault   if (!CNum)
76*a1fe17c9SMatt Arsenault     return false;
77*a1fe17c9SMatt Arsenault 
78*a1fe17c9SMatt Arsenault   // Reciprocal f32 is handled separately without denormals.
79*a1fe17c9SMatt Arsenault   return UnsafeDiv && CNum->isExactlyValue(+1.0);
80*a1fe17c9SMatt Arsenault }
81*a1fe17c9SMatt Arsenault 
82*a1fe17c9SMatt Arsenault // Insert an intrinsic for fast fdiv for safe math situations where we can
83*a1fe17c9SMatt Arsenault // reduce precision. Leave fdiv for situations where the generic node is
84*a1fe17c9SMatt Arsenault // expected to be optimized.
85*a1fe17c9SMatt Arsenault bool AMDGPUCodeGenPrepare::visitFDiv(BinaryOperator &FDiv) {
86*a1fe17c9SMatt Arsenault   Type *Ty = FDiv.getType();
87*a1fe17c9SMatt Arsenault 
88*a1fe17c9SMatt Arsenault   // TODO: Handle half
89*a1fe17c9SMatt Arsenault   if (!Ty->getScalarType()->isFloatTy())
90*a1fe17c9SMatt Arsenault     return false;
91*a1fe17c9SMatt Arsenault 
92*a1fe17c9SMatt Arsenault   MDNode *FPMath = FDiv.getMetadata(LLVMContext::MD_fpmath);
93*a1fe17c9SMatt Arsenault   if (!FPMath)
94*a1fe17c9SMatt Arsenault     return false;
95*a1fe17c9SMatt Arsenault 
96*a1fe17c9SMatt Arsenault   const FPMathOperator *FPOp = cast<const FPMathOperator>(&FDiv);
97*a1fe17c9SMatt Arsenault   float ULP = FPOp->getFPAccuracy();
98*a1fe17c9SMatt Arsenault   if (ULP < 2.5f)
99*a1fe17c9SMatt Arsenault     return false;
100*a1fe17c9SMatt Arsenault 
101*a1fe17c9SMatt Arsenault   FastMathFlags FMF = FPOp->getFastMathFlags();
102*a1fe17c9SMatt Arsenault   bool UnsafeDiv = HasUnsafeFPMath || FMF.unsafeAlgebra() ||
103*a1fe17c9SMatt Arsenault                                       FMF.allowReciprocal();
104*a1fe17c9SMatt Arsenault   if (ST->hasFP32Denormals() && !UnsafeDiv)
105*a1fe17c9SMatt Arsenault     return false;
106*a1fe17c9SMatt Arsenault 
107*a1fe17c9SMatt Arsenault   IRBuilder<> Builder(FDiv.getParent(), std::next(FDiv.getIterator()), FPMath);
108*a1fe17c9SMatt Arsenault   Builder.setFastMathFlags(FMF);
109*a1fe17c9SMatt Arsenault   Builder.SetCurrentDebugLocation(FDiv.getDebugLoc());
110*a1fe17c9SMatt Arsenault 
111*a1fe17c9SMatt Arsenault   const AMDGPUIntrinsicInfo *II = TM->getIntrinsicInfo();
112*a1fe17c9SMatt Arsenault   Function *Decl
113*a1fe17c9SMatt Arsenault     = II->getDeclaration(Mod, AMDGPUIntrinsic::amdgcn_fdiv_fast, {});
114*a1fe17c9SMatt Arsenault 
115*a1fe17c9SMatt Arsenault   Value *Num = FDiv.getOperand(0);
116*a1fe17c9SMatt Arsenault   Value *Den = FDiv.getOperand(1);
117*a1fe17c9SMatt Arsenault 
118*a1fe17c9SMatt Arsenault   Value *NewFDiv = nullptr;
119*a1fe17c9SMatt Arsenault 
120*a1fe17c9SMatt Arsenault   if (VectorType *VT = dyn_cast<VectorType>(Ty)) {
121*a1fe17c9SMatt Arsenault     NewFDiv = UndefValue::get(VT);
122*a1fe17c9SMatt Arsenault 
123*a1fe17c9SMatt Arsenault     // FIXME: Doesn't do the right thing for cases where the vector is partially
124*a1fe17c9SMatt Arsenault     // constant. This works when the scalarizer pass is run first.
125*a1fe17c9SMatt Arsenault     for (unsigned I = 0, E = VT->getNumElements(); I != E; ++I) {
126*a1fe17c9SMatt Arsenault       Value *NumEltI = Builder.CreateExtractElement(Num, I);
127*a1fe17c9SMatt Arsenault       Value *DenEltI = Builder.CreateExtractElement(Den, I);
128*a1fe17c9SMatt Arsenault       Value *NewElt;
129*a1fe17c9SMatt Arsenault 
130*a1fe17c9SMatt Arsenault       if (shouldKeepFDivF32(NumEltI, UnsafeDiv)) {
131*a1fe17c9SMatt Arsenault         NewElt = Builder.CreateFDiv(NumEltI, DenEltI);
132*a1fe17c9SMatt Arsenault       } else {
133*a1fe17c9SMatt Arsenault         NewElt = Builder.CreateCall(Decl, { NumEltI, DenEltI });
134*a1fe17c9SMatt Arsenault       }
135*a1fe17c9SMatt Arsenault 
136*a1fe17c9SMatt Arsenault       NewFDiv = Builder.CreateInsertElement(NewFDiv, NewElt, I);
137*a1fe17c9SMatt Arsenault     }
138*a1fe17c9SMatt Arsenault   } else {
139*a1fe17c9SMatt Arsenault     if (!shouldKeepFDivF32(Num, UnsafeDiv))
140*a1fe17c9SMatt Arsenault       NewFDiv = Builder.CreateCall(Decl, { Num, Den });
141*a1fe17c9SMatt Arsenault   }
142*a1fe17c9SMatt Arsenault 
143*a1fe17c9SMatt Arsenault   if (NewFDiv) {
144*a1fe17c9SMatt Arsenault     FDiv.replaceAllUsesWith(NewFDiv);
145*a1fe17c9SMatt Arsenault     NewFDiv->takeName(&FDiv);
146*a1fe17c9SMatt Arsenault     FDiv.eraseFromParent();
147*a1fe17c9SMatt Arsenault   }
148*a1fe17c9SMatt Arsenault 
149*a1fe17c9SMatt Arsenault   return true;
150*a1fe17c9SMatt Arsenault }
151*a1fe17c9SMatt Arsenault 
152*a1fe17c9SMatt Arsenault static bool hasUnsafeFPMath(const Function &F) {
153*a1fe17c9SMatt Arsenault   Attribute Attr = F.getFnAttribute("unsafe-fp-math");
154*a1fe17c9SMatt Arsenault   return Attr.getValueAsString() == "true";
155*a1fe17c9SMatt Arsenault }
156*a1fe17c9SMatt Arsenault 
15786de486dSMatt Arsenault bool AMDGPUCodeGenPrepare::doInitialization(Module &M) {
158*a1fe17c9SMatt Arsenault   Mod = &M;
15986de486dSMatt Arsenault   return false;
16086de486dSMatt Arsenault }
16186de486dSMatt Arsenault 
16286de486dSMatt Arsenault bool AMDGPUCodeGenPrepare::runOnFunction(Function &F) {
16386de486dSMatt Arsenault   if (!TM || skipFunction(F))
16486de486dSMatt Arsenault     return false;
16586de486dSMatt Arsenault 
166*a1fe17c9SMatt Arsenault   ST = &TM->getSubtarget<SISubtarget>(F);
16786de486dSMatt Arsenault   DA = &getAnalysis<DivergenceAnalysis>();
168*a1fe17c9SMatt Arsenault   HasUnsafeFPMath = hasUnsafeFPMath(F);
16986de486dSMatt Arsenault 
170*a1fe17c9SMatt Arsenault   bool MadeChange = false;
171*a1fe17c9SMatt Arsenault 
172*a1fe17c9SMatt Arsenault   for (BasicBlock &BB : F) {
173*a1fe17c9SMatt Arsenault     BasicBlock::iterator Next;
174*a1fe17c9SMatt Arsenault     for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; I = Next) {
175*a1fe17c9SMatt Arsenault       Next = std::next(I);
176*a1fe17c9SMatt Arsenault       MadeChange |= visit(*I);
177*a1fe17c9SMatt Arsenault     }
178*a1fe17c9SMatt Arsenault   }
179*a1fe17c9SMatt Arsenault 
180*a1fe17c9SMatt Arsenault   return MadeChange;
18186de486dSMatt Arsenault }
18286de486dSMatt Arsenault 
18386de486dSMatt Arsenault INITIALIZE_TM_PASS_BEGIN(AMDGPUCodeGenPrepare, DEBUG_TYPE,
18486de486dSMatt Arsenault                       "AMDGPU IR optimizations", false, false)
18586de486dSMatt Arsenault INITIALIZE_PASS_DEPENDENCY(DivergenceAnalysis)
18686de486dSMatt Arsenault INITIALIZE_TM_PASS_END(AMDGPUCodeGenPrepare, DEBUG_TYPE,
18786de486dSMatt Arsenault                        "AMDGPU IR optimizations", false, false)
18886de486dSMatt Arsenault 
18986de486dSMatt Arsenault char AMDGPUCodeGenPrepare::ID = 0;
19086de486dSMatt Arsenault 
191*a1fe17c9SMatt Arsenault FunctionPass *llvm::createAMDGPUCodeGenPreparePass(const GCNTargetMachine *TM) {
19286de486dSMatt Arsenault   return new AMDGPUCodeGenPrepare(TM);
19386de486dSMatt Arsenault }
194