[TableGen] Support encoding per-HwMode

Much like ValueTypeByHwMode/RegInfoByHwMode, this patch allows targets
to modify an instruction's encoding based on HwMode. When the
EncodingInfos field is non-empty the Inst and Size fields of the Instruction
are ignored and taken from EncodingInfos instead.

As part of this promote getHwMode() from TargetSubtargetInfo to MCSubtargetInfo.

This is NFC for all existing targets - new code is generated only if targets
use EncodingByHwMode.

llvm-svn: 372320
This commit is contained in:
James Molloy 2019-09-19 13:39:54 +00:00
parent b88800d882
commit 88a5fbfcea
10 changed files with 277 additions and 42 deletions

View File

@ -110,8 +110,6 @@ public:
return nullptr;
}
virtual unsigned getHwMode() const { return 0; }
/// Target can subclass this hook to select a different DAG scheduler.
virtual RegisterScheduler::FunctionPassCtor
getDAGScheduler(CodeGenOpt::Level) const {

View File

@ -221,6 +221,8 @@ public:
auto Found = std::lower_bound(ProcDesc.begin(), ProcDesc.end(), CPU);
return Found != ProcDesc.end() && StringRef(Found->Key) == CPU;
}
virtual unsigned getHwMode() const { return 0; }
};
} // end namespace llvm

View File

@ -443,6 +443,15 @@ class InstructionEncoding {
bit hasCompleteDecoder = 1;
}
// Allows specifying an InstructionEncoding by HwMode. If an Instruction specifies
// an EncodingByHwMode, its Inst and Size members are ignored and Ts are used
// to encode and decode based on HwMode.
class EncodingByHwMode<list<HwMode> Ms = [], list<InstructionEncoding> Ts = []>
: HwModeSelect<Ms> {
// The length of this list must be the same as the length of Ms.
list<InstructionEncoding> Objects = Ts;
}
//===----------------------------------------------------------------------===//
// Instruction set description - These classes correspond to the C++ classes in
// the Target/TargetInstrInfo.h file.
@ -454,6 +463,10 @@ class Instruction : InstructionEncoding {
dag InOperandList; // An dag containing the MI use operand list.
string AsmString = ""; // The .s format to print the instruction with.
// Allows specifying a canonical InstructionEncoding by HwMode. If non-empty,
// the Inst member of this Instruction is ignored.
EncodingByHwMode EncodingInfos;
// Pattern - Set to the DAG pattern for this instruction, if we know of one,
// otherwise, uninitialized.
list<dag> Pattern;

View File

@ -0,0 +1,81 @@
// RUN: llvm-tblgen -gen-emitter -I %p/../../include %s | FileCheck %s --check-prefix=ENCODER
// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | FileCheck %s --check-prefix=DECODER
include "llvm/Target/Target.td"
def archInstrInfo : InstrInfo { }
def arch : Target {
let InstructionSet = archInstrInfo;
}
def Myi32 : Operand<i32> {
let DecoderMethod = "DecodeMyi32";
}
def ModeA : HwMode<"+a">;
def ModeB : HwMode<"+b">;
def fooTypeEncA : InstructionEncoding {
let Size = 4;
field bits<32> SoftFail = 0;
bits<32> Inst;
bits<8> factor;
let Inst{7-0} = factor;
let Inst{3-2} = 0b11;
let Inst{1-0} = 0b00;
}
def fooTypeEncB : InstructionEncoding {
let Size = 4;
field bits<32> SoftFail = 0;
bits<32> Inst;
bits<8> factor;
let Inst{15-8} = factor;
let Inst{1-0} = 0b11;
}
let OutOperandList = (outs) in {
def foo : Instruction {
let InOperandList = (ins i32imm:$factor);
let EncodingInfos = EncodingByHwMode<
[ModeA, ModeB], [fooTypeEncA,
fooTypeEncB]
>;
let AsmString = "foo $factor";
}
def bar: Instruction {
let InOperandList = (ins i32imm:$factor);
let Size = 4;
bits<32> Inst;
bits<32> SoftFail;
bits<8> factor;
let Inst{31-24} = factor;
let Inst{1-0} = 0b10;
let AsmString = "bar $factor";
}
}
// DECODER-LABEL: DecoderTable_ModeA32[] =
// DECODER-DAG: Opcode: fooTypeEncA:foo
// DECODER-DAG: Opcode: bar
// DECODER-LABEL: DecoderTable_ModeB32[] =
// Note that the comment says fooTypeEncA but this is actually fooTypeEncB; plumbing
// the correct comment text through the decoder is nontrivial.
// DECODER-DAG: Opcode: fooTypeEncA:foo
// DECODER-DAG: Opcode: bar
// ENCODER-LABEL: static const uint64_t InstBits_ModeA[] = {
// ENCODER: UINT64_C(2), // bar
// ENCODER: UINT64_C(12), // foo
// ENCODER-LABEL: static const uint64_t InstBits_ModeB[] = {
// ENCODER: UINT64_C(2), // bar
// ENCODER: UINT64_C(3), // foo
// ENCODER: case ::foo: {
// ENCODER: switch (HwMode) {
// ENCODER: default: llvm_unreachable("Unhandled HwMode");
// ENCODER: case 1: {

View File

@ -46,12 +46,17 @@ public:
private:
int getVariableBit(const std::string &VarName, BitsInit *BI, int bit);
std::string getInstructionCase(Record *R, CodeGenTarget &Target);
std::string getInstructionCaseForEncoding(Record *R, Record *EncodingDef,
CodeGenTarget &Target);
void AddCodeToMergeInOperand(Record *R, BitsInit *BI,
const std::string &VarName,
unsigned &NumberedOp,
std::set<unsigned> &NamedOpIndices,
std::string &Case, CodeGenTarget &Target);
void emitInstructionBaseValues(
raw_ostream &o, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
CodeGenTarget &Target, int HwMode = -1);
unsigned BitWidth;
bool UseAPInt;
};
@ -261,7 +266,29 @@ AddCodeToMergeInOperand(Record *R, BitsInit *BI, const std::string &VarName,
std::string CodeEmitterGen::getInstructionCase(Record *R,
CodeGenTarget &Target) {
std::string Case;
BitsInit *BI = R->getValueAsBitsInit("Inst");
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
const CodeGenHwModes &HWM = Target.getHwModes();
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
Case += " switch (HwMode) {\n";
Case += " default: llvm_unreachable(\"Unhandled HwMode\");\n";
for (auto &KV : EBM.Map) {
Case += " case " + itostr(KV.first) + ": {\n";
Case += getInstructionCaseForEncoding(R, KV.second, Target);
Case += " break;\n";
Case += " }\n";
}
Case += " }\n";
return Case;
}
}
return getInstructionCaseForEncoding(R, R, Target);
}
std::string CodeEmitterGen::getInstructionCaseForEncoding(Record *R, Record *EncodingDef,
CodeGenTarget &Target) {
std::string Case;
BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
unsigned NumberedOp = 0;
std::set<unsigned> NamedOpIndices;
@ -281,7 +308,7 @@ std::string CodeEmitterGen::getInstructionCase(Record *R,
// Loop over all of the fields in the instruction, determining which are the
// operands to the instruction.
for (const RecordVal &RV : R->getValues()) {
for (const RecordVal &RV : EncodingDef->getValues()) {
// Ignore fixed fields in the record, we're looking for values like:
// bits<5> RST = { ?, ?, ?, ?, ? };
if (RV.getPrefix() || RV.getValue()->isComplete())
@ -317,6 +344,47 @@ static void emitInstBits(raw_ostream &OS, const APInt &Bits) {
<< ")";
}
void CodeEmitterGen::emitInstructionBaseValues(
raw_ostream &o, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
CodeGenTarget &Target, int HwMode) {
const CodeGenHwModes &HWM = Target.getHwModes();
if (HwMode == -1)
o << " static const uint64_t InstBits[] = {\n";
else
o << " static const uint64_t InstBits_" << HWM.getMode(HwMode).Name
<< "[] = {\n";
for (const CodeGenInstruction *CGI : NumberedInstructions) {
Record *R = CGI->TheDef;
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo")) {
o << " "; emitInstBits(o, APInt(BitWidth, 0)); o << ",\n";
continue;
}
Record *EncodingDef = R;
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
EncodingDef = EBM.get(HwMode);
}
}
BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
// Start by filling in fixed values.
APInt Value(BitWidth, 0);
for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) {
if (BitInit *B = dyn_cast<BitInit>(BI->getBit(e - i - 1)))
Value |= APInt(BitWidth, (uint64_t)B->getValue()) << (e - i - 1);
}
o << " ";
emitInstBits(o, Value);
o << "," << '\t' << "// " << R->getName() << "\n";
}
o << " UINT64_C(0)\n };\n";
}
void CodeEmitterGen::run(raw_ostream &o) {
CodeGenTarget Target(Records);
std::vector<Record*> Insts = Records.getAllDerivedDefinitions("Instruction");
@ -327,17 +395,29 @@ void CodeEmitterGen::run(raw_ostream &o) {
ArrayRef<const CodeGenInstruction*> NumberedInstructions =
Target.getInstructionsByEnumValue();
// Default to something sensible in case the target doesn't define Inst.
BitWidth = 32;
const CodeGenHwModes &HWM = Target.getHwModes();
// The set of HwModes used by instruction encodings.
std::set<unsigned> HwModes;
BitWidth = 0;
for (const CodeGenInstruction *CGI : NumberedInstructions) {
Record *R = CGI->TheDef;
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo"))
continue;
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
if (DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
for (auto &KV : EBM.Map) {
BitsInit *BI = KV.second->getValueAsBitsInit("Inst");
BitWidth = std::max(BitWidth, BI->getNumBits());
HwModes.insert(KV.first);
}
continue;
}
}
BitsInit *BI = R->getValueAsBitsInit("Inst");
BitWidth = BI->getNumBits();
break;
BitWidth = std::max(BitWidth, BI->getNumBits());
}
UseAPInt = BitWidth > 64;
@ -357,31 +437,25 @@ void CodeEmitterGen::run(raw_ostream &o) {
}
// Emit instruction base values
o << " static const uint64_t InstBits[] = {\n";
for (const CodeGenInstruction *CGI : NumberedInstructions) {
Record *R = CGI->TheDef;
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo")) {
o << " "; emitInstBits(o, APInt(BitWidth, 0)); o << ",\n";
continue;
}
BitsInit *BI = R->getValueAsBitsInit("Inst");
BitWidth = BI->getNumBits();
// Start by filling in fixed values.
APInt Value(BitWidth, 0);
for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) {
if (BitInit *B = dyn_cast<BitInit>(BI->getBit(e - i - 1)))
Value |= APInt(BitWidth, (uint64_t)B->getValue()) << (e - i - 1);
}
o << " ";
emitInstBits(o, Value);
o << "," << '\t' << "// " << R->getName() << "\n";
if (HwModes.empty()) {
emitInstructionBaseValues(o, NumberedInstructions, Target, -1);
} else {
for (unsigned HwMode : HwModes)
emitInstructionBaseValues(o, NumberedInstructions, Target, (int)HwMode);
}
o << " UINT64_C(0)\n };\n";
if (!HwModes.empty()) {
o << " const uint64_t *InstBits;\n";
o << " unsigned HwMode = STI.getHwMode();\n";
o << " switch (HwMode) {\n";
o << " default: llvm_unreachable(\"Unknown hardware mode!\"); break;\n";
for (unsigned I : HwModes) {
o << " case " << I << ": InstBits = InstBits_" << HWM.getMode(I).Name
<< "; break;\n";
}
o << " };\n";
}
// Map to accumulate all the cases.
std::map<std::string, std::vector<std::string>> CaseMap;

View File

@ -478,7 +478,8 @@ void CodeGenTarget::reverseBitsForLittleEndianEncoding() {
if (!isLittleEndianEncoding())
return;
std::vector<Record*> Insts = Records.getAllDerivedDefinitions("Instruction");
std::vector<Record *> Insts =
Records.getAllDerivedDefinitions("InstructionEncoding");
for (Record *R : Insts) {
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo"))

View File

@ -13,6 +13,7 @@
#include "CodeGenInstruction.h"
#include "CodeGenTarget.h"
#include "InfoByHwMode.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/CachedHashString.h"
@ -97,9 +98,11 @@ struct DecoderTableInfo {
struct EncodingAndInst {
const Record *EncodingDef;
const CodeGenInstruction *Inst;
StringRef HwModeName;
EncodingAndInst(const Record *EncodingDef, const CodeGenInstruction *Inst)
: EncodingDef(EncodingDef), Inst(Inst) {}
EncodingAndInst(const Record *EncodingDef, const CodeGenInstruction *Inst,
StringRef HwModeName = "")
: EncodingDef(EncodingDef), Inst(Inst), HwModeName(HwModeName) {}
};
struct EncodingIDAndOpcode {
@ -2382,12 +2385,50 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) {
Target.reverseBitsForLittleEndianEncoding();
// Parameterize the decoders based on namespace and instruction width.
std::set<StringRef> HwModeNames;
const auto &NumberedInstructions = Target.getInstructionsByEnumValue();
NumberedEncodings.reserve(NumberedInstructions.size());
DenseMap<Record *, unsigned> IndexOfInstruction;
// First, collect all HwModes referenced by the target.
for (const auto &NumberedInstruction : NumberedInstructions) {
IndexOfInstruction[NumberedInstruction->TheDef] = NumberedEncodings.size();
NumberedEncodings.emplace_back(NumberedInstruction->TheDef, NumberedInstruction);
if (const RecordVal *RV =
NumberedInstruction->TheDef->getValue("EncodingInfos")) {
if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
const CodeGenHwModes &HWM = Target.getHwModes();
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
for (auto &KV : EBM.Map)
HwModeNames.insert(HWM.getMode(KV.first).Name);
}
}
}
// If HwModeNames is empty, add the empty string so we always have one HwMode.
if (HwModeNames.empty())
HwModeNames.insert("");
for (const auto &NumberedInstruction : NumberedInstructions) {
IndexOfInstruction[NumberedInstruction->TheDef] = NumberedEncodings.size();
if (const RecordVal *RV =
NumberedInstruction->TheDef->getValue("EncodingInfos")) {
if (DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
const CodeGenHwModes &HWM = Target.getHwModes();
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
for (auto &KV : EBM.Map) {
NumberedEncodings.emplace_back(KV.second, NumberedInstruction,
HWM.getMode(KV.first).Name);
HwModeNames.insert(HWM.getMode(KV.first).Name);
}
continue;
}
}
// This instruction is encoded the same on all HwModes. Emit it for all
// HwModes.
for (StringRef HwModeName : HwModeNames)
NumberedEncodings.emplace_back(NumberedInstruction->TheDef,
NumberedInstruction, HwModeName);
}
for (const auto &NumberedAlias : RK.getAllDerivedDefinitions("AdditionalEncoding"))
NumberedEncodings.emplace_back(
@ -2415,13 +2456,19 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) {
NumInstructions++;
NumEncodings++;
StringRef DecoderNamespace = EncodingDef->getValueAsString("DecoderNamespace");
if (!Size)
continue;
if (Size) {
if (populateInstruction(Target, *EncodingDef, *Inst, i, Operands)) {
OpcMap[std::make_pair(DecoderNamespace, Size)].emplace_back(i, IndexOfInstruction.find(Def)->second);
} else
NumEncodingsOmitted++;
if (populateInstruction(Target, *EncodingDef, *Inst, i, Operands)) {
std::string DecoderNamespace =
EncodingDef->getValueAsString("DecoderNamespace");
if (!NumberedEncodings[i].HwModeName.empty())
DecoderNamespace +=
std::string("_") + NumberedEncodings[i].HwModeName.str();
OpcMap[std::make_pair(DecoderNamespace, Size)].emplace_back(
i, IndexOfInstruction.find(Def)->second);
} else {
NumEncodingsOmitted++;
}
}

View File

@ -192,6 +192,17 @@ void RegSizeInfoByHwMode::writeToStream(raw_ostream &OS) const {
OS << '}';
}
EncodingInfoByHwMode::EncodingInfoByHwMode(Record *R, const CodeGenHwModes &CGH) {
const HwModeSelect &MS = CGH.getHwModeSelect(R);
for (const HwModeSelect::PairType &P : MS.Items) {
assert(P.second && P.second->isSubClassOf("InstructionEncoding") &&
"Encoding must subclass InstructionEncoding");
auto I = Map.insert({P.first, P.second});
assert(I.second && "Duplicate entry?");
(void)I;
}
}
namespace llvm {
raw_ostream &operator<<(raw_ostream &OS, const ValueTypeByHwMode &T) {
T.writeToStream(OS);

View File

@ -184,6 +184,11 @@ raw_ostream &operator<<(raw_ostream &OS, const ValueTypeByHwMode &T);
raw_ostream &operator<<(raw_ostream &OS, const RegSizeInfo &T);
raw_ostream &operator<<(raw_ostream &OS, const RegSizeInfoByHwMode &T);
struct EncodingInfoByHwMode : public InfoByHwMode<Record*> {
EncodingInfoByHwMode(Record *R, const CodeGenHwModes &CGH);
EncodingInfoByHwMode() = default;
};
} // namespace llvm
#endif // LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H

View File

@ -1746,7 +1746,10 @@ void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) {
<< " return " << Target << "_MC"
<< "::resolveVariantSchedClassImpl(SchedClass, MI, CPUID); \n";
OS << " }\n";
if (TGT.getHwModes().getNumModeIds() > 1)
OS << " unsigned getHwMode() const override;\n";
OS << "};\n";
EmitHwModeCheck(Target + "GenMCSubtargetInfo", OS);
}
void SubtargetEmitter::EmitMCInstrAnalysisPredicateFunctions(raw_ostream &OS) {