1*0b57cec5SDimitry Andric //===-- AMDGPUAtomicOptimizer.cpp -----------------------------------------===// 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 /// \file 10*0b57cec5SDimitry Andric /// This pass optimizes atomic operations by using a single lane of a wavefront 11*0b57cec5SDimitry Andric /// to perform the atomic operation, thus reducing contention on that memory 12*0b57cec5SDimitry Andric /// location. 13*0b57cec5SDimitry Andric // 14*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 15*0b57cec5SDimitry Andric 16*0b57cec5SDimitry Andric #include "AMDGPU.h" 17*0b57cec5SDimitry Andric #include "AMDGPUSubtarget.h" 188bcb0991SDimitry Andric #include "SIDefines.h" 19*0b57cec5SDimitry Andric #include "llvm/Analysis/LegacyDivergenceAnalysis.h" 20*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 21*0b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h" 22*0b57cec5SDimitry Andric #include "llvm/IR/InstVisitor.h" 23480093f4SDimitry Andric #include "llvm/InitializePasses.h" 24*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h" 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric #define DEBUG_TYPE "amdgpu-atomic-optimizer" 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric using namespace llvm; 298bcb0991SDimitry Andric using namespace llvm::AMDGPU; 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric namespace { 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric struct ReplacementInfo { 34*0b57cec5SDimitry Andric Instruction *I; 35*0b57cec5SDimitry Andric AtomicRMWInst::BinOp Op; 36*0b57cec5SDimitry Andric unsigned ValIdx; 37*0b57cec5SDimitry Andric bool ValDivergent; 38*0b57cec5SDimitry Andric }; 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric class AMDGPUAtomicOptimizer : public FunctionPass, 41*0b57cec5SDimitry Andric public InstVisitor<AMDGPUAtomicOptimizer> { 42*0b57cec5SDimitry Andric private: 43*0b57cec5SDimitry Andric SmallVector<ReplacementInfo, 8> ToReplace; 44*0b57cec5SDimitry Andric const LegacyDivergenceAnalysis *DA; 45*0b57cec5SDimitry Andric const DataLayout *DL; 46*0b57cec5SDimitry Andric DominatorTree *DT; 478bcb0991SDimitry Andric const GCNSubtarget *ST; 48*0b57cec5SDimitry Andric bool IsPixelShader; 49*0b57cec5SDimitry Andric 508bcb0991SDimitry Andric Value *buildScan(IRBuilder<> &B, AtomicRMWInst::BinOp Op, Value *V, 518bcb0991SDimitry Andric Value *const Identity) const; 528bcb0991SDimitry Andric Value *buildShiftRight(IRBuilder<> &B, Value *V, Value *const Identity) const; 53*0b57cec5SDimitry Andric void optimizeAtomic(Instruction &I, AtomicRMWInst::BinOp Op, unsigned ValIdx, 54*0b57cec5SDimitry Andric bool ValDivergent) const; 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric public: 57*0b57cec5SDimitry Andric static char ID; 58*0b57cec5SDimitry Andric 59*0b57cec5SDimitry Andric AMDGPUAtomicOptimizer() : FunctionPass(ID) {} 60*0b57cec5SDimitry Andric 61*0b57cec5SDimitry Andric bool runOnFunction(Function &F) override; 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 64*0b57cec5SDimitry Andric AU.addPreserved<DominatorTreeWrapperPass>(); 65*0b57cec5SDimitry Andric AU.addRequired<LegacyDivergenceAnalysis>(); 66*0b57cec5SDimitry Andric AU.addRequired<TargetPassConfig>(); 67*0b57cec5SDimitry Andric } 68*0b57cec5SDimitry Andric 69*0b57cec5SDimitry Andric void visitAtomicRMWInst(AtomicRMWInst &I); 70*0b57cec5SDimitry Andric void visitIntrinsicInst(IntrinsicInst &I); 71*0b57cec5SDimitry Andric }; 72*0b57cec5SDimitry Andric 73*0b57cec5SDimitry Andric } // namespace 74*0b57cec5SDimitry Andric 75*0b57cec5SDimitry Andric char AMDGPUAtomicOptimizer::ID = 0; 76*0b57cec5SDimitry Andric 77*0b57cec5SDimitry Andric char &llvm::AMDGPUAtomicOptimizerID = AMDGPUAtomicOptimizer::ID; 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric bool AMDGPUAtomicOptimizer::runOnFunction(Function &F) { 80*0b57cec5SDimitry Andric if (skipFunction(F)) { 81*0b57cec5SDimitry Andric return false; 82*0b57cec5SDimitry Andric } 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric DA = &getAnalysis<LegacyDivergenceAnalysis>(); 85*0b57cec5SDimitry Andric DL = &F.getParent()->getDataLayout(); 86*0b57cec5SDimitry Andric DominatorTreeWrapperPass *const DTW = 87*0b57cec5SDimitry Andric getAnalysisIfAvailable<DominatorTreeWrapperPass>(); 88*0b57cec5SDimitry Andric DT = DTW ? &DTW->getDomTree() : nullptr; 89*0b57cec5SDimitry Andric const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>(); 90*0b57cec5SDimitry Andric const TargetMachine &TM = TPC.getTM<TargetMachine>(); 918bcb0991SDimitry Andric ST = &TM.getSubtarget<GCNSubtarget>(F); 92*0b57cec5SDimitry Andric IsPixelShader = F.getCallingConv() == CallingConv::AMDGPU_PS; 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric visit(F); 95*0b57cec5SDimitry Andric 96*0b57cec5SDimitry Andric const bool Changed = !ToReplace.empty(); 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric for (ReplacementInfo &Info : ToReplace) { 99*0b57cec5SDimitry Andric optimizeAtomic(*Info.I, Info.Op, Info.ValIdx, Info.ValDivergent); 100*0b57cec5SDimitry Andric } 101*0b57cec5SDimitry Andric 102*0b57cec5SDimitry Andric ToReplace.clear(); 103*0b57cec5SDimitry Andric 104*0b57cec5SDimitry Andric return Changed; 105*0b57cec5SDimitry Andric } 106*0b57cec5SDimitry Andric 107*0b57cec5SDimitry Andric void AMDGPUAtomicOptimizer::visitAtomicRMWInst(AtomicRMWInst &I) { 108*0b57cec5SDimitry Andric // Early exit for unhandled address space atomic instructions. 109*0b57cec5SDimitry Andric switch (I.getPointerAddressSpace()) { 110*0b57cec5SDimitry Andric default: 111*0b57cec5SDimitry Andric return; 112*0b57cec5SDimitry Andric case AMDGPUAS::GLOBAL_ADDRESS: 113*0b57cec5SDimitry Andric case AMDGPUAS::LOCAL_ADDRESS: 114*0b57cec5SDimitry Andric break; 115*0b57cec5SDimitry Andric } 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric AtomicRMWInst::BinOp Op = I.getOperation(); 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric switch (Op) { 120*0b57cec5SDimitry Andric default: 121*0b57cec5SDimitry Andric return; 122*0b57cec5SDimitry Andric case AtomicRMWInst::Add: 123*0b57cec5SDimitry Andric case AtomicRMWInst::Sub: 124*0b57cec5SDimitry Andric case AtomicRMWInst::And: 125*0b57cec5SDimitry Andric case AtomicRMWInst::Or: 126*0b57cec5SDimitry Andric case AtomicRMWInst::Xor: 127*0b57cec5SDimitry Andric case AtomicRMWInst::Max: 128*0b57cec5SDimitry Andric case AtomicRMWInst::Min: 129*0b57cec5SDimitry Andric case AtomicRMWInst::UMax: 130*0b57cec5SDimitry Andric case AtomicRMWInst::UMin: 131*0b57cec5SDimitry Andric break; 132*0b57cec5SDimitry Andric } 133*0b57cec5SDimitry Andric 134*0b57cec5SDimitry Andric const unsigned PtrIdx = 0; 135*0b57cec5SDimitry Andric const unsigned ValIdx = 1; 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric // If the pointer operand is divergent, then each lane is doing an atomic 138*0b57cec5SDimitry Andric // operation on a different address, and we cannot optimize that. 1398bcb0991SDimitry Andric if (DA->isDivergentUse(&I.getOperandUse(PtrIdx))) { 140*0b57cec5SDimitry Andric return; 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric 1438bcb0991SDimitry Andric const bool ValDivergent = DA->isDivergentUse(&I.getOperandUse(ValIdx)); 144*0b57cec5SDimitry Andric 145*0b57cec5SDimitry Andric // If the value operand is divergent, each lane is contributing a different 146*0b57cec5SDimitry Andric // value to the atomic calculation. We can only optimize divergent values if 147*0b57cec5SDimitry Andric // we have DPP available on our subtarget, and the atomic operation is 32 148*0b57cec5SDimitry Andric // bits. 1498bcb0991SDimitry Andric if (ValDivergent && 1508bcb0991SDimitry Andric (!ST->hasDPP() || DL->getTypeSizeInBits(I.getType()) != 32)) { 151*0b57cec5SDimitry Andric return; 152*0b57cec5SDimitry Andric } 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric // If we get here, we can optimize the atomic using a single wavefront-wide 155*0b57cec5SDimitry Andric // atomic operation to do the calculation for the entire wavefront, so 156*0b57cec5SDimitry Andric // remember the instruction so we can come back to it. 157*0b57cec5SDimitry Andric const ReplacementInfo Info = {&I, Op, ValIdx, ValDivergent}; 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric ToReplace.push_back(Info); 160*0b57cec5SDimitry Andric } 161*0b57cec5SDimitry Andric 162*0b57cec5SDimitry Andric void AMDGPUAtomicOptimizer::visitIntrinsicInst(IntrinsicInst &I) { 163*0b57cec5SDimitry Andric AtomicRMWInst::BinOp Op; 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric switch (I.getIntrinsicID()) { 166*0b57cec5SDimitry Andric default: 167*0b57cec5SDimitry Andric return; 168*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_add: 169*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_add: 170*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_add: 171*0b57cec5SDimitry Andric Op = AtomicRMWInst::Add; 172*0b57cec5SDimitry Andric break; 173*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_sub: 174*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_sub: 175*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_sub: 176*0b57cec5SDimitry Andric Op = AtomicRMWInst::Sub; 177*0b57cec5SDimitry Andric break; 178*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_and: 179*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_and: 180*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_and: 181*0b57cec5SDimitry Andric Op = AtomicRMWInst::And; 182*0b57cec5SDimitry Andric break; 183*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_or: 184*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_or: 185*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_or: 186*0b57cec5SDimitry Andric Op = AtomicRMWInst::Or; 187*0b57cec5SDimitry Andric break; 188*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_xor: 189*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_xor: 190*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_xor: 191*0b57cec5SDimitry Andric Op = AtomicRMWInst::Xor; 192*0b57cec5SDimitry Andric break; 193*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_smin: 194*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_smin: 195*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_smin: 196*0b57cec5SDimitry Andric Op = AtomicRMWInst::Min; 197*0b57cec5SDimitry Andric break; 198*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_umin: 199*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_umin: 200*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_umin: 201*0b57cec5SDimitry Andric Op = AtomicRMWInst::UMin; 202*0b57cec5SDimitry Andric break; 203*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_smax: 204*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_smax: 205*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_smax: 206*0b57cec5SDimitry Andric Op = AtomicRMWInst::Max; 207*0b57cec5SDimitry Andric break; 208*0b57cec5SDimitry Andric case Intrinsic::amdgcn_buffer_atomic_umax: 209*0b57cec5SDimitry Andric case Intrinsic::amdgcn_struct_buffer_atomic_umax: 210*0b57cec5SDimitry Andric case Intrinsic::amdgcn_raw_buffer_atomic_umax: 211*0b57cec5SDimitry Andric Op = AtomicRMWInst::UMax; 212*0b57cec5SDimitry Andric break; 213*0b57cec5SDimitry Andric } 214*0b57cec5SDimitry Andric 215*0b57cec5SDimitry Andric const unsigned ValIdx = 0; 216*0b57cec5SDimitry Andric 2178bcb0991SDimitry Andric const bool ValDivergent = DA->isDivergentUse(&I.getOperandUse(ValIdx)); 218*0b57cec5SDimitry Andric 219*0b57cec5SDimitry Andric // If the value operand is divergent, each lane is contributing a different 220*0b57cec5SDimitry Andric // value to the atomic calculation. We can only optimize divergent values if 221*0b57cec5SDimitry Andric // we have DPP available on our subtarget, and the atomic operation is 32 222*0b57cec5SDimitry Andric // bits. 2238bcb0991SDimitry Andric if (ValDivergent && 2248bcb0991SDimitry Andric (!ST->hasDPP() || DL->getTypeSizeInBits(I.getType()) != 32)) { 225*0b57cec5SDimitry Andric return; 226*0b57cec5SDimitry Andric } 227*0b57cec5SDimitry Andric 228*0b57cec5SDimitry Andric // If any of the other arguments to the intrinsic are divergent, we can't 229*0b57cec5SDimitry Andric // optimize the operation. 230*0b57cec5SDimitry Andric for (unsigned Idx = 1; Idx < I.getNumOperands(); Idx++) { 2318bcb0991SDimitry Andric if (DA->isDivergentUse(&I.getOperandUse(Idx))) { 232*0b57cec5SDimitry Andric return; 233*0b57cec5SDimitry Andric } 234*0b57cec5SDimitry Andric } 235*0b57cec5SDimitry Andric 236*0b57cec5SDimitry Andric // If we get here, we can optimize the atomic using a single wavefront-wide 237*0b57cec5SDimitry Andric // atomic operation to do the calculation for the entire wavefront, so 238*0b57cec5SDimitry Andric // remember the instruction so we can come back to it. 239*0b57cec5SDimitry Andric const ReplacementInfo Info = {&I, Op, ValIdx, ValDivergent}; 240*0b57cec5SDimitry Andric 241*0b57cec5SDimitry Andric ToReplace.push_back(Info); 242*0b57cec5SDimitry Andric } 243*0b57cec5SDimitry Andric 244*0b57cec5SDimitry Andric // Use the builder to create the non-atomic counterpart of the specified 245*0b57cec5SDimitry Andric // atomicrmw binary op. 246*0b57cec5SDimitry Andric static Value *buildNonAtomicBinOp(IRBuilder<> &B, AtomicRMWInst::BinOp Op, 247*0b57cec5SDimitry Andric Value *LHS, Value *RHS) { 248*0b57cec5SDimitry Andric CmpInst::Predicate Pred; 249*0b57cec5SDimitry Andric 250*0b57cec5SDimitry Andric switch (Op) { 251*0b57cec5SDimitry Andric default: 252*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic op"); 253*0b57cec5SDimitry Andric case AtomicRMWInst::Add: 254*0b57cec5SDimitry Andric return B.CreateBinOp(Instruction::Add, LHS, RHS); 255*0b57cec5SDimitry Andric case AtomicRMWInst::Sub: 256*0b57cec5SDimitry Andric return B.CreateBinOp(Instruction::Sub, LHS, RHS); 257*0b57cec5SDimitry Andric case AtomicRMWInst::And: 258*0b57cec5SDimitry Andric return B.CreateBinOp(Instruction::And, LHS, RHS); 259*0b57cec5SDimitry Andric case AtomicRMWInst::Or: 260*0b57cec5SDimitry Andric return B.CreateBinOp(Instruction::Or, LHS, RHS); 261*0b57cec5SDimitry Andric case AtomicRMWInst::Xor: 262*0b57cec5SDimitry Andric return B.CreateBinOp(Instruction::Xor, LHS, RHS); 263*0b57cec5SDimitry Andric 264*0b57cec5SDimitry Andric case AtomicRMWInst::Max: 265*0b57cec5SDimitry Andric Pred = CmpInst::ICMP_SGT; 266*0b57cec5SDimitry Andric break; 267*0b57cec5SDimitry Andric case AtomicRMWInst::Min: 268*0b57cec5SDimitry Andric Pred = CmpInst::ICMP_SLT; 269*0b57cec5SDimitry Andric break; 270*0b57cec5SDimitry Andric case AtomicRMWInst::UMax: 271*0b57cec5SDimitry Andric Pred = CmpInst::ICMP_UGT; 272*0b57cec5SDimitry Andric break; 273*0b57cec5SDimitry Andric case AtomicRMWInst::UMin: 274*0b57cec5SDimitry Andric Pred = CmpInst::ICMP_ULT; 275*0b57cec5SDimitry Andric break; 276*0b57cec5SDimitry Andric } 277*0b57cec5SDimitry Andric Value *Cond = B.CreateICmp(Pred, LHS, RHS); 278*0b57cec5SDimitry Andric return B.CreateSelect(Cond, LHS, RHS); 279*0b57cec5SDimitry Andric } 280*0b57cec5SDimitry Andric 2818bcb0991SDimitry Andric // Use the builder to create an inclusive scan of V across the wavefront, with 2828bcb0991SDimitry Andric // all lanes active. 2838bcb0991SDimitry Andric Value *AMDGPUAtomicOptimizer::buildScan(IRBuilder<> &B, AtomicRMWInst::BinOp Op, 2848bcb0991SDimitry Andric Value *V, Value *const Identity) const { 2858bcb0991SDimitry Andric Type *const Ty = V->getType(); 2868bcb0991SDimitry Andric Module *M = B.GetInsertBlock()->getModule(); 2878bcb0991SDimitry Andric Function *UpdateDPP = 2888bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_update_dpp, Ty); 2898bcb0991SDimitry Andric Function *PermLaneX16 = 2908bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_permlanex16, {}); 2918bcb0991SDimitry Andric Function *ReadLane = 2928bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_readlane, {}); 2938bcb0991SDimitry Andric 2948bcb0991SDimitry Andric for (unsigned Idx = 0; Idx < 4; Idx++) { 2958bcb0991SDimitry Andric V = buildNonAtomicBinOp( 2968bcb0991SDimitry Andric B, Op, V, 2978bcb0991SDimitry Andric B.CreateCall(UpdateDPP, 2988bcb0991SDimitry Andric {Identity, V, B.getInt32(DPP::ROW_SHR0 | 1 << Idx), 2998bcb0991SDimitry Andric B.getInt32(0xf), B.getInt32(0xf), B.getFalse()})); 3008bcb0991SDimitry Andric } 3018bcb0991SDimitry Andric if (ST->hasDPPBroadcasts()) { 3028bcb0991SDimitry Andric // GFX9 has DPP row broadcast operations. 3038bcb0991SDimitry Andric V = buildNonAtomicBinOp( 3048bcb0991SDimitry Andric B, Op, V, 3058bcb0991SDimitry Andric B.CreateCall(UpdateDPP, 3068bcb0991SDimitry Andric {Identity, V, B.getInt32(DPP::BCAST15), B.getInt32(0xa), 3078bcb0991SDimitry Andric B.getInt32(0xf), B.getFalse()})); 3088bcb0991SDimitry Andric V = buildNonAtomicBinOp( 3098bcb0991SDimitry Andric B, Op, V, 3108bcb0991SDimitry Andric B.CreateCall(UpdateDPP, 3118bcb0991SDimitry Andric {Identity, V, B.getInt32(DPP::BCAST31), B.getInt32(0xc), 3128bcb0991SDimitry Andric B.getInt32(0xf), B.getFalse()})); 3138bcb0991SDimitry Andric } else { 3148bcb0991SDimitry Andric // On GFX10 all DPP operations are confined to a single row. To get cross- 3158bcb0991SDimitry Andric // row operations we have to use permlane or readlane. 3168bcb0991SDimitry Andric 3178bcb0991SDimitry Andric // Combine lane 15 into lanes 16..31 (and, for wave 64, lane 47 into lanes 3188bcb0991SDimitry Andric // 48..63). 3198bcb0991SDimitry Andric Value *const PermX = 3208bcb0991SDimitry Andric B.CreateCall(PermLaneX16, {V, V, B.getInt32(-1), B.getInt32(-1), 3218bcb0991SDimitry Andric B.getFalse(), B.getFalse()}); 3228bcb0991SDimitry Andric V = buildNonAtomicBinOp( 3238bcb0991SDimitry Andric B, Op, V, 3248bcb0991SDimitry Andric B.CreateCall(UpdateDPP, 3258bcb0991SDimitry Andric {Identity, PermX, B.getInt32(DPP::QUAD_PERM_ID), 3268bcb0991SDimitry Andric B.getInt32(0xa), B.getInt32(0xf), B.getFalse()})); 3278bcb0991SDimitry Andric if (!ST->isWave32()) { 3288bcb0991SDimitry Andric // Combine lane 31 into lanes 32..63. 3298bcb0991SDimitry Andric Value *const Lane31 = B.CreateCall(ReadLane, {V, B.getInt32(31)}); 3308bcb0991SDimitry Andric V = buildNonAtomicBinOp( 3318bcb0991SDimitry Andric B, Op, V, 3328bcb0991SDimitry Andric B.CreateCall(UpdateDPP, 3338bcb0991SDimitry Andric {Identity, Lane31, B.getInt32(DPP::QUAD_PERM_ID), 3348bcb0991SDimitry Andric B.getInt32(0xc), B.getInt32(0xf), B.getFalse()})); 3358bcb0991SDimitry Andric } 3368bcb0991SDimitry Andric } 3378bcb0991SDimitry Andric return V; 3388bcb0991SDimitry Andric } 3398bcb0991SDimitry Andric 3408bcb0991SDimitry Andric // Use the builder to create a shift right of V across the wavefront, with all 3418bcb0991SDimitry Andric // lanes active, to turn an inclusive scan into an exclusive scan. 3428bcb0991SDimitry Andric Value *AMDGPUAtomicOptimizer::buildShiftRight(IRBuilder<> &B, Value *V, 3438bcb0991SDimitry Andric Value *const Identity) const { 3448bcb0991SDimitry Andric Type *const Ty = V->getType(); 3458bcb0991SDimitry Andric Module *M = B.GetInsertBlock()->getModule(); 3468bcb0991SDimitry Andric Function *UpdateDPP = 3478bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_update_dpp, Ty); 3488bcb0991SDimitry Andric Function *ReadLane = 3498bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_readlane, {}); 3508bcb0991SDimitry Andric Function *WriteLane = 3518bcb0991SDimitry Andric Intrinsic::getDeclaration(M, Intrinsic::amdgcn_writelane, {}); 3528bcb0991SDimitry Andric 3538bcb0991SDimitry Andric if (ST->hasDPPWavefrontShifts()) { 3548bcb0991SDimitry Andric // GFX9 has DPP wavefront shift operations. 3558bcb0991SDimitry Andric V = B.CreateCall(UpdateDPP, 3568bcb0991SDimitry Andric {Identity, V, B.getInt32(DPP::WAVE_SHR1), B.getInt32(0xf), 3578bcb0991SDimitry Andric B.getInt32(0xf), B.getFalse()}); 3588bcb0991SDimitry Andric } else { 3598bcb0991SDimitry Andric // On GFX10 all DPP operations are confined to a single row. To get cross- 3608bcb0991SDimitry Andric // row operations we have to use permlane or readlane. 3618bcb0991SDimitry Andric Value *Old = V; 3628bcb0991SDimitry Andric V = B.CreateCall(UpdateDPP, 3638bcb0991SDimitry Andric {Identity, V, B.getInt32(DPP::ROW_SHR0 + 1), 3648bcb0991SDimitry Andric B.getInt32(0xf), B.getInt32(0xf), B.getFalse()}); 3658bcb0991SDimitry Andric 3668bcb0991SDimitry Andric // Copy the old lane 15 to the new lane 16. 3678bcb0991SDimitry Andric V = B.CreateCall(WriteLane, {B.CreateCall(ReadLane, {Old, B.getInt32(15)}), 3688bcb0991SDimitry Andric B.getInt32(16), V}); 3698bcb0991SDimitry Andric 3708bcb0991SDimitry Andric if (!ST->isWave32()) { 3718bcb0991SDimitry Andric // Copy the old lane 31 to the new lane 32. 3728bcb0991SDimitry Andric V = B.CreateCall( 3738bcb0991SDimitry Andric WriteLane, 3748bcb0991SDimitry Andric {B.CreateCall(ReadLane, {Old, B.getInt32(31)}), B.getInt32(32), V}); 3758bcb0991SDimitry Andric 3768bcb0991SDimitry Andric // Copy the old lane 47 to the new lane 48. 3778bcb0991SDimitry Andric V = B.CreateCall( 3788bcb0991SDimitry Andric WriteLane, 3798bcb0991SDimitry Andric {B.CreateCall(ReadLane, {Old, B.getInt32(47)}), B.getInt32(48), V}); 3808bcb0991SDimitry Andric } 3818bcb0991SDimitry Andric } 3828bcb0991SDimitry Andric 3838bcb0991SDimitry Andric return V; 3848bcb0991SDimitry Andric } 3858bcb0991SDimitry Andric 386*0b57cec5SDimitry Andric static APInt getIdentityValueForAtomicOp(AtomicRMWInst::BinOp Op, 387*0b57cec5SDimitry Andric unsigned BitWidth) { 388*0b57cec5SDimitry Andric switch (Op) { 389*0b57cec5SDimitry Andric default: 390*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic op"); 391*0b57cec5SDimitry Andric case AtomicRMWInst::Add: 392*0b57cec5SDimitry Andric case AtomicRMWInst::Sub: 393*0b57cec5SDimitry Andric case AtomicRMWInst::Or: 394*0b57cec5SDimitry Andric case AtomicRMWInst::Xor: 395*0b57cec5SDimitry Andric case AtomicRMWInst::UMax: 396*0b57cec5SDimitry Andric return APInt::getMinValue(BitWidth); 397*0b57cec5SDimitry Andric case AtomicRMWInst::And: 398*0b57cec5SDimitry Andric case AtomicRMWInst::UMin: 399*0b57cec5SDimitry Andric return APInt::getMaxValue(BitWidth); 400*0b57cec5SDimitry Andric case AtomicRMWInst::Max: 401*0b57cec5SDimitry Andric return APInt::getSignedMinValue(BitWidth); 402*0b57cec5SDimitry Andric case AtomicRMWInst::Min: 403*0b57cec5SDimitry Andric return APInt::getSignedMaxValue(BitWidth); 404*0b57cec5SDimitry Andric } 405*0b57cec5SDimitry Andric } 406*0b57cec5SDimitry Andric 407*0b57cec5SDimitry Andric void AMDGPUAtomicOptimizer::optimizeAtomic(Instruction &I, 408*0b57cec5SDimitry Andric AtomicRMWInst::BinOp Op, 409*0b57cec5SDimitry Andric unsigned ValIdx, 410*0b57cec5SDimitry Andric bool ValDivergent) const { 411*0b57cec5SDimitry Andric // Start building just before the instruction. 412*0b57cec5SDimitry Andric IRBuilder<> B(&I); 413*0b57cec5SDimitry Andric 414*0b57cec5SDimitry Andric // If we are in a pixel shader, because of how we have to mask out helper 415*0b57cec5SDimitry Andric // lane invocations, we need to record the entry and exit BB's. 416*0b57cec5SDimitry Andric BasicBlock *PixelEntryBB = nullptr; 417*0b57cec5SDimitry Andric BasicBlock *PixelExitBB = nullptr; 418*0b57cec5SDimitry Andric 419*0b57cec5SDimitry Andric // If we're optimizing an atomic within a pixel shader, we need to wrap the 420*0b57cec5SDimitry Andric // entire atomic operation in a helper-lane check. We do not want any helper 421*0b57cec5SDimitry Andric // lanes that are around only for the purposes of derivatives to take part 422*0b57cec5SDimitry Andric // in any cross-lane communication, and we use a branch on whether the lane is 423*0b57cec5SDimitry Andric // live to do this. 424*0b57cec5SDimitry Andric if (IsPixelShader) { 425*0b57cec5SDimitry Andric // Record I's original position as the entry block. 426*0b57cec5SDimitry Andric PixelEntryBB = I.getParent(); 427*0b57cec5SDimitry Andric 428*0b57cec5SDimitry Andric Value *const Cond = B.CreateIntrinsic(Intrinsic::amdgcn_ps_live, {}, {}); 429*0b57cec5SDimitry Andric Instruction *const NonHelperTerminator = 430*0b57cec5SDimitry Andric SplitBlockAndInsertIfThen(Cond, &I, false, nullptr, DT, nullptr); 431*0b57cec5SDimitry Andric 432*0b57cec5SDimitry Andric // Record I's new position as the exit block. 433*0b57cec5SDimitry Andric PixelExitBB = I.getParent(); 434*0b57cec5SDimitry Andric 435*0b57cec5SDimitry Andric I.moveBefore(NonHelperTerminator); 436*0b57cec5SDimitry Andric B.SetInsertPoint(&I); 437*0b57cec5SDimitry Andric } 438*0b57cec5SDimitry Andric 439*0b57cec5SDimitry Andric Type *const Ty = I.getType(); 440*0b57cec5SDimitry Andric const unsigned TyBitWidth = DL->getTypeSizeInBits(Ty); 441*0b57cec5SDimitry Andric Type *const VecTy = VectorType::get(B.getInt32Ty(), 2); 442*0b57cec5SDimitry Andric 443*0b57cec5SDimitry Andric // This is the value in the atomic operation we need to combine in order to 444*0b57cec5SDimitry Andric // reduce the number of atomic operations. 445*0b57cec5SDimitry Andric Value *const V = I.getOperand(ValIdx); 446*0b57cec5SDimitry Andric 447*0b57cec5SDimitry Andric // We need to know how many lanes are active within the wavefront, and we do 448*0b57cec5SDimitry Andric // this by doing a ballot of active lanes. 4498bcb0991SDimitry Andric Type *const WaveTy = B.getIntNTy(ST->getWavefrontSize()); 450*0b57cec5SDimitry Andric CallInst *const Ballot = B.CreateIntrinsic( 4518bcb0991SDimitry Andric Intrinsic::amdgcn_icmp, {WaveTy, B.getInt32Ty()}, 452*0b57cec5SDimitry Andric {B.getInt32(1), B.getInt32(0), B.getInt32(CmpInst::ICMP_NE)}); 453*0b57cec5SDimitry Andric 454*0b57cec5SDimitry Andric // We need to know how many lanes are active within the wavefront that are 455*0b57cec5SDimitry Andric // below us. If we counted each lane linearly starting from 0, a lane is 456*0b57cec5SDimitry Andric // below us only if its associated index was less than ours. We do this by 457*0b57cec5SDimitry Andric // using the mbcnt intrinsic. 4588bcb0991SDimitry Andric Value *Mbcnt; 4598bcb0991SDimitry Andric if (ST->isWave32()) { 4608bcb0991SDimitry Andric Mbcnt = B.CreateIntrinsic(Intrinsic::amdgcn_mbcnt_lo, {}, 4618bcb0991SDimitry Andric {Ballot, B.getInt32(0)}); 4628bcb0991SDimitry Andric } else { 463*0b57cec5SDimitry Andric Value *const BitCast = B.CreateBitCast(Ballot, VecTy); 464*0b57cec5SDimitry Andric Value *const ExtractLo = B.CreateExtractElement(BitCast, B.getInt32(0)); 465*0b57cec5SDimitry Andric Value *const ExtractHi = B.CreateExtractElement(BitCast, B.getInt32(1)); 4668bcb0991SDimitry Andric Mbcnt = B.CreateIntrinsic(Intrinsic::amdgcn_mbcnt_lo, {}, 4678bcb0991SDimitry Andric {ExtractLo, B.getInt32(0)}); 4688bcb0991SDimitry Andric Mbcnt = 4698bcb0991SDimitry Andric B.CreateIntrinsic(Intrinsic::amdgcn_mbcnt_hi, {}, {ExtractHi, Mbcnt}); 4708bcb0991SDimitry Andric } 4718bcb0991SDimitry Andric Mbcnt = B.CreateIntCast(Mbcnt, Ty, false); 472*0b57cec5SDimitry Andric 473*0b57cec5SDimitry Andric Value *const Identity = B.getInt(getIdentityValueForAtomicOp(Op, TyBitWidth)); 474*0b57cec5SDimitry Andric 475*0b57cec5SDimitry Andric Value *ExclScan = nullptr; 476*0b57cec5SDimitry Andric Value *NewV = nullptr; 477*0b57cec5SDimitry Andric 478*0b57cec5SDimitry Andric // If we have a divergent value in each lane, we need to combine the value 479*0b57cec5SDimitry Andric // using DPP. 480*0b57cec5SDimitry Andric if (ValDivergent) { 481*0b57cec5SDimitry Andric // First we need to set all inactive invocations to the identity value, so 482*0b57cec5SDimitry Andric // that they can correctly contribute to the final result. 4838bcb0991SDimitry Andric NewV = B.CreateIntrinsic(Intrinsic::amdgcn_set_inactive, Ty, {V, Identity}); 484*0b57cec5SDimitry Andric 4858bcb0991SDimitry Andric const AtomicRMWInst::BinOp ScanOp = 4868bcb0991SDimitry Andric Op == AtomicRMWInst::Sub ? AtomicRMWInst::Add : Op; 4878bcb0991SDimitry Andric NewV = buildScan(B, ScanOp, NewV, Identity); 4888bcb0991SDimitry Andric ExclScan = buildShiftRight(B, NewV, Identity); 489*0b57cec5SDimitry Andric 490*0b57cec5SDimitry Andric // Read the value from the last lane, which has accumlated the values of 491*0b57cec5SDimitry Andric // each active lane in the wavefront. This will be our new value which we 492*0b57cec5SDimitry Andric // will provide to the atomic operation. 4938bcb0991SDimitry Andric Value *const LastLaneIdx = B.getInt32(ST->getWavefrontSize() - 1); 494*0b57cec5SDimitry Andric if (TyBitWidth == 64) { 495*0b57cec5SDimitry Andric Value *const ExtractLo = B.CreateTrunc(NewV, B.getInt32Ty()); 496*0b57cec5SDimitry Andric Value *const ExtractHi = 4978bcb0991SDimitry Andric B.CreateTrunc(B.CreateLShr(NewV, 32), B.getInt32Ty()); 498*0b57cec5SDimitry Andric CallInst *const ReadLaneLo = B.CreateIntrinsic( 4998bcb0991SDimitry Andric Intrinsic::amdgcn_readlane, {}, {ExtractLo, LastLaneIdx}); 500*0b57cec5SDimitry Andric CallInst *const ReadLaneHi = B.CreateIntrinsic( 5018bcb0991SDimitry Andric Intrinsic::amdgcn_readlane, {}, {ExtractHi, LastLaneIdx}); 502*0b57cec5SDimitry Andric Value *const PartialInsert = B.CreateInsertElement( 503*0b57cec5SDimitry Andric UndefValue::get(VecTy), ReadLaneLo, B.getInt32(0)); 504*0b57cec5SDimitry Andric Value *const Insert = 505*0b57cec5SDimitry Andric B.CreateInsertElement(PartialInsert, ReadLaneHi, B.getInt32(1)); 506*0b57cec5SDimitry Andric NewV = B.CreateBitCast(Insert, Ty); 507*0b57cec5SDimitry Andric } else if (TyBitWidth == 32) { 508*0b57cec5SDimitry Andric NewV = B.CreateIntrinsic(Intrinsic::amdgcn_readlane, {}, 5098bcb0991SDimitry Andric {NewV, LastLaneIdx}); 510*0b57cec5SDimitry Andric } else { 511*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic bit width"); 512*0b57cec5SDimitry Andric } 513*0b57cec5SDimitry Andric 514*0b57cec5SDimitry Andric // Finally mark the readlanes in the WWM section. 515*0b57cec5SDimitry Andric NewV = B.CreateIntrinsic(Intrinsic::amdgcn_wwm, Ty, NewV); 516*0b57cec5SDimitry Andric } else { 517*0b57cec5SDimitry Andric switch (Op) { 518*0b57cec5SDimitry Andric default: 519*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic op"); 520*0b57cec5SDimitry Andric 521*0b57cec5SDimitry Andric case AtomicRMWInst::Add: 522*0b57cec5SDimitry Andric case AtomicRMWInst::Sub: { 523*0b57cec5SDimitry Andric // The new value we will be contributing to the atomic operation is the 524*0b57cec5SDimitry Andric // old value times the number of active lanes. 525*0b57cec5SDimitry Andric Value *const Ctpop = B.CreateIntCast( 526*0b57cec5SDimitry Andric B.CreateUnaryIntrinsic(Intrinsic::ctpop, Ballot), Ty, false); 527*0b57cec5SDimitry Andric NewV = B.CreateMul(V, Ctpop); 528*0b57cec5SDimitry Andric break; 529*0b57cec5SDimitry Andric } 530*0b57cec5SDimitry Andric 531*0b57cec5SDimitry Andric case AtomicRMWInst::And: 532*0b57cec5SDimitry Andric case AtomicRMWInst::Or: 533*0b57cec5SDimitry Andric case AtomicRMWInst::Max: 534*0b57cec5SDimitry Andric case AtomicRMWInst::Min: 535*0b57cec5SDimitry Andric case AtomicRMWInst::UMax: 536*0b57cec5SDimitry Andric case AtomicRMWInst::UMin: 537*0b57cec5SDimitry Andric // These operations with a uniform value are idempotent: doing the atomic 538*0b57cec5SDimitry Andric // operation multiple times has the same effect as doing it once. 539*0b57cec5SDimitry Andric NewV = V; 540*0b57cec5SDimitry Andric break; 541*0b57cec5SDimitry Andric 542*0b57cec5SDimitry Andric case AtomicRMWInst::Xor: 543*0b57cec5SDimitry Andric // The new value we will be contributing to the atomic operation is the 544*0b57cec5SDimitry Andric // old value times the parity of the number of active lanes. 545*0b57cec5SDimitry Andric Value *const Ctpop = B.CreateIntCast( 546*0b57cec5SDimitry Andric B.CreateUnaryIntrinsic(Intrinsic::ctpop, Ballot), Ty, false); 547*0b57cec5SDimitry Andric NewV = B.CreateMul(V, B.CreateAnd(Ctpop, 1)); 548*0b57cec5SDimitry Andric break; 549*0b57cec5SDimitry Andric } 550*0b57cec5SDimitry Andric } 551*0b57cec5SDimitry Andric 552*0b57cec5SDimitry Andric // We only want a single lane to enter our new control flow, and we do this 553*0b57cec5SDimitry Andric // by checking if there are any active lanes below us. Only one lane will 554*0b57cec5SDimitry Andric // have 0 active lanes below us, so that will be the only one to progress. 555*0b57cec5SDimitry Andric Value *const Cond = B.CreateICmpEQ(Mbcnt, B.getIntN(TyBitWidth, 0)); 556*0b57cec5SDimitry Andric 557*0b57cec5SDimitry Andric // Store I's original basic block before we split the block. 558*0b57cec5SDimitry Andric BasicBlock *const EntryBB = I.getParent(); 559*0b57cec5SDimitry Andric 560*0b57cec5SDimitry Andric // We need to introduce some new control flow to force a single lane to be 561*0b57cec5SDimitry Andric // active. We do this by splitting I's basic block at I, and introducing the 562*0b57cec5SDimitry Andric // new block such that: 563*0b57cec5SDimitry Andric // entry --> single_lane -\ 564*0b57cec5SDimitry Andric // \------------------> exit 565*0b57cec5SDimitry Andric Instruction *const SingleLaneTerminator = 566*0b57cec5SDimitry Andric SplitBlockAndInsertIfThen(Cond, &I, false, nullptr, DT, nullptr); 567*0b57cec5SDimitry Andric 568*0b57cec5SDimitry Andric // Move the IR builder into single_lane next. 569*0b57cec5SDimitry Andric B.SetInsertPoint(SingleLaneTerminator); 570*0b57cec5SDimitry Andric 571*0b57cec5SDimitry Andric // Clone the original atomic operation into single lane, replacing the 572*0b57cec5SDimitry Andric // original value with our newly created one. 573*0b57cec5SDimitry Andric Instruction *const NewI = I.clone(); 574*0b57cec5SDimitry Andric B.Insert(NewI); 575*0b57cec5SDimitry Andric NewI->setOperand(ValIdx, NewV); 576*0b57cec5SDimitry Andric 577*0b57cec5SDimitry Andric // Move the IR builder into exit next, and start inserting just before the 578*0b57cec5SDimitry Andric // original instruction. 579*0b57cec5SDimitry Andric B.SetInsertPoint(&I); 580*0b57cec5SDimitry Andric 5818bcb0991SDimitry Andric const bool NeedResult = !I.use_empty(); 5828bcb0991SDimitry Andric if (NeedResult) { 583*0b57cec5SDimitry Andric // Create a PHI node to get our new atomic result into the exit block. 584*0b57cec5SDimitry Andric PHINode *const PHI = B.CreatePHI(Ty, 2); 585*0b57cec5SDimitry Andric PHI->addIncoming(UndefValue::get(Ty), EntryBB); 586*0b57cec5SDimitry Andric PHI->addIncoming(NewI, SingleLaneTerminator->getParent()); 587*0b57cec5SDimitry Andric 588*0b57cec5SDimitry Andric // We need to broadcast the value who was the lowest active lane (the first 589*0b57cec5SDimitry Andric // lane) to all other lanes in the wavefront. We use an intrinsic for this, 590*0b57cec5SDimitry Andric // but have to handle 64-bit broadcasts with two calls to this intrinsic. 591*0b57cec5SDimitry Andric Value *BroadcastI = nullptr; 592*0b57cec5SDimitry Andric 593*0b57cec5SDimitry Andric if (TyBitWidth == 64) { 594*0b57cec5SDimitry Andric Value *const ExtractLo = B.CreateTrunc(PHI, B.getInt32Ty()); 595*0b57cec5SDimitry Andric Value *const ExtractHi = 5968bcb0991SDimitry Andric B.CreateTrunc(B.CreateLShr(PHI, 32), B.getInt32Ty()); 597*0b57cec5SDimitry Andric CallInst *const ReadFirstLaneLo = 598*0b57cec5SDimitry Andric B.CreateIntrinsic(Intrinsic::amdgcn_readfirstlane, {}, ExtractLo); 599*0b57cec5SDimitry Andric CallInst *const ReadFirstLaneHi = 600*0b57cec5SDimitry Andric B.CreateIntrinsic(Intrinsic::amdgcn_readfirstlane, {}, ExtractHi); 601*0b57cec5SDimitry Andric Value *const PartialInsert = B.CreateInsertElement( 602*0b57cec5SDimitry Andric UndefValue::get(VecTy), ReadFirstLaneLo, B.getInt32(0)); 603*0b57cec5SDimitry Andric Value *const Insert = 604*0b57cec5SDimitry Andric B.CreateInsertElement(PartialInsert, ReadFirstLaneHi, B.getInt32(1)); 605*0b57cec5SDimitry Andric BroadcastI = B.CreateBitCast(Insert, Ty); 606*0b57cec5SDimitry Andric } else if (TyBitWidth == 32) { 607*0b57cec5SDimitry Andric 608*0b57cec5SDimitry Andric BroadcastI = B.CreateIntrinsic(Intrinsic::amdgcn_readfirstlane, {}, PHI); 609*0b57cec5SDimitry Andric } else { 610*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic bit width"); 611*0b57cec5SDimitry Andric } 612*0b57cec5SDimitry Andric 613*0b57cec5SDimitry Andric // Now that we have the result of our single atomic operation, we need to 6148bcb0991SDimitry Andric // get our individual lane's slice into the result. We use the lane offset 6158bcb0991SDimitry Andric // we previously calculated combined with the atomic result value we got 6168bcb0991SDimitry Andric // from the first lane, to get our lane's index into the atomic result. 617*0b57cec5SDimitry Andric Value *LaneOffset = nullptr; 618*0b57cec5SDimitry Andric if (ValDivergent) { 619*0b57cec5SDimitry Andric LaneOffset = B.CreateIntrinsic(Intrinsic::amdgcn_wwm, Ty, ExclScan); 620*0b57cec5SDimitry Andric } else { 621*0b57cec5SDimitry Andric switch (Op) { 622*0b57cec5SDimitry Andric default: 623*0b57cec5SDimitry Andric llvm_unreachable("Unhandled atomic op"); 624*0b57cec5SDimitry Andric case AtomicRMWInst::Add: 625*0b57cec5SDimitry Andric case AtomicRMWInst::Sub: 626*0b57cec5SDimitry Andric LaneOffset = B.CreateMul(V, Mbcnt); 627*0b57cec5SDimitry Andric break; 628*0b57cec5SDimitry Andric case AtomicRMWInst::And: 629*0b57cec5SDimitry Andric case AtomicRMWInst::Or: 630*0b57cec5SDimitry Andric case AtomicRMWInst::Max: 631*0b57cec5SDimitry Andric case AtomicRMWInst::Min: 632*0b57cec5SDimitry Andric case AtomicRMWInst::UMax: 633*0b57cec5SDimitry Andric case AtomicRMWInst::UMin: 634*0b57cec5SDimitry Andric LaneOffset = B.CreateSelect(Cond, Identity, V); 635*0b57cec5SDimitry Andric break; 636*0b57cec5SDimitry Andric case AtomicRMWInst::Xor: 637*0b57cec5SDimitry Andric LaneOffset = B.CreateMul(V, B.CreateAnd(Mbcnt, 1)); 638*0b57cec5SDimitry Andric break; 639*0b57cec5SDimitry Andric } 640*0b57cec5SDimitry Andric } 641*0b57cec5SDimitry Andric Value *const Result = buildNonAtomicBinOp(B, Op, BroadcastI, LaneOffset); 642*0b57cec5SDimitry Andric 643*0b57cec5SDimitry Andric if (IsPixelShader) { 644*0b57cec5SDimitry Andric // Need a final PHI to reconverge to above the helper lane branch mask. 645*0b57cec5SDimitry Andric B.SetInsertPoint(PixelExitBB->getFirstNonPHI()); 646*0b57cec5SDimitry Andric 647*0b57cec5SDimitry Andric PHINode *const PHI = B.CreatePHI(Ty, 2); 648*0b57cec5SDimitry Andric PHI->addIncoming(UndefValue::get(Ty), PixelEntryBB); 649*0b57cec5SDimitry Andric PHI->addIncoming(Result, I.getParent()); 650*0b57cec5SDimitry Andric I.replaceAllUsesWith(PHI); 651*0b57cec5SDimitry Andric } else { 652*0b57cec5SDimitry Andric // Replace the original atomic instruction with the new one. 653*0b57cec5SDimitry Andric I.replaceAllUsesWith(Result); 654*0b57cec5SDimitry Andric } 6558bcb0991SDimitry Andric } 656*0b57cec5SDimitry Andric 657*0b57cec5SDimitry Andric // And delete the original. 658*0b57cec5SDimitry Andric I.eraseFromParent(); 659*0b57cec5SDimitry Andric } 660*0b57cec5SDimitry Andric 661*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(AMDGPUAtomicOptimizer, DEBUG_TYPE, 662*0b57cec5SDimitry Andric "AMDGPU atomic optimizations", false, false) 663*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(LegacyDivergenceAnalysis) 664*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) 665*0b57cec5SDimitry Andric INITIALIZE_PASS_END(AMDGPUAtomicOptimizer, DEBUG_TYPE, 666*0b57cec5SDimitry Andric "AMDGPU atomic optimizations", false, false) 667*0b57cec5SDimitry Andric 668*0b57cec5SDimitry Andric FunctionPass *llvm::createAMDGPUAtomicOptimizerPass() { 669*0b57cec5SDimitry Andric return new AMDGPUAtomicOptimizer(); 670*0b57cec5SDimitry Andric } 671