From 88a5fbfcea79b4711542f0587bed39aa392da12f Mon Sep 17 00:00:00 2001 From: James Molloy Date: Thu, 19 Sep 2019 13:39:54 +0000 Subject: [PATCH] [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 --- .../llvm/CodeGen/TargetSubtargetInfo.h | 2 - llvm/include/llvm/MC/MCSubtargetInfo.h | 2 + llvm/include/llvm/Target/Target.td | 13 ++ llvm/test/TableGen/HwModeEncodeDecode.td | 81 +++++++++++ llvm/utils/TableGen/CodeEmitterGen.cpp | 134 ++++++++++++++---- llvm/utils/TableGen/CodeGenTarget.cpp | 3 +- .../utils/TableGen/FixedLenDecoderEmitter.cpp | 65 +++++++-- llvm/utils/TableGen/InfoByHwMode.cpp | 11 ++ llvm/utils/TableGen/InfoByHwMode.h | 5 + llvm/utils/TableGen/SubtargetEmitter.cpp | 3 + 10 files changed, 277 insertions(+), 42 deletions(-) create mode 100644 llvm/test/TableGen/HwModeEncodeDecode.td diff --git a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h index b24828152e5b..56018eca8c27 100644 --- a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h +++ b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h @@ -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 { diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index 9490a6ecedad..23a5999b359c 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -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 diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index fc63696142cc..93a613592882 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -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 Ms = [], list Ts = []> + : HwModeSelect { + // The length of this list must be the same as the length of Ms. + list 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 Pattern; diff --git a/llvm/test/TableGen/HwModeEncodeDecode.td b/llvm/test/TableGen/HwModeEncodeDecode.td new file mode 100644 index 000000000000..b76d85646519 --- /dev/null +++ b/llvm/test/TableGen/HwModeEncodeDecode.td @@ -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 { + 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: { diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp index 10c42bbc6ff6..b021d08881fb 100644 --- a/llvm/utils/TableGen/CodeEmitterGen.cpp +++ b/llvm/utils/TableGen/CodeEmitterGen.cpp @@ -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 &NamedOpIndices, std::string &Case, CodeGenTarget &Target); + void emitInstructionBaseValues( + raw_ostream &o, ArrayRef 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(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 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 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(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(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 Insts = Records.getAllDerivedDefinitions("Instruction"); @@ -327,17 +395,29 @@ void CodeEmitterGen::run(raw_ostream &o) { ArrayRef 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 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(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(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> CaseMap; diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 478bbb7e4809..fa8b842c97f9 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -478,7 +478,8 @@ void CodeGenTarget::reverseBitsForLittleEndianEncoding() { if (!isLittleEndianEncoding()) return; - std::vector Insts = Records.getAllDerivedDefinitions("Instruction"); + std::vector Insts = + Records.getAllDerivedDefinitions("InstructionEncoding"); for (Record *R : Insts) { if (R->getValueAsString("Namespace") == "TargetOpcode" || R->getValueAsBit("isPseudo")) diff --git a/llvm/utils/TableGen/FixedLenDecoderEmitter.cpp b/llvm/utils/TableGen/FixedLenDecoderEmitter.cpp index 01f4115aa1dd..ac69b431607d 100644 --- a/llvm/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/llvm/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -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 HwModeNames; const auto &NumberedInstructions = Target.getInstructionsByEnumValue(); NumberedEncodings.reserve(NumberedInstructions.size()); DenseMap 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(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(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++; } } diff --git a/llvm/utils/TableGen/InfoByHwMode.cpp b/llvm/utils/TableGen/InfoByHwMode.cpp index d9662889a5db..7cd1b0f08132 100644 --- a/llvm/utils/TableGen/InfoByHwMode.cpp +++ b/llvm/utils/TableGen/InfoByHwMode.cpp @@ -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); diff --git a/llvm/utils/TableGen/InfoByHwMode.h b/llvm/utils/TableGen/InfoByHwMode.h index 9e5cc3d5f2a4..d92e5901a7f3 100644 --- a/llvm/utils/TableGen/InfoByHwMode.h +++ b/llvm/utils/TableGen/InfoByHwMode.h @@ -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 { + EncodingInfoByHwMode(Record *R, const CodeGenHwModes &CGH); + EncodingInfoByHwMode() = default; +}; + } // namespace llvm #endif // LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index 41589b6b10de..f97057236502 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -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) {