[AMDGPU][MC][NFC] Refactored custom operands handling

The original design of custom operands support assumed that most GPUs
have the same or very similar operand names end encodings. This is
no longer the case. As a result the support code becomes over-complicated
and difficult to maintain.

This change implements a different design with the following benefits:

- support of aliases;
- support of operands with overlapped encodings;
- identification of defined but unsupported operands.

Differential Revision: https://reviews.llvm.org/D121696
This commit is contained in:
Dmitry Preobrazhensky 2022-03-16 15:52:37 +03:00
parent 0bc451e7e1
commit 5977dfba64
6 changed files with 149 additions and 134 deletions

View File

@ -1542,7 +1542,6 @@ private:
int64_t Id;
bool IsSymbolic = false;
bool IsDefined = false;
StringRef Name;
OperandInfoTy(int64_t Id_) : Id(Id_) {}
};
@ -6258,9 +6257,8 @@ AMDGPUAsmParser::parseHwregBody(OperandInfoTy &HwReg,
// The register may be specified by name or using a numeric code
HwReg.Loc = getLoc();
if (isToken(AsmToken::Identifier) &&
(HwReg.Id = getHwregId(getTokenStr(), getSTI())) >= 0) {
(HwReg.Id = getHwregId(getTokenStr(), getSTI())) != OPR_ID_UNKNOWN) {
HwReg.IsSymbolic = true;
HwReg.Name = getTokenStr();
lex(); // skip register name
} else if (!parseExpr(HwReg.Id, "a register name")) {
return false;
@ -6292,16 +6290,18 @@ AMDGPUAsmParser::validateHwreg(const OperandInfoTy &HwReg,
using namespace llvm::AMDGPU::Hwreg;
if (HwReg.IsSymbolic &&
!isValidHwreg(HwReg.Id, getSTI(), HwReg.Name)) {
Error(HwReg.Loc,
"specified hardware register is not supported on this GPU");
return false;
}
if (!isValidHwreg(HwReg.Id)) {
Error(HwReg.Loc,
"invalid code of hardware register: only 6-bit values are legal");
return false;
if (HwReg.IsSymbolic) {
if (HwReg.Id == OPR_ID_UNSUPPORTED) {
Error(HwReg.Loc,
"specified hardware register is not supported on this GPU");
return false;
}
} else {
if (!isValidHwreg(HwReg.Id)) {
Error(HwReg.Loc,
"invalid code of hardware register: only 6-bit values are legal");
return false;
}
}
if (!isValidHwregOffset(Offset.Id)) {
Error(Offset.Loc, "invalid bit offset: only 5-bit values are legal");
@ -6323,7 +6323,7 @@ AMDGPUAsmParser::parseHwreg(OperandVector &Operands) {
SMLoc Loc = getLoc();
if (trySkipId("hwreg", AsmToken::LParen)) {
OperandInfoTy HwReg(ID_UNKNOWN_);
OperandInfoTy HwReg(OPR_ID_UNKNOWN);
OperandInfoTy Offset(OFFSET_DEFAULT_);
OperandInfoTy Width(WIDTH_DEFAULT_);
if (parseHwregBody(HwReg, Offset, Width) &&

View File

@ -363,8 +363,6 @@ enum StreamId : unsigned { // Stream ID, (2) [9:8].
namespace Hwreg { // Encoding of SIMM16 used in s_setreg/getreg* insns.
enum Id { // HwRegCode, (6) [5:0]
ID_UNKNOWN_ = -1,
ID_SYMBOLIC_FIRST_ = 1, // There are corresponding symbolic names defined.
ID_MODE = 1,
ID_STATUS = 2,
ID_TRAPSTS = 3,
@ -373,28 +371,23 @@ enum Id { // HwRegCode, (6) [5:0]
ID_LDS_ALLOC = 6,
ID_IB_STS = 7,
ID_MEM_BASES = 15,
ID_SYMBOLIC_FIRST_GFX9_ = ID_MEM_BASES,
ID_TBA_LO = 16,
ID_TBA_HI = 17,
ID_TMA_LO = 18,
ID_TMA_HI = 19,
ID_XCC_ID = 20,
ID_SYMBOLIC_FIRST_GFX940_ = ID_XCC_ID,
ID_SQ_PERF_SNAPSHOT_DATA = 21,
ID_SQ_PERF_SNAPSHOT_DATA1 = 22,
ID_SQ_PERF_SNAPSHOT_PC_LO = 23,
ID_SQ_PERF_SNAPSHOT_PC_HI = 24,
ID_SYMBOLIC_LAST_GFX940_ = ID_SQ_PERF_SNAPSHOT_PC_HI + 1,
ID_FLAT_SCR_LO = 20,
ID_SYMBOLIC_FIRST_GFX10_ = ID_FLAT_SCR_LO,
ID_FLAT_SCR_HI = 21,
ID_XNACK_MASK = 22,
ID_HW_ID1 = 23,
ID_HW_ID2 = 24,
ID_POPS_PACKER = 25,
ID_SHADER_CYCLES = 29,
ID_SYMBOLIC_FIRST_GFX1030_ = ID_SHADER_CYCLES,
ID_SYMBOLIC_LAST_ = 30,
ID_SHIFT_ = 0,
ID_WIDTH_ = 6,
ID_MASK_ = (((1 << ID_WIDTH_) - 1) << ID_SHIFT_)

View File

@ -6,10 +6,9 @@
//
//===----------------------------------------------------------------------===//
#include "AMDGPUAsmUtils.h"
#include "AMDGPUBaseInfo.h"
#include "SIDefines.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
namespace AMDGPU {
namespace SendMsg {
@ -54,49 +53,62 @@ const char *const OpGsSymbolic[OP_GS_LAST_] = {
namespace Hwreg {
// This must be in sync with llvm::AMDGPU::Hwreg::ID_SYMBOLIC_FIRST_/LAST_, see SIDefines.h.
const char* const IdSymbolic[] = {
nullptr,
"HW_REG_MODE",
"HW_REG_STATUS",
"HW_REG_TRAPSTS",
"HW_REG_HW_ID",
"HW_REG_GPR_ALLOC",
"HW_REG_LDS_ALLOC",
"HW_REG_IB_STS",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"HW_REG_SH_MEM_BASES",
"HW_REG_TBA_LO",
"HW_REG_TBA_HI",
"HW_REG_TMA_LO",
"HW_REG_TMA_HI",
"HW_REG_FLAT_SCR_LO",
"HW_REG_FLAT_SCR_HI",
"HW_REG_XNACK_MASK",
"HW_REG_HW_ID1",
"HW_REG_HW_ID2",
"HW_REG_POPS_PACKER",
nullptr,
nullptr,
nullptr,
"HW_REG_SHADER_CYCLES"
};
// Disable lint checking for this block since it makes the table unreadable.
// NOLINTBEGIN
const CustomOperand<const MCSubtargetInfo &> Opr[] = {
{},
{{"HW_REG_MODE"}, ID_MODE},
{{"HW_REG_STATUS"}, ID_STATUS},
{{"HW_REG_TRAPSTS"}, ID_TRAPSTS},
{{"HW_REG_HW_ID"}, ID_HW_ID,
[](const MCSubtargetInfo &STI) {
return isSI(STI) || isCI(STI) ||
isVI(STI) || isGFX9(STI);
}},
{{"HW_REG_GPR_ALLOC"}, ID_GPR_ALLOC},
{{"HW_REG_LDS_ALLOC"}, ID_LDS_ALLOC},
{{"HW_REG_IB_STS"}, ID_IB_STS},
{},
{},
{},
{},
{},
{},
{},
{{"HW_REG_SH_MEM_BASES"}, ID_MEM_BASES, isGFX9Plus},
{{"HW_REG_TBA_LO"}, ID_TBA_LO, isGFX9_GFX10},
{{"HW_REG_TBA_HI"}, ID_TBA_HI, isGFX9_GFX10},
{{"HW_REG_TMA_LO"}, ID_TMA_LO, isGFX9_GFX10},
{{"HW_REG_TMA_HI"}, ID_TMA_HI, isGFX9_GFX10},
{{"HW_REG_FLAT_SCR_LO"}, ID_FLAT_SCR_LO, isGFX10Plus},
{{"HW_REG_FLAT_SCR_HI"}, ID_FLAT_SCR_HI, isGFX10Plus},
{{"HW_REG_XNACK_MASK"}, ID_XNACK_MASK,
[](const MCSubtargetInfo &STI) {
return isGFX10(STI) &&
!AMDGPU::isGFX10_BEncoding(STI);
}},
{{"HW_REG_HW_ID1"}, ID_HW_ID1, isGFX10Plus},
{{"HW_REG_HW_ID2"}, ID_HW_ID2, isGFX10Plus},
{{"HW_REG_POPS_PACKER"}, ID_POPS_PACKER, isGFX10},
{},
{},
{},
{{"HW_REG_SHADER_CYCLES"}, ID_SHADER_CYCLES, isGFX10_BEncoding},
// This is gfx940 specific portion from ID_SYMBOLIC_FIRST_GFX940_ to
// ID_SYMBOLIC_LAST_GFX940_
const char* const IdSymbolicGFX940Specific[] = {
"HW_REG_XCC_ID",
"HW_REG_SQ_PERF_SNAPSHOT_DATA",
"HW_REG_SQ_PERF_SNAPSHOT_DATA1",
"HW_REG_SQ_PERF_SNAPSHOT_PC_LO",
"HW_REG_SQ_PERF_SNAPSHOT_PC_HI"
// GFX940 specific registers
{{"HW_REG_XCC_ID"}, ID_XCC_ID, isGFX940},
{{"HW_REG_SQ_PERF_SNAPSHOT_DATA"}, ID_SQ_PERF_SNAPSHOT_DATA, isGFX940},
{{"HW_REG_SQ_PERF_SNAPSHOT_DATA1"}, ID_SQ_PERF_SNAPSHOT_DATA1, isGFX940},
{{"HW_REG_SQ_PERF_SNAPSHOT_PC_LO"}, ID_SQ_PERF_SNAPSHOT_PC_LO, isGFX940},
{{"HW_REG_SQ_PERF_SNAPSHOT_PC_HI"}, ID_SQ_PERF_SNAPSHOT_PC_HI, isGFX940},
// Aliases
{{"HW_REG_HW_ID"}, ID_HW_ID1, isGFX10},
};
// NOLINTEND
const int OPR_SIZE = static_cast<int>(
sizeof(Opr) / sizeof(CustomOperand<const MCSubtargetInfo &>));
} // namespace Hwreg

View File

@ -11,12 +11,24 @@
#include "SIDefines.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
class StringLiteral;
class MCSubtargetInfo;
namespace AMDGPU {
const int OPR_ID_UNKNOWN = -1;
const int OPR_ID_UNSUPPORTED = -2;
template <class T> struct CustomOperand {
StringLiteral Name = "";
int Encoding = 0;
bool (*Cond)(T Context) = nullptr;
};
namespace SendMsg { // Symbolic names for the sendmsg(...) syntax.
extern const char *const IdSymbolic[ID_GAPS_LAST_];
@ -27,8 +39,8 @@ extern const char *const OpGsSymbolic[OP_GS_LAST_];
namespace Hwreg { // Symbolic names for the hwreg(...) syntax.
extern const char* const IdSymbolic[];
extern const char* const IdSymbolicGFX940Specific[];
extern const CustomOperand<const MCSubtargetInfo &> Opr[];
extern const int OPR_SIZE;
} // namespace Hwreg

View File

@ -1038,75 +1038,71 @@ unsigned encodeWaitcnt(const IsaVersion &Version, const Waitcnt &Decoded) {
return encodeWaitcnt(Version, Decoded.VmCnt, Decoded.ExpCnt, Decoded.LgkmCnt);
}
//===----------------------------------------------------------------------===//
// Custom Operands.
//
// A table of custom operands shall describe "primary" operand names
// first followed by aliases if any. It is not required but recommended
// to arrange operands so that operand encoding match operand position
// in the table. This will make disassembly a bit more efficient.
// Unused slots in the table shall have an empty name.
//
//===----------------------------------------------------------------------===//
template <class T>
static bool isValidOpr(int Idx, const CustomOperand<T> OpInfo[], int OpInfoSize,
T Context) {
return 0 <= Idx && Idx < OpInfoSize && !OpInfo[Idx].Name.empty() &&
(!OpInfo[Idx].Cond || OpInfo[Idx].Cond(Context));
}
template <class T>
static int getOprIdx(std::function<bool(const CustomOperand<T> &)> Test,
const CustomOperand<T> OpInfo[], int OpInfoSize,
T Context) {
int InvalidIdx = OPR_ID_UNKNOWN;
for (int Idx = 0; Idx < OpInfoSize; ++Idx) {
if (Test(OpInfo[Idx])) {
if (!OpInfo[Idx].Cond || OpInfo[Idx].Cond(Context))
return Idx;
InvalidIdx = OPR_ID_UNSUPPORTED;
}
}
return InvalidIdx;
}
template <class T>
static int getOprIdx(const StringRef Name, const CustomOperand<T> OpInfo[],
int OpInfoSize, T Context) {
auto Test = [=](const CustomOperand<T> &Op) { return Op.Name == Name; };
return getOprIdx<T>(Test, OpInfo, OpInfoSize, Context);
}
template <class T>
static int getOprIdx(int Id, const CustomOperand<T> OpInfo[], int OpInfoSize,
T Context, bool QuickCheck = true) {
auto Test = [=](const CustomOperand<T> &Op) {
return Op.Encoding == Id && !Op.Name.empty();
};
// This is an optimization that should work in most cases.
// As a side effect, it may cause selection of an alias
// instead of a primary operand name in case of sparse tables.
if (QuickCheck && isValidOpr<T>(Id, OpInfo, OpInfoSize, Context) &&
OpInfo[Id].Encoding == Id) {
return Id;
}
return getOprIdx<T>(Test, OpInfo, OpInfoSize, Context);
}
//===----------------------------------------------------------------------===//
// hwreg
//===----------------------------------------------------------------------===//
namespace Hwreg {
static const char* getHwregName(int64_t Id, const MCSubtargetInfo &STI) {
if (isGFX940(STI) && Id >= ID_SYMBOLIC_FIRST_GFX940_ &&
Id < ID_SYMBOLIC_LAST_GFX940_)
return IdSymbolicGFX940Specific[Id - ID_SYMBOLIC_FIRST_GFX940_];
return (Id < ID_SYMBOLIC_LAST_) ? IdSymbolic[Id] : nullptr;
}
int64_t getHwregId(const StringRef Name, const MCSubtargetInfo &STI) {
if (isGFX10(STI) && Name == "HW_REG_HW_ID") // An alias
return ID_HW_ID1;
for (int Id = ID_SYMBOLIC_FIRST_; Id < ID_SYMBOLIC_LAST_; ++Id) {
if (IdSymbolic[Id] && Name == IdSymbolic[Id])
return Id;
// These are all defined, however may be invalid for subtarget and need
// further validation.
if (Id >= ID_SYMBOLIC_FIRST_GFX940_ && Id < ID_SYMBOLIC_LAST_GFX940_ &&
Name == IdSymbolicGFX940Specific[Id - ID_SYMBOLIC_FIRST_GFX940_])
return Id;
}
return ID_UNKNOWN_;
}
static unsigned getLastSymbolicHwreg(const MCSubtargetInfo &STI) {
if (isSI(STI) || isCI(STI) || isVI(STI))
return ID_SYMBOLIC_FIRST_GFX9_;
else if (isGFX940(STI))
return ID_SYMBOLIC_LAST_GFX940_;
else if (isGFX9(STI))
return ID_SYMBOLIC_FIRST_GFX10_;
else if (isGFX10(STI) && !isGFX10_BEncoding(STI))
return ID_SYMBOLIC_FIRST_GFX1030_;
else
return ID_SYMBOLIC_LAST_;
}
bool isValidHwreg(int64_t Id, const MCSubtargetInfo &STI, StringRef Name) {
if (isGFX10(STI) && Name == "HW_REG_HW_ID") // An alias
return true;
const char *HWRegName = getHwregName(Id, STI);
if (!HWRegName || !Name.startswith(HWRegName))
return false;
if (isGFX10Plus(STI)) {
switch (Id) {
case ID_HW_ID1:
case ID_HW_ID2:
return true;
case ID_XNACK_MASK:
return !AMDGPU::isGFX10_BEncoding(STI);
default:
break;
}
}
if (ID_SYMBOLIC_FIRST_ > Id || Id >= getLastSymbolicHwreg(STI))
return false;
if (isGFX940(STI) && Id >= ID_SYMBOLIC_FIRST_GFX10_ &&
Id < ID_SYMBOLIC_FIRST_GFX940_)
return false;
return true;
int Idx = getOprIdx<const MCSubtargetInfo &>(Name, Opr, OPR_SIZE, STI);
return (Idx < 0) ? Idx : Opr[Idx].Encoding;
}
bool isValidHwreg(int64_t Id) {
@ -1128,8 +1124,8 @@ uint64_t encodeHwreg(uint64_t Id, uint64_t Offset, uint64_t Width) {
}
StringRef getHwreg(unsigned Id, const MCSubtargetInfo &STI) {
const char *HWRegName = getHwregName(Id, STI);
return (HWRegName && isValidHwreg(Id, STI, HWRegName)) ? HWRegName : "";
int Idx = getOprIdx<const MCSubtargetInfo &>(Id, Opr, OPR_SIZE, STI);
return (Idx < 0) ? "" : Opr[Idx].Name;
}
void decodeHwreg(unsigned Val, unsigned &Id, unsigned &Offset, unsigned &Width) {
@ -1537,6 +1533,10 @@ bool isGFX9(const MCSubtargetInfo &STI) {
return STI.getFeatureBits()[AMDGPU::FeatureGFX9];
}
bool isGFX9_GFX10(const MCSubtargetInfo &STI) {
return isGFX9(STI) || isGFX10(STI);
}
bool isGFX9Plus(const MCSubtargetInfo &STI) {
return isGFX9(STI) || isGFX10Plus(STI);
}

View File

@ -612,9 +612,6 @@ namespace Hwreg {
LLVM_READONLY
int64_t getHwregId(const StringRef Name, const MCSubtargetInfo &STI);
LLVM_READONLY
bool isValidHwreg(int64_t Id, const MCSubtargetInfo &STI, StringRef Name);
LLVM_READNONE
bool isValidHwreg(int64_t Id);
@ -775,6 +772,7 @@ bool isSI(const MCSubtargetInfo &STI);
bool isCI(const MCSubtargetInfo &STI);
bool isVI(const MCSubtargetInfo &STI);
bool isGFX9(const MCSubtargetInfo &STI);
bool isGFX9_GFX10(const MCSubtargetInfo &STI);
bool isGFX9Plus(const MCSubtargetInfo &STI);
bool isGFX10(const MCSubtargetInfo &STI);
bool isGFX10Plus(const MCSubtargetInfo &STI);