1 //===- SIMemoryLegalizer.cpp ----------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 /// \file 11 /// \brief Memory legalizer - implements memory model. More information can be 12 /// found here: 13 /// http://llvm.org/docs/AMDGPUUsage.html#memory-model 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "AMDGPU.h" 18 #include "AMDGPUMachineModuleInfo.h" 19 #include "AMDGPUSubtarget.h" 20 #include "SIDefines.h" 21 #include "SIInstrInfo.h" 22 #include "Utils/AMDGPUBaseInfo.h" 23 #include "llvm/ADT/None.h" 24 #include "llvm/ADT/Optional.h" 25 #include "llvm/CodeGen/MachineBasicBlock.h" 26 #include "llvm/CodeGen/MachineFunction.h" 27 #include "llvm/CodeGen/MachineFunctionPass.h" 28 #include "llvm/CodeGen/MachineInstrBuilder.h" 29 #include "llvm/CodeGen/MachineMemOperand.h" 30 #include "llvm/CodeGen/MachineModuleInfo.h" 31 #include "llvm/CodeGen/MachineOperand.h" 32 #include "llvm/IR/DebugLoc.h" 33 #include "llvm/IR/DiagnosticInfo.h" 34 #include "llvm/IR/Function.h" 35 #include "llvm/IR/LLVMContext.h" 36 #include "llvm/MC/MCInstrDesc.h" 37 #include "llvm/Pass.h" 38 #include "llvm/Support/AtomicOrdering.h" 39 #include <cassert> 40 #include <list> 41 42 using namespace llvm; 43 using namespace llvm::AMDGPU; 44 45 #define DEBUG_TYPE "si-memory-legalizer" 46 #define PASS_NAME "SI Memory Legalizer" 47 48 namespace { 49 50 class SIMemoryLegalizer final : public MachineFunctionPass { 51 private: 52 struct MemOpInfo final { 53 SyncScope::ID SSID = SyncScope::System; 54 AtomicOrdering Ordering = AtomicOrdering::SequentiallyConsistent; 55 AtomicOrdering FailureOrdering = AtomicOrdering::SequentiallyConsistent; 56 57 MemOpInfo() = default; 58 59 MemOpInfo(SyncScope::ID SSID, 60 AtomicOrdering Ordering, 61 AtomicOrdering FailureOrdering) 62 : SSID(SSID), 63 Ordering(Ordering), 64 FailureOrdering(FailureOrdering) {} 65 66 MemOpInfo(const MachineMemOperand *MMO) 67 : SSID(MMO->getSyncScopeID()), 68 Ordering(MMO->getOrdering()), 69 FailureOrdering(MMO->getFailureOrdering()) {} 70 }; 71 72 /// \brief LLVM context. 73 LLVMContext *CTX = nullptr; 74 75 /// \brief Machine module info. 76 const AMDGPUMachineModuleInfo *MMI = nullptr; 77 78 /// \brief Instruction info. 79 const SIInstrInfo *TII = nullptr; 80 81 /// \brief Immediate for "vmcnt(0)". 82 unsigned Vmcnt0Immediate = 0; 83 84 /// \brief Opcode for cache invalidation instruction (L1). 85 unsigned Wbinvl1Opcode = 0; 86 87 /// \brief List of atomic pseudo instructions. 88 std::list<MachineBasicBlock::iterator> AtomicPseudoMIs; 89 90 /// \brief Inserts "buffer_wbinvl1_vol" instruction \p Before or after \p MI. 91 /// Always returns true. 92 bool insertBufferWbinvl1Vol(MachineBasicBlock::iterator &MI, 93 bool Before = true) const; 94 /// \brief Inserts "s_waitcnt vmcnt(0)" instruction \p Before or after \p MI. 95 /// Always returns true. 96 bool insertWaitcntVmcnt0(MachineBasicBlock::iterator &MI, 97 bool Before = true) const; 98 99 /// \brief Sets GLC bit if present in \p MI. Returns true if \p MI is 100 /// modified, false otherwise. 101 bool setGLC(const MachineBasicBlock::iterator &MI) const; 102 103 /// \brief Removes all processed atomic pseudo instructions from the current 104 /// function. Returns true if current function is modified, false otherwise. 105 bool removeAtomicPseudoMIs(); 106 107 /// \brief Reports unknown synchronization scope used in \p MI to LLVM 108 /// context. 109 void reportUnknownSynchScope(const MachineBasicBlock::iterator &MI); 110 111 /// \returns Atomic fence info if \p MI is an atomic fence operation, 112 /// "None" otherwise. 113 Optional<MemOpInfo> getAtomicFenceInfo( 114 const MachineBasicBlock::iterator &MI) const; 115 /// \returns Load info if \p MI is a load operation, "None" otherwise. 116 Optional<MemOpInfo> getLoadInfo(const MachineBasicBlock::iterator &MI) const; 117 /// \returns Store info if \p MI is a store operation, "None" otherwise. 118 Optional<MemOpInfo> getStoreInfo(const MachineBasicBlock::iterator &MI) const; 119 /// \returns Atomic cmpxchg info if \p MI is an atomic cmpxchg operation, 120 /// "None" otherwise. 121 Optional<MemOpInfo> getAtomicCmpxchgInfo( 122 const MachineBasicBlock::iterator &MI) const; 123 /// \returns Atomic rmw info if \p MI is an atomic rmw operation, 124 /// "None" otherwise. 125 Optional<MemOpInfo> getAtomicRmwInfo( 126 const MachineBasicBlock::iterator &MI) const; 127 128 /// \brief Expands atomic fence operation \p MI. Returns true if 129 /// instructions are added/deleted or \p MI is modified, false otherwise. 130 bool expandAtomicFence(const MemOpInfo &MOI, 131 MachineBasicBlock::iterator &MI); 132 /// \brief Expands load operation \p MI. Returns true if instructions are 133 /// added/deleted or \p MI is modified, false otherwise. 134 bool expandLoad(const MemOpInfo &MOI, MachineBasicBlock::iterator &MI); 135 /// \brief Expands store operation \p MI. Returns true if instructions are 136 /// added/deleted or \p MI is modified, false otherwise. 137 bool expandStore(const MemOpInfo &MOI, MachineBasicBlock::iterator &MI); 138 /// \brief Expands atomic cmpxchg operation \p MI. Returns true if 139 /// instructions are added/deleted or \p MI is modified, false otherwise. 140 bool expandAtomicCmpxchg(const MemOpInfo &MOI, 141 MachineBasicBlock::iterator &MI); 142 /// \brief Expands atomic rmw operation \p MI. Returns true if 143 /// instructions are added/deleted or \p MI is modified, false otherwise. 144 bool expandAtomicRmw(const MemOpInfo &MOI, 145 MachineBasicBlock::iterator &MI); 146 147 public: 148 static char ID; 149 150 SIMemoryLegalizer() : MachineFunctionPass(ID) {} 151 152 void getAnalysisUsage(AnalysisUsage &AU) const override { 153 AU.setPreservesCFG(); 154 MachineFunctionPass::getAnalysisUsage(AU); 155 } 156 157 StringRef getPassName() const override { 158 return PASS_NAME; 159 } 160 161 bool runOnMachineFunction(MachineFunction &MF) override; 162 }; 163 164 } // end namespace anonymous 165 166 bool SIMemoryLegalizer::insertBufferWbinvl1Vol(MachineBasicBlock::iterator &MI, 167 bool Before) const { 168 MachineBasicBlock &MBB = *MI->getParent(); 169 DebugLoc DL = MI->getDebugLoc(); 170 171 if (!Before) 172 ++MI; 173 174 BuildMI(MBB, MI, DL, TII->get(Wbinvl1Opcode)); 175 176 if (!Before) 177 --MI; 178 179 return true; 180 } 181 182 bool SIMemoryLegalizer::insertWaitcntVmcnt0(MachineBasicBlock::iterator &MI, 183 bool Before) const { 184 MachineBasicBlock &MBB = *MI->getParent(); 185 DebugLoc DL = MI->getDebugLoc(); 186 187 if (!Before) 188 ++MI; 189 190 BuildMI(MBB, MI, DL, TII->get(AMDGPU::S_WAITCNT)).addImm(Vmcnt0Immediate); 191 192 if (!Before) 193 --MI; 194 195 return true; 196 } 197 198 bool SIMemoryLegalizer::setGLC(const MachineBasicBlock::iterator &MI) const { 199 int GLCIdx = AMDGPU::getNamedOperandIdx(MI->getOpcode(), AMDGPU::OpName::glc); 200 if (GLCIdx == -1) 201 return false; 202 203 MachineOperand &GLC = MI->getOperand(GLCIdx); 204 if (GLC.getImm() == 1) 205 return false; 206 207 GLC.setImm(1); 208 return true; 209 } 210 211 bool SIMemoryLegalizer::removeAtomicPseudoMIs() { 212 if (AtomicPseudoMIs.empty()) 213 return false; 214 215 for (auto &MI : AtomicPseudoMIs) 216 MI->eraseFromParent(); 217 218 AtomicPseudoMIs.clear(); 219 return true; 220 } 221 222 void SIMemoryLegalizer::reportUnknownSynchScope( 223 const MachineBasicBlock::iterator &MI) { 224 DiagnosticInfoUnsupported Diag(*MI->getParent()->getParent()->getFunction(), 225 "Unsupported synchronization scope"); 226 CTX->diagnose(Diag); 227 } 228 229 Optional<SIMemoryLegalizer::MemOpInfo> SIMemoryLegalizer::getAtomicFenceInfo( 230 const MachineBasicBlock::iterator &MI) const { 231 assert(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic); 232 233 if (MI->getOpcode() != AMDGPU::ATOMIC_FENCE) 234 return None; 235 236 SyncScope::ID SSID = 237 static_cast<SyncScope::ID>(MI->getOperand(1).getImm()); 238 AtomicOrdering Ordering = 239 static_cast<AtomicOrdering>(MI->getOperand(0).getImm()); 240 return MemOpInfo(SSID, Ordering, AtomicOrdering::NotAtomic); 241 } 242 243 Optional<SIMemoryLegalizer::MemOpInfo> SIMemoryLegalizer::getLoadInfo( 244 const MachineBasicBlock::iterator &MI) const { 245 assert(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic); 246 247 if (!(MI->mayLoad() && !MI->mayStore())) 248 return None; 249 if (!MI->hasOneMemOperand()) 250 return MemOpInfo(); 251 252 const MachineMemOperand *MMO = *MI->memoperands_begin(); 253 if (!MMO->isAtomic()) 254 return None; 255 256 return MemOpInfo(MMO); 257 } 258 259 Optional<SIMemoryLegalizer::MemOpInfo> SIMemoryLegalizer::getStoreInfo( 260 const MachineBasicBlock::iterator &MI) const { 261 assert(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic); 262 263 if (!(!MI->mayLoad() && MI->mayStore())) 264 return None; 265 if (!MI->hasOneMemOperand()) 266 return MemOpInfo(); 267 268 const MachineMemOperand *MMO = *MI->memoperands_begin(); 269 if (!MMO->isAtomic()) 270 return None; 271 272 return MemOpInfo(MMO); 273 } 274 275 Optional<SIMemoryLegalizer::MemOpInfo> SIMemoryLegalizer::getAtomicCmpxchgInfo( 276 const MachineBasicBlock::iterator &MI) const { 277 assert(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic); 278 279 if (!(MI->mayLoad() && MI->mayStore())) 280 return None; 281 if (!MI->hasOneMemOperand()) 282 return MemOpInfo(); 283 284 const MachineMemOperand *MMO = *MI->memoperands_begin(); 285 if (!MMO->isAtomic()) 286 return None; 287 if (MMO->getFailureOrdering() == AtomicOrdering::NotAtomic) 288 return None; 289 290 return MemOpInfo(MMO); 291 } 292 293 Optional<SIMemoryLegalizer::MemOpInfo> SIMemoryLegalizer::getAtomicRmwInfo( 294 const MachineBasicBlock::iterator &MI) const { 295 assert(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic); 296 297 if (!(MI->mayLoad() && MI->mayStore())) 298 return None; 299 if (!MI->hasOneMemOperand()) 300 return MemOpInfo(); 301 302 const MachineMemOperand *MMO = *MI->memoperands_begin(); 303 if (!MMO->isAtomic()) 304 return None; 305 if (MMO->getFailureOrdering() != AtomicOrdering::NotAtomic) 306 return None; 307 308 return MemOpInfo(MMO); 309 } 310 311 bool SIMemoryLegalizer::expandAtomicFence(const MemOpInfo &MOI, 312 MachineBasicBlock::iterator &MI) { 313 assert(MI->getOpcode() == AMDGPU::ATOMIC_FENCE); 314 315 bool Changed = false; 316 if (MOI.SSID == SyncScope::System || 317 MOI.SSID == MMI->getAgentSSID()) { 318 if (MOI.Ordering == AtomicOrdering::Acquire || 319 MOI.Ordering == AtomicOrdering::Release || 320 MOI.Ordering == AtomicOrdering::AcquireRelease || 321 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 322 Changed |= insertWaitcntVmcnt0(MI); 323 324 if (MOI.Ordering == AtomicOrdering::Acquire || 325 MOI.Ordering == AtomicOrdering::AcquireRelease || 326 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 327 Changed |= insertBufferWbinvl1Vol(MI); 328 329 AtomicPseudoMIs.push_back(MI); 330 return Changed; 331 } else if (MOI.SSID == SyncScope::SingleThread || 332 MOI.SSID == MMI->getWorkgroupSSID() || 333 MOI.SSID == MMI->getWavefrontSSID()) { 334 AtomicPseudoMIs.push_back(MI); 335 return Changed; 336 } else { 337 reportUnknownSynchScope(MI); 338 return Changed; 339 } 340 } 341 342 bool SIMemoryLegalizer::expandLoad(const MemOpInfo &MOI, 343 MachineBasicBlock::iterator &MI) { 344 assert(MI->mayLoad() && !MI->mayStore()); 345 346 bool Changed = false; 347 if (MOI.SSID == SyncScope::System || 348 MOI.SSID == MMI->getAgentSSID()) { 349 if (MOI.Ordering == AtomicOrdering::Acquire || 350 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 351 Changed |= setGLC(MI); 352 353 if (MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 354 Changed |= insertWaitcntVmcnt0(MI); 355 356 if (MOI.Ordering == AtomicOrdering::Acquire || 357 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) { 358 Changed |= insertWaitcntVmcnt0(MI, false); 359 Changed |= insertBufferWbinvl1Vol(MI, false); 360 } 361 362 return Changed; 363 } else if (MOI.SSID == SyncScope::SingleThread || 364 MOI.SSID == MMI->getWorkgroupSSID() || 365 MOI.SSID == MMI->getWavefrontSSID()) { 366 return Changed; 367 } else { 368 reportUnknownSynchScope(MI); 369 return Changed; 370 } 371 } 372 373 bool SIMemoryLegalizer::expandStore(const MemOpInfo &MOI, 374 MachineBasicBlock::iterator &MI) { 375 assert(!MI->mayLoad() && MI->mayStore()); 376 377 bool Changed = false; 378 if (MOI.SSID == SyncScope::System || 379 MOI.SSID == MMI->getAgentSSID()) { 380 if (MOI.Ordering == AtomicOrdering::Release || 381 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 382 Changed |= insertWaitcntVmcnt0(MI); 383 384 return Changed; 385 } else if (MOI.SSID == SyncScope::SingleThread || 386 MOI.SSID == MMI->getWorkgroupSSID() || 387 MOI.SSID == MMI->getWavefrontSSID()) { 388 return Changed; 389 } else { 390 reportUnknownSynchScope(MI); 391 return Changed; 392 } 393 } 394 395 bool SIMemoryLegalizer::expandAtomicCmpxchg(const MemOpInfo &MOI, 396 MachineBasicBlock::iterator &MI) { 397 assert(MI->mayLoad() && MI->mayStore()); 398 399 bool Changed = false; 400 if (MOI.SSID == SyncScope::System || 401 MOI.SSID == MMI->getAgentSSID()) { 402 if (MOI.Ordering == AtomicOrdering::Release || 403 MOI.Ordering == AtomicOrdering::AcquireRelease || 404 MOI.Ordering == AtomicOrdering::SequentiallyConsistent || 405 MOI.FailureOrdering == AtomicOrdering::SequentiallyConsistent) 406 Changed |= insertWaitcntVmcnt0(MI); 407 408 if (MOI.Ordering == AtomicOrdering::Acquire || 409 MOI.Ordering == AtomicOrdering::AcquireRelease || 410 MOI.Ordering == AtomicOrdering::SequentiallyConsistent || 411 MOI.FailureOrdering == AtomicOrdering::Acquire || 412 MOI.FailureOrdering == AtomicOrdering::SequentiallyConsistent) { 413 Changed |= insertWaitcntVmcnt0(MI, false); 414 Changed |= insertBufferWbinvl1Vol(MI, false); 415 } 416 417 return Changed; 418 } else if (MOI.SSID == SyncScope::SingleThread || 419 MOI.SSID == MMI->getWorkgroupSSID() || 420 MOI.SSID == MMI->getWavefrontSSID()) { 421 Changed |= setGLC(MI); 422 return Changed; 423 } else { 424 reportUnknownSynchScope(MI); 425 return Changed; 426 } 427 } 428 429 bool SIMemoryLegalizer::expandAtomicRmw(const MemOpInfo &MOI, 430 MachineBasicBlock::iterator &MI) { 431 assert(MI->mayLoad() && MI->mayStore()); 432 433 bool Changed = false; 434 if (MOI.SSID == SyncScope::System || 435 MOI.SSID == MMI->getAgentSSID()) { 436 if (MOI.Ordering == AtomicOrdering::Release || 437 MOI.Ordering == AtomicOrdering::AcquireRelease || 438 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) 439 Changed |= insertWaitcntVmcnt0(MI); 440 441 if (MOI.Ordering == AtomicOrdering::Acquire || 442 MOI.Ordering == AtomicOrdering::AcquireRelease || 443 MOI.Ordering == AtomicOrdering::SequentiallyConsistent) { 444 Changed |= insertWaitcntVmcnt0(MI, false); 445 Changed |= insertBufferWbinvl1Vol(MI, false); 446 } 447 448 return Changed; 449 } else if (MOI.SSID == SyncScope::SingleThread || 450 MOI.SSID == MMI->getWorkgroupSSID() || 451 MOI.SSID == MMI->getWavefrontSSID()) { 452 Changed |= setGLC(MI); 453 return Changed; 454 } else { 455 reportUnknownSynchScope(MI); 456 return Changed; 457 } 458 } 459 460 bool SIMemoryLegalizer::runOnMachineFunction(MachineFunction &MF) { 461 bool Changed = false; 462 const SISubtarget &ST = MF.getSubtarget<SISubtarget>(); 463 const IsaInfo::IsaVersion IV = IsaInfo::getIsaVersion(ST.getFeatureBits()); 464 465 CTX = &MF.getFunction()->getContext(); 466 MMI = &MF.getMMI().getObjFileInfo<AMDGPUMachineModuleInfo>(); 467 TII = ST.getInstrInfo(); 468 469 Vmcnt0Immediate = 470 AMDGPU::encodeWaitcnt(IV, 0, getExpcntBitMask(IV), getLgkmcntBitMask(IV)); 471 Wbinvl1Opcode = ST.getGeneration() <= AMDGPUSubtarget::SOUTHERN_ISLANDS ? 472 AMDGPU::BUFFER_WBINVL1 : AMDGPU::BUFFER_WBINVL1_VOL; 473 474 for (auto &MBB : MF) { 475 for (auto MI = MBB.begin(); MI != MBB.end(); ++MI) { 476 if (!(MI->getDesc().TSFlags & SIInstrFlags::maybeAtomic)) 477 continue; 478 479 if (const auto &MOI = getAtomicFenceInfo(MI)) 480 Changed |= expandAtomicFence(MOI.getValue(), MI); 481 else if (const auto &MOI = getLoadInfo(MI)) 482 Changed |= expandLoad(MOI.getValue(), MI); 483 else if (const auto &MOI = getStoreInfo(MI)) 484 Changed |= expandStore(MOI.getValue(), MI); 485 else if (const auto &MOI = getAtomicCmpxchgInfo(MI)) 486 Changed |= expandAtomicCmpxchg(MOI.getValue(), MI); 487 else if (const auto &MOI = getAtomicRmwInfo(MI)) 488 Changed |= expandAtomicRmw(MOI.getValue(), MI); 489 } 490 } 491 492 Changed |= removeAtomicPseudoMIs(); 493 return Changed; 494 } 495 496 INITIALIZE_PASS(SIMemoryLegalizer, DEBUG_TYPE, PASS_NAME, false, false) 497 498 char SIMemoryLegalizer::ID = 0; 499 char &llvm::SIMemoryLegalizerID = SIMemoryLegalizer::ID; 500 501 FunctionPass *llvm::createSIMemoryLegalizerPass() { 502 return new SIMemoryLegalizer(); 503 } 504