//===-- SIFixWWMLiveness.cpp - Fix WWM live intervals ---------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // /// \file /// Computations in WWM can overwrite values in inactive channels for /// variables that the register allocator thinks are dead. This pass adds fake /// uses of those variables to WWM instructions to make sure that they aren't /// overwritten. /// /// As an example, consider this snippet: /// %vgpr0 = V_MOV_B32_e32 0.0 /// if (...) { /// %vgpr1 = ... /// %vgpr2 = WWM killed %vgpr1 /// ... = killed %vgpr2 /// %vgpr0 = V_MOV_B32_e32 1.0 /// } /// ... = %vgpr0 /// /// The live intervals of %vgpr0 don't overlap with those of %vgpr1. Normally, /// we can safely allocate %vgpr0 and %vgpr1 in the same register, since /// writing %vgpr1 would only write to channels that would be clobbered by the /// second write to %vgpr0 anyways. But if %vgpr1 is written with WWM enabled, /// it would clobber even the inactive channels for which the if-condition is /// false, for which %vgpr0 is supposed to be 0. This pass adds an implicit use /// of %vgpr0 to the WWM instruction to make sure they aren't allocated to the /// same register. /// /// In general, we need to figure out what registers might have their inactive /// channels which are eventually used accidentally clobbered by a WWM /// instruction. We approximate this using two conditions: /// /// 1. A definition of the variable reaches the WWM instruction. /// 2. The variable would be live at the WWM instruction if all its defs were /// partial defs (i.e. considered as a use), ignoring normal uses. /// /// If a register matches both conditions, then we add an implicit use of it to /// the WWM instruction. Condition #2 is the heart of the matter: every /// definition is really a partial definition, since every VALU instruction is /// implicitly predicated. We can usually ignore this, but WWM forces us not /// to. Condition #1 prevents false positives if the variable is undefined at /// the WWM instruction anyways. This is overly conservative in certain cases, /// especially in uniform control flow, but this is a workaround anyways until /// LLVM gains the notion of predicated uses and definitions of variables. /// //===----------------------------------------------------------------------===// #include "AMDGPU.h" #include "AMDGPUSubtarget.h" #include "SIInstrInfo.h" #include "SIRegisterInfo.h" #include "MCTargetDesc/AMDGPUMCTargetDesc.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/SparseBitVector.h" #include "llvm/CodeGen/LiveIntervals.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetRegisterInfo.h" using namespace llvm; #define DEBUG_TYPE "si-fix-wwm-liveness" namespace { class SIFixWWMLiveness : public MachineFunctionPass { private: LiveIntervals *LIS = nullptr; const SIRegisterInfo *TRI; MachineRegisterInfo *MRI; public: static char ID; SIFixWWMLiveness() : MachineFunctionPass(ID) { initializeSIFixWWMLivenessPass(*PassRegistry::getPassRegistry()); } bool runOnMachineFunction(MachineFunction &MF) override; bool runOnWWMInstruction(MachineInstr &MI); void addDefs(const MachineInstr &MI, SparseBitVector<> &set); StringRef getPassName() const override { return "SI Fix WWM Liveness"; } void getAnalysisUsage(AnalysisUsage &AU) const override { // Should preserve the same set that TwoAddressInstructions does. AU.addPreserved<SlotIndexes>(); AU.addPreserved<LiveIntervals>(); AU.addPreservedID(LiveVariablesID); AU.addPreservedID(MachineLoopInfoID); AU.addPreservedID(MachineDominatorsID); AU.setPreservesCFG(); MachineFunctionPass::getAnalysisUsage(AU); } }; } // End anonymous namespace. INITIALIZE_PASS(SIFixWWMLiveness, DEBUG_TYPE, "SI fix WWM liveness", false, false) char SIFixWWMLiveness::ID = 0; char &llvm::SIFixWWMLivenessID = SIFixWWMLiveness::ID; FunctionPass *llvm::createSIFixWWMLivenessPass() { return new SIFixWWMLiveness(); } void SIFixWWMLiveness::addDefs(const MachineInstr &MI, SparseBitVector<> &Regs) { for (const MachineOperand &Op : MI.defs()) { if (Op.isReg()) { unsigned Reg = Op.getReg(); if (TRI->isVGPR(*MRI, Reg)) Regs.set(Reg); } } } bool SIFixWWMLiveness::runOnWWMInstruction(MachineInstr &WWM) { MachineBasicBlock *MBB = WWM.getParent(); // Compute the registers that are live out of MI by figuring out which defs // are reachable from MI. SparseBitVector<> LiveOut; for (auto II = MachineBasicBlock::iterator(WWM), IE = MBB->end(); II != IE; ++II) { addDefs(*II, LiveOut); } for (df_iterator<MachineBasicBlock *> I = ++df_begin(MBB), E = df_end(MBB); I != E; ++I) { for (const MachineInstr &MI : **I) { addDefs(MI, LiveOut); } } // Compute the registers that reach MI. SparseBitVector<> Reachable; for (auto II = ++MachineBasicBlock::reverse_iterator(WWM), IE = MBB->rend(); II != IE; ++II) { addDefs(*II, Reachable); } for (idf_iterator<MachineBasicBlock *> I = ++idf_begin(MBB), E = idf_end(MBB); I != E; ++I) { for (const MachineInstr &MI : **I) { addDefs(MI, Reachable); } } // find the intersection, and add implicit uses. LiveOut &= Reachable; bool Modified = false; for (unsigned Reg : LiveOut) { WWM.addOperand(MachineOperand::CreateReg(Reg, false, /*isImp=*/true)); if (LIS) { // FIXME: is there a better way to update the live interval? LIS->removeInterval(Reg); LIS->createAndComputeVirtRegInterval(Reg); } Modified = true; } return Modified; } bool SIFixWWMLiveness::runOnMachineFunction(MachineFunction &MF) { bool Modified = false; // This doesn't actually need LiveIntervals, but we can preserve them. LIS = getAnalysisIfAvailable<LiveIntervals>(); const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>(); const SIInstrInfo *TII = ST.getInstrInfo(); TRI = &TII->getRegisterInfo(); MRI = &MF.getRegInfo(); for (MachineBasicBlock &MBB : MF) { for (MachineInstr &MI : MBB) { if (MI.getOpcode() == AMDGPU::EXIT_WWM) { Modified |= runOnWWMInstruction(MI); } } } return Modified; }