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