| //===- GCNRegPressureTest.cpp -----------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "GCNRegPressure.h" | |
| #include "AMDGPUUnitTests.h" | |
| #include "GCNSubtarget.h" | |
| #include "SIRegisterInfo.h" | |
| #include "llvm/CodeGen/LiveIntervals.h" | |
| #include "llvm/CodeGen/MIRParser/MIRParser.h" | |
| #include "llvm/CodeGen/MachineFunctionAnalysis.h" | |
| #include "llvm/CodeGen/MachineModuleInfo.h" | |
| #include "llvm/CodeGen/MachineScheduler.h" | |
| #include "llvm/CodeGen/SelectionDAG.h" | |
| #include "llvm/CodeGen/TargetLowering.h" | |
| #include "llvm/Passes/PassBuilder.h" | |
| #include "gtest/gtest.h" | |
| using namespace llvm; | |
| class GCNRegPressureTest : public AMDGPUCodeGenTestBase { | |
| public: | |
| void SetUp() override { setUpImpl("amdgpu9.08--", "", ""); } | |
| }; | |
| TEST_F(GCNRegPressureTest, DownwardTrackerEndOnDbgVal) { | |
| StringRef MIR = R"( | |
| name: DownwardTrackerEndOnDbgVal | |
| tracksRegLiveness: true | |
| machineFunctionInfo: | |
| isEntryFunction: true | |
| body: | | |
| bb.0: | |
| %0:vgpr_32 = IMPLICIT_DEF | |
| %1:vgpr_32 = IMPLICIT_DEF | |
| bb.1: | |
| DBG_VALUE %0 | |
| DBG_VALUE %1 | |
| %2:vgpr_32 = IMPLICIT_DEF | |
| bb.3: | |
| S_NOP 0, implicit %0, implicit %1, implicit %2 | |
| S_ENDPGM 0 | |
| ... | |
| )"; | |
| EXPECT_TRUE(parseMIR(MIR)); | |
| MachineFunction &MF = getMF("DownwardTrackerEndOnDbgVal"); | |
| const LiveIntervals &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF); | |
| // MBB1 live-in pressure is equivalent to MBB0 live-out pressure. | |
| MachineBasicBlock &MBB0 = *MF.getBlockNumbered(0); | |
| MachineBasicBlock &MBB1 = *MF.getBlockNumbered(1); | |
| GCNRPTracker::LiveRegSet MBB1LiveIns = | |
| getLiveRegs(LIS.getInstructionIndex(*MBB0.rbegin()).getDeadSlot(), LIS, | |
| MF.getRegInfo()); | |
| // Track pressure across MBB1. | |
| { | |
| GCNDownwardRPTracker RPTracker(LIS), RPTrackerNoLiveIns(LIS); | |
| // There is a non-debug instruction in bb.1 (%2's def), so advance should | |
| // return true. | |
| EXPECT_TRUE(RPTracker.advance(MBB1.begin(), MBB1.end(), &MBB1LiveIns)); | |
| EXPECT_TRUE(RPTrackerNoLiveIns.advance(MBB1.begin(), MBB1.end(), nullptr)); | |
| // When advance returns true, maximum pressure should be the pressured | |
| // induced by the block's live-ins plus %2's def i.e., 3 VGPRs. | |
| EXPECT_EQ(RPTracker.moveMaxPressure().getVGPRNum(false), 3U); | |
| EXPECT_EQ(RPTrackerNoLiveIns.moveMaxPressure().getVGPRNum(false), 3U); | |
| } | |
| // Track pressure just across the first debug value of bb.1. | |
| { | |
| MachineBasicBlock::iterator Dbg1 = std::next(MBB1.begin()); | |
| GCNDownwardRPTracker RPTracker(LIS), RPTrackerNoLiveIns(LIS); | |
| // The following unpacks a call to | |
| // advance(*MBB1.begin(), Dbg1, [MBB1LiveIns|nullptr]) | |
| // which would return false in this case. | |
| // | |
| // There aren't any non-debug instruction between the beginning of bb1 and | |
| // Dbg1 (exclusive), the reset is therefore unsuccessful. The advance caller | |
| // returns early on a failure to reset. Calling advance after this does | |
| // nothing and produces false because the internal iterator already points | |
| // to the second debug instruction. | |
| EXPECT_FALSE(RPTracker.reset(*MBB1.begin(), Dbg1, &MBB1LiveIns)); | |
| EXPECT_FALSE(RPTrackerNoLiveIns.reset(*MBB1.begin(), Dbg1, nullptr)); | |
| EXPECT_FALSE(RPTracker.advance(Dbg1)); | |
| EXPECT_FALSE(RPTrackerNoLiveIns.advance(Dbg1)); | |
| // Register pressure should be the one at the block's live-ins. | |
| EXPECT_EQ(RPTracker.moveMaxPressure().getVGPRNum(false), 2U); | |
| EXPECT_EQ(RPTrackerNoLiveIns.moveMaxPressure().getVGPRNum(false), 2U); | |
| } | |
| } | |
| TEST_F(GCNRegPressureTest, DownwardTrackerAllDbgVal) { | |
| StringRef MIR = R"( | |
| name: DownwardTrackerAllDbgVal | |
| tracksRegLiveness: true | |
| machineFunctionInfo: | |
| isEntryFunction: true | |
| body: | | |
| bb.0: | |
| %0:vgpr_32 = IMPLICIT_DEF | |
| bb.1: | |
| DBG_VALUE %0 | |
| bb.2: | |
| S_NOP 0, implicit %0 | |
| S_ENDPGM 0 | |
| ... | |
| )"; | |
| EXPECT_TRUE(parseMIR(MIR)); | |
| MachineFunction &MF = getMF("DownwardTrackerAllDbgVal"); | |
| const LiveIntervals &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF); | |
| // MBB1 live-in pressure is equivalent to MBB0 live-out pressure. | |
| MachineBasicBlock &MBB0 = *MF.getBlockNumbered(0); | |
| GCNRPTracker::LiveRegSet MBB1LiveIns = | |
| getLiveRegs(LIS.getInstructionIndex(*MBB0.rbegin()).getDeadSlot(), LIS, | |
| MF.getRegInfo()); | |
| MachineBasicBlock &MBB1 = *MF.getBlockNumbered(1); | |
| GCNDownwardRPTracker RPTracker(LIS), RPTrackerNoLiveIns(LIS); | |
| // The following unpacks a call to | |
| // advance(MBB1.begin(), MBB1.end(), [MBB1LiveIns|nullptr]) | |
| // which would return false in this case. | |
| // | |
| // There aren't any non-debug instruction in bb.2, the reset is therefore | |
| // unsuccessful. The advance caller returns early on a failure to reset. | |
| // Calling advance after this does nothing and produces false because the | |
| // internal iterator is already at the block's end. | |
| EXPECT_FALSE(RPTracker.reset(*MBB1.begin(), MBB1.end(), &MBB1LiveIns)); | |
| EXPECT_FALSE(RPTrackerNoLiveIns.reset(*MBB1.begin(), MBB1.end(), nullptr)); | |
| EXPECT_FALSE(RPTracker.advance(MBB1.end())); | |
| EXPECT_FALSE(RPTrackerNoLiveIns.advance(MBB1.end())); | |
| // Register pressure should be the one at the block's live-ins. | |
| EXPECT_EQ(RPTracker.moveMaxPressure().getVGPRNum(false), 1U); | |
| EXPECT_EQ(RPTrackerNoLiveIns.moveMaxPressure().getVGPRNum(false), 1U); | |
| } | |
| // Tests the correct handling of multiple uses of the same virtual register | |
| // in bumpDownwardPressure (speculative estimate of register pressure). | |
| TEST_F(GCNRegPressureTest, BumpDownwardPressureLastUseAfterCommit) { | |
| StringRef MIR = R"( | |
| name: BumpDownwardPressureLastUseAfterCommit | |
| tracksRegLiveness: true | |
| body: | | |
| bb.0: | |
| %0:vgpr_32 = IMPLICIT_DEF | |
| %1:vreg_256_align2 = IMPLICIT_DEF | |
| S_NOP 0, implicit %1 | |
| S_NOP 0, implicit %1 | |
| S_NOP 0, implicit %0 | |
| S_ENDPGM 0 | |
| ... | |
| )"; | |
| ASSERT_TRUE(parseMIR(MIR)); | |
| MachineFunction &MF = getMF("BumpDownwardPressureLastUseAfterCommit"); | |
| const LiveIntervals &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF); | |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| const SIRegisterInfo *TRI = MF.getSubtarget<GCNSubtarget>().getRegisterInfo(); | |
| MachineBasicBlock &MBB = *MF.getBlockNumbered(0); | |
| SmallVector<MachineInstr *, 8> Instrs; | |
| for (MachineInstr &MI : MBB) | |
| Instrs.push_back(&MI); | |
| // 0: def %0, 1: def %1, 2: U1 (use %1), 3: U2 (last use %1), | |
| // 4: use %0, 5: S_ENDPGM | |
| MachineInstr *DefV0 = Instrs[0]; | |
| MachineInstr *DefV1 = Instrs[1]; | |
| MachineInstr *U1 = Instrs[2]; | |
| MachineInstr *U2 = Instrs[3]; | |
| GCNDownwardRPTracker RPTracker(LIS); | |
| GCNRPTracker::LiveRegSet Empty; | |
| RPTracker.reset(MRI, Empty); | |
| // Commit the defs and the first use of %1 via the externally-managed | |
| // iterator (same as while scheduling). | |
| RPTracker.advance(DefV0, /*UseInternalIterator=*/false); | |
| RPTracker.advance(DefV1, /*UseInternalIterator=*/false); | |
| RPTracker.advance(U1, /*UseInternalIterator=*/false); | |
| // After committing U1, both %0 (1 VGPR) and %1 (vreg_256 = 8 VGPRs) are live. | |
| EXPECT_EQ(RPTracker.getPressure().getArchVGPRNum(), 9U); | |
| // Speculate the last use of %1. %1 must die here, dropping its 8 VGPRs and | |
| // leaving only %0 live. | |
| GCNRegPressure P = RPTracker.bumpDownwardPressure(U2, TRI); | |
| EXPECT_EQ(P.getArchVGPRNum(), 1U); | |
| } | |
| // Tests bumpDownwardPressure for an instruction that uses and redefines | |
| // the same register. | |
| TEST_F(GCNRegPressureTest, BumpDownwardPressureUseAndRedef) { | |
| StringRef MIR = R"( | |
| name: BumpDownwardPressureUseAndRedef | |
| tracksRegLiveness: true | |
| body: | | |
| bb.0: | |
| %0:sgpr_32 = IMPLICIT_DEF | |
| %0:sgpr_32 = S_OR_B32 %0:sgpr_32, 1, implicit-def dead $scc | |
| S_NOP 0, implicit %0 | |
| S_ENDPGM 0 | |
| ... | |
| )"; | |
| ASSERT_TRUE(parseMIR(MIR)); | |
| MachineFunction &MF = getMF("BumpDownwardPressureUseAndRedef"); | |
| const LiveIntervals &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF); | |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| const SIRegisterInfo *TRI = MF.getSubtarget<GCNSubtarget>().getRegisterInfo(); | |
| MachineBasicBlock &MBB = *MF.getBlockNumbered(0); | |
| SmallVector<MachineInstr *, 8> Instrs; | |
| for (MachineInstr &MI : MBB) | |
| Instrs.push_back(&MI); | |
| // 0: def %0 (sgpr_32 = 1 SGPR) | |
| // 1: use+redef of %0 | |
| // 2: use %0 | |
| // 3: S_ENDPGM | |
| MachineInstr *DefS0 = Instrs[0]; | |
| MachineInstr *UseRedef = Instrs[1]; | |
| GCNDownwardRPTracker RPTracker(LIS); | |
| GCNRPTracker::LiveRegSet Empty; | |
| RPTracker.reset(MRI, Empty); | |
| // Commit the def; %0 occupies 1 SGPR and stays live. | |
| RPTracker.advance(DefS0, /*UseInternalIterator=*/false); | |
| EXPECT_EQ(RPTracker.getPressure().getSGPRNum(), 1U); | |
| // Speculate the instruction. It uses and redefines %0, which stays live, so | |
| // pressure must be unchanged. | |
| GCNRegPressure P = RPTracker.bumpDownwardPressure(UseRedef, TRI); | |
| EXPECT_EQ(P.getSGPRNum(), 1U); | |
| } |