343 lines · cpp
1//===--------- llvm/unittests/Target/AMDGPU/AMDGPUUnitTests.cpp -----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "AMDGPUUnitTests.h"10#include "AMDGPUTargetMachine.h"11#include "GCNSubtarget.h"12#include "llvm/MC/TargetRegistry.h"13#include "llvm/Support/TargetSelect.h"14#include "llvm/TargetParser/TargetParser.h"15#include "gtest/gtest.h"16 17#include "AMDGPUGenSubtargetInfo.inc"18 19using namespace llvm;20 21std::once_flag flag;22 23void InitializeAMDGPUTarget() {24 std::call_once(flag, []() {25 LLVMInitializeAMDGPUTargetInfo();26 LLVMInitializeAMDGPUTarget();27 LLVMInitializeAMDGPUTargetMC();28 });29}30 31std::unique_ptr<const GCNTargetMachine>32llvm::createAMDGPUTargetMachine(std::string TStr, StringRef CPU, StringRef FS) {33 InitializeAMDGPUTarget();34 35 Triple TT(TStr);36 std::string Error;37 const Target *T = TargetRegistry::lookupTarget(TT, Error);38 if (!T)39 return nullptr;40 41 TargetOptions Options;42 return std::unique_ptr<GCNTargetMachine>(43 static_cast<GCNTargetMachine *>(T->createTargetMachine(44 TT, CPU, FS, Options, std::nullopt, std::nullopt)));45}46 47static cl::opt<bool> PrintCpuRegLimits(48 "print-cpu-reg-limits", cl::NotHidden, cl::init(false),49 cl::desc("force printing per AMDGPU CPU register limits"));50 51static bool checkMinMax(std::stringstream &OS, unsigned Occ, unsigned MinOcc,52 unsigned MaxOcc,53 std::function<unsigned(unsigned)> GetOcc,54 std::function<unsigned(unsigned)> GetMinGPRs,55 std::function<unsigned(unsigned)> GetMaxGPRs) {56 bool MinValid = true, MaxValid = true, RangeValid = true;57 unsigned MinGPRs = GetMinGPRs(Occ);58 unsigned MaxGPRs = GetMaxGPRs(Occ);59 unsigned RealOcc;60 61 if (MinGPRs >= MaxGPRs)62 RangeValid = false;63 else {64 RealOcc = GetOcc(MinGPRs);65 for (unsigned NumRegs = MinGPRs + 1; NumRegs <= MaxGPRs; ++NumRegs) {66 if (RealOcc != GetOcc(NumRegs)) {67 RangeValid = false;68 break;69 }70 }71 }72 73 if (RangeValid && RealOcc > MinOcc && RealOcc <= MaxOcc) {74 if (MinGPRs > 0 && GetOcc(MinGPRs - 1) <= RealOcc)75 MinValid = false;76 77 if (GetOcc(MaxGPRs + 1) >= RealOcc)78 MaxValid = false;79 }80 81 std::stringstream MinStr;82 MinStr << (MinValid ? ' ' : '<') << ' ' << std::setw(3) << MinGPRs << " (O"83 << GetOcc(MinGPRs) << ") " << (RangeValid ? ' ' : 'R');84 85 OS << std::left << std::setw(15) << MinStr.str() << std::setw(3) << MaxGPRs86 << " (O" << GetOcc(MaxGPRs) << ')' << (MaxValid ? "" : " >");87 88 return MinValid && MaxValid && RangeValid;89}90 91static const std::pair<StringRef, StringRef>92 EmptyFS = {"", ""},93 W32FS = {"+wavefrontsize32", "w32"},94 W64FS = {"+wavefrontsize64", "w64"};95 96using TestFuncTy = function_ref<bool(std::stringstream &, unsigned,97 const GCNSubtarget &, bool)>;98 99static bool testAndRecord(std::stringstream &Table, const GCNSubtarget &ST,100 TestFuncTy test, unsigned DynamicVGPRBlockSize) {101 bool Success = true;102 unsigned MaxOcc = ST.getMaxWavesPerEU();103 for (unsigned Occ = MaxOcc; Occ > 0; --Occ) {104 Table << std::right << std::setw(3) << Occ << " ";105 Success = test(Table, Occ, ST, DynamicVGPRBlockSize) && Success;106 Table << '\n';107 }108 return Success;109}110 111static void testGPRLimits(const char *RegName, bool TestW32W64,112 TestFuncTy test) {113 SmallVector<StringRef> CPUs;114 AMDGPU::fillValidArchListAMDGCN(CPUs);115 116 std::map<std::string, SmallVector<std::string>> TablePerCPUs;117 for (auto CPUName : CPUs) {118 auto CanonCPUName =119 AMDGPU::getArchNameAMDGCN(AMDGPU::parseArchAMDGCN(CPUName));120 121 auto *FS = &EmptyFS;122 while (true) {123 auto TM = createAMDGPUTargetMachine("amdgcn-amd-", CPUName, FS->first);124 if (!TM)125 break;126 127 GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),128 std::string(TM->getTargetFeatureString()), *TM);129 130 if (TestW32W64 &&131 ST.getFeatureBits().test(AMDGPU::FeatureWavefrontSize32))132 FS = &W32FS;133 134 std::stringstream Table;135 bool Success = testAndRecord(Table, ST, test, /*DynamicVGPRBlockSize=*/0);136 if (!Success || PrintCpuRegLimits)137 TablePerCPUs[Table.str()].push_back((CanonCPUName + FS->second).str());138 139 if (FS != &W32FS)140 break;141 142 FS = &W64FS;143 }144 }145 std::stringstream OS;146 for (auto &P : TablePerCPUs) {147 for (auto &CPUName : P.second)148 OS << ' ' << CPUName;149 OS << ":\nOcc Min" << RegName << " Max" << RegName << '\n'150 << P.first << '\n';151 }152 auto ErrStr = OS.str();153 EXPECT_TRUE(ErrStr.empty()) << ErrStr;154}155 156static void testDynamicVGPRLimits(StringRef CPUName, StringRef FS,157 TestFuncTy test) {158 auto TM = createAMDGPUTargetMachine("amdgcn-amd-", CPUName, FS);159 ASSERT_TRUE(TM) << "No target machine";160 161 GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),162 std::string(TM->getTargetFeatureString()), *TM);163 164 auto testWithBlockSize = [&](unsigned DynamicVGPRBlockSize) {165 std::stringstream Table;166 bool Success = testAndRecord(Table, ST, test, DynamicVGPRBlockSize);167 EXPECT_TRUE(Success && !PrintCpuRegLimits)168 << CPUName << " dynamic VGPR block size " << DynamicVGPRBlockSize169 << ":\nOcc MinVGPR MaxVGPR\n"170 << Table.str() << '\n';171 };172 173 testWithBlockSize(16);174 testWithBlockSize(32);175}176 177TEST(AMDGPU, TestVGPRLimitsPerOccupancy) {178 auto test = [](std::stringstream &OS, unsigned Occ, const GCNSubtarget &ST,179 unsigned DynamicVGPRBlockSize) {180 unsigned MaxVGPRNum = ST.getAddressableNumVGPRs(DynamicVGPRBlockSize);181 return checkMinMax(182 OS, Occ, ST.getOccupancyWithNumVGPRs(MaxVGPRNum, DynamicVGPRBlockSize),183 ST.getMaxWavesPerEU(),184 [&](unsigned NumGPRs) {185 return ST.getOccupancyWithNumVGPRs(NumGPRs, DynamicVGPRBlockSize);186 },187 [&](unsigned Occ) {188 return ST.getMinNumVGPRs(Occ, DynamicVGPRBlockSize);189 },190 [&](unsigned Occ) {191 return ST.getMaxNumVGPRs(Occ, DynamicVGPRBlockSize);192 });193 };194 195 testGPRLimits("VGPR", true, test);196 197 testDynamicVGPRLimits("gfx1200", "+wavefrontsize32", test);198}199 200static void testAbsoluteLimits(StringRef CPUName, StringRef FS,201 unsigned DynamicVGPRBlockSize,202 unsigned ExpectedMinOcc, unsigned ExpectedMaxOcc,203 unsigned ExpectedMaxVGPRs) {204 auto TM = createAMDGPUTargetMachine("amdgcn-amd-", CPUName, FS);205 ASSERT_TRUE(TM) << "No target machine";206 207 GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),208 std::string(TM->getTargetFeatureString()), *TM);209 210 // Test function without attributes.211 LLVMContext Context;212 Module M("", Context);213 Function *Func =214 Function::Create(FunctionType::get(Type::getVoidTy(Context), false),215 GlobalValue::ExternalLinkage, "testFunc", &M);216 Func->setCallingConv(CallingConv::AMDGPU_CS_Chain);217 Func->addFnAttr("amdgpu-flat-work-group-size", "1,32");218 219 std::string DVGPRBlockSize = std::to_string(DynamicVGPRBlockSize);220 if (DynamicVGPRBlockSize)221 Func->addFnAttr("amdgpu-dynamic-vgpr-block-size", DVGPRBlockSize);222 223 auto Range = ST.getWavesPerEU(*Func);224 EXPECT_EQ(ExpectedMinOcc, Range.first) << CPUName << ' ' << FS;225 EXPECT_EQ(ExpectedMaxOcc, Range.second) << CPUName << ' ' << FS;226 EXPECT_EQ(ExpectedMaxVGPRs, ST.getMaxNumVGPRs(*Func)) << CPUName << ' ' << FS;227 EXPECT_EQ(ExpectedMaxVGPRs, ST.getAddressableNumVGPRs(DynamicVGPRBlockSize))228 << CPUName << ' ' << FS;229 230 // Function with requested 'amdgpu-waves-per-eu' in a valid range.231 Func->addFnAttr("amdgpu-waves-per-eu", "10,12");232 Range = ST.getWavesPerEU(*Func);233 EXPECT_EQ(10u, Range.first) << CPUName << ' ' << FS;234 EXPECT_EQ(12u, Range.second) << CPUName << ' ' << FS;235}236 237TEST(AMDGPU, TestOccupancyAbsoluteLimits) {238 // CPUName, Features, DynamicVGPRBlockSize; Expected MinOcc, MaxOcc, MaxVGPRs239 testAbsoluteLimits("gfx1200", "+wavefrontsize32", 0, 1, 16, 256);240 testAbsoluteLimits("gfx1200", "+wavefrontsize32", 16, 1, 16, 128);241 testAbsoluteLimits("gfx1200", "+wavefrontsize32", 32, 1, 16, 256);242}243 244static const char *printSubReg(const TargetRegisterInfo &TRI, unsigned SubReg) {245 return SubReg ? TRI.getSubRegIndexName(SubReg) : "<none>";246}247 248TEST(AMDGPU, TestReverseComposeSubRegIndices) {249 auto TM = createAMDGPUTargetMachine("amdgcn-amd-", "gfx900", "");250 if (!TM)251 return;252 GCNSubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),253 std::string(TM->getTargetFeatureString()), *TM);254 255 const SIRegisterInfo *TRI = ST.getRegisterInfo();256 257#define EXPECT_SUBREG_EQ(A, B, Expect) \258 do { \259 unsigned Reversed = TRI->reverseComposeSubRegIndices(A, B); \260 EXPECT_EQ(Reversed, Expect) \261 << printSubReg(*TRI, A) << ", " << printSubReg(*TRI, B) << " => " \262 << printSubReg(*TRI, Reversed) << ", *" << printSubReg(*TRI, Expect); \263 } while (0);264 265 EXPECT_SUBREG_EQ(AMDGPU::NoSubRegister, AMDGPU::sub0, AMDGPU::sub0);266 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::NoSubRegister, AMDGPU::sub0);267 268 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::sub0, AMDGPU::sub0);269 270 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::sub1, AMDGPU::sub1);271 EXPECT_SUBREG_EQ(AMDGPU::sub1, AMDGPU::sub0, AMDGPU::NoSubRegister);272 273 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1, AMDGPU::sub0, AMDGPU::sub0);274 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::sub0_sub1, AMDGPU::sub0_sub1);275 276 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1_sub2_sub3, AMDGPU::sub0_sub1,277 AMDGPU::sub0_sub1);278 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1, AMDGPU::sub0_sub1_sub2_sub3,279 AMDGPU::sub0_sub1_sub2_sub3);280 281 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1_sub2_sub3, AMDGPU::sub1_sub2,282 AMDGPU::sub1_sub2);283 EXPECT_SUBREG_EQ(AMDGPU::sub1_sub2, AMDGPU::sub0_sub1_sub2_sub3,284 AMDGPU::NoSubRegister);285 286 EXPECT_SUBREG_EQ(AMDGPU::sub1_sub2_sub3, AMDGPU::sub0_sub1_sub2_sub3,287 AMDGPU::NoSubRegister);288 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1_sub2_sub3, AMDGPU::sub1_sub2_sub3,289 AMDGPU::sub1_sub2_sub3);290 291 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::sub30, AMDGPU::NoSubRegister);292 EXPECT_SUBREG_EQ(AMDGPU::sub30, AMDGPU::sub0, AMDGPU::NoSubRegister);293 294 EXPECT_SUBREG_EQ(AMDGPU::sub0, AMDGPU::sub31, AMDGPU::NoSubRegister);295 EXPECT_SUBREG_EQ(AMDGPU::sub31, AMDGPU::sub0, AMDGPU::NoSubRegister);296 297 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1, AMDGPU::sub30, AMDGPU::NoSubRegister);298 EXPECT_SUBREG_EQ(AMDGPU::sub30, AMDGPU::sub0_sub1, AMDGPU::NoSubRegister);299 300 EXPECT_SUBREG_EQ(AMDGPU::sub0_sub1, AMDGPU::sub30_sub31,301 AMDGPU::NoSubRegister);302 EXPECT_SUBREG_EQ(AMDGPU::sub30_sub31, AMDGPU::sub0_sub1,303 AMDGPU::NoSubRegister);304 305 for (unsigned SubIdx0 = 1, LastSubReg = TRI->getNumSubRegIndices();306 SubIdx0 != LastSubReg; ++SubIdx0) {307 for (unsigned SubIdx1 = 1; SubIdx1 != LastSubReg; ++SubIdx1) {308 if (unsigned ForwardCompose =309 TRI->composeSubRegIndices(SubIdx0, SubIdx1)) {310 unsigned ReverseComposed =311 TRI->reverseComposeSubRegIndices(SubIdx0, ForwardCompose);312 EXPECT_EQ(ReverseComposed, SubIdx1);313 }314 315 if (unsigned ReverseCompose =316 TRI->reverseComposeSubRegIndices(SubIdx0, SubIdx1)) {317 unsigned Recompose = TRI->composeSubRegIndices(SubIdx0, ReverseCompose);318 EXPECT_EQ(Recompose, SubIdx1);319 }320 }321 }322}323 324TEST(AMDGPU, TestGetNamedOperandIdx) {325 std::unique_ptr<const GCNTargetMachine> TM =326 createAMDGPUTargetMachine("amdgcn-amd-", "gfx900", "");327 if (!TM)328 return;329 const MCInstrInfo *MCII = TM->getMCInstrInfo();330 331 for (unsigned Opcode = 0, E = MCII->getNumOpcodes(); Opcode != E; ++Opcode) {332 const MCInstrDesc &Desc = MCII->get(Opcode);333 for (unsigned Idx = 0; Idx < Desc.getNumOperands(); ++Idx) {334 AMDGPU::OpName OpName = AMDGPU::getOperandIdxName(Opcode, Idx);335 if (OpName == AMDGPU::OpName::NUM_OPERAND_NAMES)336 continue;337 int16_t RetrievedIdx = AMDGPU::getNamedOperandIdx(Opcode, OpName);338 EXPECT_EQ(Idx, static_cast<unsigned>(RetrievedIdx))339 << "Opcode " << Opcode << " (" << MCII->getName(Opcode) << ')';340 }341 }342}343