| //===- InstrInfoEmitter.cpp - Generate a Instruction Set Desc. --*- 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This tablegen backend is responsible for emitting a description of the target | |
| // instruction set for the code generator. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "Basic/SequenceToOffsetTable.h" | |
| #include "Common/CodeGenDAGPatterns.h" | |
| #include "Common/CodeGenInstruction.h" | |
| #include "Common/CodeGenRegisters.h" | |
| #include "Common/CodeGenSchedule.h" | |
| #include "Common/CodeGenTarget.h" | |
| #include "Common/PredicateExpander.h" | |
| #include "Common/SubtargetFeatureInfo.h" | |
| #include "Common/Types.h" | |
| #include "TableGenBackends.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/CodeGenHelpers.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/TGTimer.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <iterator> | |
| #include <map> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| using namespace llvm; | |
| static cl::OptionCategory InstrInfoEmitterCat("Options for -gen-instr-info"); | |
| static cl::opt<bool> ExpandMIOperandInfo( | |
| "instr-info-expand-mi-operand-info", | |
| cl::desc("Expand operand's MIOperandInfo DAG into suboperands"), | |
| cl::cat(InstrInfoEmitterCat), cl::init(true)); | |
| namespace { | |
| class InstrInfoEmitter { | |
| const RecordKeeper &Records; | |
| const CodeGenDAGPatterns CDP; | |
| const CodeGenSchedModels &SchedModels; | |
| public: | |
| InstrInfoEmitter(const RecordKeeper &R) | |
| : Records(R), CDP(R, /*ExpandHwMode=*/false), | |
| SchedModels(CDP.getTargetInfo().getSchedModels()) {} | |
| // run - Output the instruction set description. | |
| void run(raw_ostream &OS); | |
| private: | |
| void emitEnums(raw_ostream &OS, | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions); | |
| using OperandInfoTy = std::vector<std::string>; | |
| using OperandInfoListTy = std::vector<OperandInfoTy>; | |
| using OperandInfoMapTy = std::map<OperandInfoTy, unsigned>; | |
| DenseMap<const CodeGenInstruction *, const CodeGenInstruction *> | |
| TargetSpecializedPseudoInsts; | |
| /// Compute mapping of opcodes which should have their definitions overridden | |
| /// by a target version. | |
| void buildTargetSpecializedPseudoInstsMap(); | |
| /// Generate member functions in the target-specific GenInstrInfo class. | |
| /// | |
| /// This method is used to custom expand TIIPredicate definitions. | |
| /// See file llvm/Target/TargetInstPredicates.td for a description of what is | |
| /// a TIIPredicate and how to use it. | |
| void emitTIIHelperMethods(raw_ostream &OS, StringRef TargetName, | |
| bool ExpandDefinition = true); | |
| /// Expand TIIPredicate definitions to functions that accept a const MCInst | |
| /// reference. | |
| void emitMCIIHelperMethods(raw_ostream &OS, StringRef TargetName); | |
| /// Write verifyInstructionPredicates methods. | |
| void emitFeatureVerifier(raw_ostream &OS, const CodeGenTarget &Target); | |
| void emitRecord(const CodeGenInstruction &Inst, unsigned Num, | |
| const Record *InstrInfo, | |
| std::map<std::vector<const Record *>, unsigned> &EL, | |
| const OperandInfoMapTy &OperandInfo, raw_ostream &OS); | |
| void emitOperandTypeMappings( | |
| raw_ostream &OS, const CodeGenTarget &Target, | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions); | |
| void emitOperandNameMappings( | |
| raw_ostream &OS, const CodeGenTarget &Target, | |
| ArrayRef<const CodeGenInstruction *> TargetInstructions); | |
| void emitLogicalOperandSizeMappings( | |
| raw_ostream &OS, StringRef Namespace, | |
| ArrayRef<const CodeGenInstruction *> TargetInstructions); | |
| // Operand information. | |
| unsigned CollectOperandInfo(OperandInfoListTy &OperandInfoList, | |
| OperandInfoMapTy &OperandInfoMap); | |
| void EmitOperandInfo(raw_ostream &OS, OperandInfoListTy &OperandInfoList); | |
| OperandInfoTy GetOperandInfo(const CodeGenInstruction &Inst); | |
| }; | |
| } // end anonymous namespace | |
| //===----------------------------------------------------------------------===// | |
| // Operand Info Emission. | |
| //===----------------------------------------------------------------------===// | |
| InstrInfoEmitter::OperandInfoTy | |
| InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { | |
| OperandInfoTy Result; | |
| StringRef Namespace = CDP.getTargetInfo().getInstNamespace(); | |
| for (auto &Op : Inst.Operands) { | |
| // Handle aggregate operands and normal operands the same way by expanding | |
| // either case into a list of operands for this op. | |
| std::vector<CGIOperandList::OperandInfo> OperandList; | |
| // This might be a multiple operand thing. Targets like X86 have registers | |
| // in their multi-operand operands. It may also be an anonymous operand, | |
| // which has a single operand, but no declared class for the operand. | |
| const DagInit *MIOI = Op.MIOperandInfo; | |
| if (!MIOI || MIOI->getNumArgs() == 0) { | |
| // Single, anonymous, operand. | |
| OperandList.push_back(Op); | |
| } else { | |
| for (unsigned j = 0, e = Op.MINumOperands; j != e; ++j) { | |
| OperandList.push_back(Op); | |
| auto *OpR = cast<DefInit>(MIOI->getArg(j))->getDef(); | |
| OperandList.back().Rec = OpR; | |
| } | |
| } | |
| for (const auto &[OpInfo, Constraint] : | |
| zip_equal(OperandList, Op.Constraints)) { | |
| const Record *OpR = OpInfo.Rec; | |
| std::string Res; | |
| if (OpR->isSubClassOf("RegisterOperand")) | |
| OpR = OpR->getValueAsDef("RegClass"); | |
| if (OpR->isSubClassOf("RegClassByHwMode") && | |
| !OpR->getValueAsListOfDefs("Objects").empty()) { | |
| Res += Namespace; | |
| Res += "::"; | |
| Res += OpR->getName(); | |
| Res += ", "; | |
| } else if (OpR->isSubClassOf("RegClassByHwMode")) { | |
| // An empty RegClassByHwMode is the generic ptr_rc placeholder. It must | |
| // be substituted with a real class per target (via | |
| // RemapAllTargetPseudoPointerOperands); reaching here means it was not. | |
| if (Inst.isPseudo) { | |
| // TODO: Verify this is a fixed pseudo | |
| PrintError(Inst.TheDef, | |
| "missing target override for pseudoinstruction " | |
| "using ptr_rc"); | |
| PrintNote(OpR->getLoc(), | |
| "target should define equivalent instruction " | |
| "with RegisterClassLike replacement; (use " | |
| "RemapAllTargetPseudoPointerOperands?)"); | |
| } else { | |
| PrintError(Inst.TheDef, "non-pseudoinstruction user of ptr_rc"); | |
| } | |
| // -1 means the operand does not have a fixed register class. | |
| Res += "-1, "; | |
| } else if (OpR->isSubClassOf("RegisterClass")) { | |
| Res += getQualifiedName(OpR) + "RegClassID, "; | |
| } else { | |
| // -1 means the operand does not have a fixed register class. | |
| Res += "-1, "; | |
| } | |
| // Fill in applicable flags. | |
| Res += "0"; | |
| if (OpR->isSubClassOf("RegClassByHwMode")) | |
| Res += "|(1<<MCOI::LookupRegClassByHwMode)"; | |
| // Predicate operands. Check to see if the original unexpanded operand | |
| // was of type PredicateOp. | |
| if (Op.Rec->isSubClassOf("PredicateOp")) | |
| Res += "|(1<<MCOI::Predicate)"; | |
| // Optional def operands. Check to see if the original unexpanded operand | |
| // was of type OptionalDefOperand. | |
| if (Op.Rec->isSubClassOf("OptionalDefOperand")) | |
| Res += "|(1<<MCOI::OptionalDef)"; | |
| // Branch target operands. Check to see if the original unexpanded | |
| // operand was of type BranchTargetOperand. | |
| if (Op.Rec->isSubClassOf("BranchTargetOperand")) | |
| Res += "|(1<<MCOI::BranchTarget)"; | |
| // Fill in operand type. | |
| Res += ", "; | |
| assert(!Op.OperandType.empty() && "Invalid operand type."); | |
| Res += Op.OperandType; | |
| // Fill in constraint info. | |
| Res += ", "; | |
| if (Constraint.isNone()) { | |
| Res += "0"; | |
| } else if (Constraint.isEarlyClobber()) { | |
| Res += "MCOI_EARLY_CLOBBER"; | |
| } else { | |
| assert(Constraint.isTied()); | |
| Res += "MCOI_TIED_TO(" + utostr(Constraint.getTiedOperand()) + ")"; | |
| } | |
| Result.push_back(Res); | |
| } | |
| } | |
| return Result; | |
| } | |
| unsigned | |
| InstrInfoEmitter::CollectOperandInfo(OperandInfoListTy &OperandInfoList, | |
| OperandInfoMapTy &OperandInfoMap) { | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| unsigned Offset = 0; | |
| for (const CodeGenInstruction *Inst : Target.getInstructions()) { | |
| auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst); | |
| if (OverrideEntry != TargetSpecializedPseudoInsts.end()) | |
| Inst = OverrideEntry->second; | |
| OperandInfoTy OperandInfo = GetOperandInfo(*Inst); | |
| if (OperandInfoMap.try_emplace(OperandInfo, Offset).second) { | |
| OperandInfoList.push_back(OperandInfo); | |
| Offset += OperandInfo.size(); | |
| } | |
| } | |
| return Offset; | |
| } | |
| void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS, | |
| OperandInfoListTy &OperandInfoList) { | |
| unsigned Offset = 0; | |
| for (auto &OperandInfo : OperandInfoList) { | |
| OS << " /* " << Offset << " */"; | |
| for (auto &Info : OperandInfo) | |
| OS << " { " << Info << " },"; | |
| OS << '\n'; | |
| Offset += OperandInfo.size(); | |
| } | |
| } | |
| static void emitGetInstructionIndexForOpLookup( | |
| raw_ostream &OS, const MapVector<SmallVector<int>, unsigned> &OperandMap, | |
| ArrayRef<unsigned> InstructionIndex) { | |
| StringRef Type = OperandMap.size() <= UINT8_MAX + 1 ? "uint8_t" : "uint16_t"; | |
| OS << "LLVM_READONLY static " << Type | |
| << " getInstructionIndexForOpLookup(uint32_t Opcode) {\n" | |
| " static constexpr " | |
| << Type << " InstructionIndex[] = {"; | |
| for (auto [TableIndex, Entry] : enumerate(InstructionIndex)) | |
| OS << (TableIndex % 16 == 0 ? "\n " : " ") << Entry << ','; | |
| OS << "\n };\n" | |
| " return InstructionIndex[Opcode];\n" | |
| "}\n"; | |
| } | |
| static void | |
| emitGetNamedOperandIdx(raw_ostream &OS, | |
| const MapVector<SmallVector<int>, unsigned> &OperandMap, | |
| unsigned MaxOperandNo, unsigned NumOperandNames) { | |
| OS << "LLVM_READONLY int16_t getNamedOperandIdx(uint32_t Opcode, OpName " | |
| "Name) {\n"; | |
| OS << " assert(Name != OpName::NUM_OPERAND_NAMES);\n"; | |
| if (!NumOperandNames) { | |
| // There are no operands, so no need to emit anything | |
| OS << " return -1;\n}\n"; | |
| return; | |
| } | |
| assert(MaxOperandNo <= INT16_MAX && | |
| "Too many operands for the operand name -> index table"); | |
| StringRef Type = MaxOperandNo <= INT8_MAX ? "int8_t" : "int16_t"; | |
| OS << " static constexpr " << Type << " OperandMap[][" << NumOperandNames | |
| << "] = {\n"; | |
| for (const auto &[OpList, _] : OperandMap) { | |
| // Emit a row of the OperandMap table. | |
| OS << " {"; | |
| for (unsigned ID = 0; ID < NumOperandNames; ++ID) | |
| OS << (ID < OpList.size() ? OpList[ID] : -1) << ", "; | |
| OS << "},\n"; | |
| } | |
| OS << " };\n"; | |
| OS << " unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);\n" | |
| " return OperandMap[InstrIdx][(unsigned)Name];\n" | |
| "}\n"; | |
| } | |
| static void | |
| emitGetOperandIdxName(raw_ostream &OS, | |
| const MapVector<StringRef, unsigned> &OperandNameToID, | |
| const MapVector<SmallVector<int>, unsigned> &OperandMap, | |
| unsigned MaxNumOperands, unsigned NumOperandNames) { | |
| OS << "LLVM_READONLY OpName getOperandIdxName(uint32_t Opcode, int16_t Idx) " | |
| "{\n"; | |
| OS << " assert(Idx >= 0 && Idx < " << MaxNumOperands << ");\n"; | |
| if (!MaxNumOperands) { | |
| // There are no operands, so no need to emit anything | |
| OS << " return -1;\n}\n"; | |
| return; | |
| } | |
| OS << " static constexpr OpName OperandMap[][" << MaxNumOperands | |
| << "] = {\n"; | |
| for (const auto &[OpList, _] : OperandMap) { | |
| SmallVector<unsigned> IDs(MaxNumOperands, NumOperandNames); | |
| for (const auto &[ID, Idx] : enumerate(OpList)) { | |
| if (Idx >= 0) | |
| IDs[Idx] = ID; | |
| } | |
| // Emit a row of the OperandMap table. Map operand indices to enum values. | |
| OS << " {"; | |
| for (unsigned ID : IDs) { | |
| if (ID == NumOperandNames) | |
| OS << "OpName::NUM_OPERAND_NAMES, "; | |
| else | |
| OS << "OpName::" << OperandNameToID.getArrayRef()[ID].first << ", "; | |
| } | |
| OS << "},\n"; | |
| } | |
| OS << " };\n"; | |
| OS << " unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);\n" | |
| " return OperandMap[InstrIdx][(unsigned)Idx];\n" | |
| "}\n"; | |
| } | |
| /// Generate a table and function for looking up the indices of operands by | |
| /// name. | |
| /// | |
| /// This code generates: | |
| /// - An enum in the llvm::TargetNamespace::OpName namespace, with one entry | |
| /// for each operand name. | |
| /// - A 2-dimensional table for mapping OpName enum values to operand indices. | |
| /// - A function called getNamedOperandIdx(uint32_t Opcode, OpName Name) | |
| /// for looking up the operand index for an instruction, given a value from | |
| /// OpName enum | |
| /// - A 2-dimensional table for mapping operand indices to OpName enum values. | |
| /// - A function called getOperandIdxName(uint32_t Opcode, int16_t Idx) | |
| /// for looking up the OpName enum for an instruction, given the operand | |
| /// index. This is the inverse of getNamedOperandIdx(). | |
| /// | |
| /// Fixed/Predefined instructions do not have UseNamedOperandTable enabled, so | |
| /// we can just skip them. Hence accept just the TargetInstructions. | |
| void InstrInfoEmitter::emitOperandNameMappings( | |
| raw_ostream &OS, const CodeGenTarget &Target, | |
| ArrayRef<const CodeGenInstruction *> TargetInstructions) { | |
| // Map of operand names to their ID. | |
| MapVector<StringRef, unsigned> OperandNameToID; | |
| /// A key in this map is a vector mapping OpName ID values to instruction | |
| /// operand indices or -1 (but without any trailing -1 values which will be | |
| /// added later). The corresponding value in this map is the index of that row | |
| /// in the emitted OperandMap table. This map helps to unique entries among | |
| /// instructions that have identical OpName -> Operand index mapping. | |
| MapVector<SmallVector<int>, unsigned> OperandMap; | |
| // Max operand index seen. | |
| unsigned MaxOperandNo = 0; | |
| // Fixed/Predefined instructions do not have UseNamedOperandTable enabled, so | |
| // add a dummy map entry for them. | |
| OperandMap.try_emplace({}, 0); | |
| unsigned FirstTargetVal = TargetInstructions.front()->EnumVal; | |
| SmallVector<unsigned> InstructionIndex(FirstTargetVal, 0); | |
| for (const CodeGenInstruction *Inst : TargetInstructions) { | |
| if (!Inst->TheDef->getValueAsBit("UseNamedOperandTable")) { | |
| InstructionIndex.push_back(0); | |
| continue; | |
| } | |
| SmallVector<int> OpList; | |
| for (const auto &Info : Inst->Operands) { | |
| unsigned ID = | |
| OperandNameToID.try_emplace(Info.Name, OperandNameToID.size()) | |
| .first->second; | |
| OpList.resize(std::max((unsigned)OpList.size(), ID + 1), -1); | |
| OpList[ID] = Info.MIOperandNo; | |
| MaxOperandNo = std::max(MaxOperandNo, Info.MIOperandNo); | |
| } | |
| auto [It, Inserted] = | |
| OperandMap.try_emplace(std::move(OpList), OperandMap.size()); | |
| InstructionIndex.push_back(It->second); | |
| } | |
| const size_t NumOperandNames = OperandNameToID.size(); | |
| const unsigned MaxNumOperands = MaxOperandNo + 1; | |
| const SmallString<32> Namespace({"llvm::", Target.getInstNamespace()}); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_OPERAND_ENUM"); | |
| NamespaceEmitter NS(OS, Namespace); | |
| assert(NumOperandNames <= UINT16_MAX && | |
| "Too many operands for the operand index -> name table"); | |
| StringRef EnumType = getMinimalTypeForRange(NumOperandNames); | |
| OS << "enum class OpName : " << EnumType << " {\n"; | |
| for (const auto &[Op, I] : OperandNameToID) | |
| OS << " " << Op << " = " << I << ",\n"; | |
| OS << " NUM_OPERAND_NAMES = " << NumOperandNames << ",\n"; | |
| OS << "}; // enum class OpName\n\n"; | |
| OS << "LLVM_READONLY int16_t getNamedOperandIdx(uint32_t Opcode, OpName " | |
| "Name);\n"; | |
| OS << "LLVM_READONLY OpName getOperandIdxName(uint32_t Opcode, int16_t " | |
| "Idx);\n"; | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_NAMED_OPS"); | |
| NamespaceEmitter NS(OS, Namespace); | |
| emitGetInstructionIndexForOpLookup(OS, OperandMap, InstructionIndex); | |
| emitGetNamedOperandIdx(OS, OperandMap, MaxOperandNo, NumOperandNames); | |
| emitGetOperandIdxName(OS, OperandNameToID, OperandMap, MaxNumOperands, | |
| NumOperandNames); | |
| } | |
| } | |
| /// Generate an enum for all the operand types for this target, under the | |
| /// llvm::TargetNamespace::OpTypes namespace. | |
| /// Operand types are all definitions derived of the Operand Target.td class. | |
| /// | |
| void InstrInfoEmitter::emitOperandTypeMappings( | |
| raw_ostream &OS, const CodeGenTarget &Target, | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions) { | |
| StringRef Namespace = Target.getInstNamespace(); | |
| // These generated functions are used only by the X86 target | |
| // (in bolt/lib/Target/X86/X86MCPlusBuilder.cpp). So emit them only | |
| // for X86. | |
| if (Namespace != "X86") | |
| return; | |
| ArrayRef<const Record *> Operands = | |
| Records.getAllDerivedDefinitions("Operand"); | |
| ArrayRef<const Record *> RegisterOperands = | |
| Records.getAllDerivedDefinitions("RegisterOperand"); | |
| ArrayRef<const Record *> RegisterClasses = | |
| Records.getAllDerivedDefinitions("RegisterClass"); | |
| unsigned EnumVal = 0; | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_OPERAND_TYPES_ENUM"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace + "::OpTypes").str()); | |
| OS << "enum OperandType {\n"; | |
| for (ArrayRef<const Record *> RecordsToAdd : | |
| {Operands, RegisterOperands, RegisterClasses}) { | |
| for (const Record *Op : RecordsToAdd) { | |
| if (!Op->isAnonymous()) | |
| OS << " " << Op->getName() << " = " << EnumVal << ",\n"; | |
| ++EnumVal; | |
| } | |
| } | |
| OS << " OPERAND_TYPE_LIST_END" << "\n};\n"; | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_OPERAND_TYPE"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace).str()); | |
| OS << "LLVM_READONLY\n"; | |
| OS << "static int getOperandType(uint32_t Opcode, uint16_t OpIdx) {\n"; | |
| auto getInstrName = [&](int I) -> StringRef { | |
| return NumberedInstructions[I]->getName(); | |
| }; | |
| // TODO: Factor out duplicate operand lists to compress the tables. | |
| std::vector<size_t> OperandOffsets; | |
| std::vector<const Record *> OperandRecords; | |
| size_t CurrentOffset = 0; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| OperandOffsets.push_back(CurrentOffset); | |
| for (const auto &Op : Inst->Operands) { | |
| const DagInit *MIOI = Op.MIOperandInfo; | |
| if (!ExpandMIOperandInfo || !MIOI || MIOI->getNumArgs() == 0) { | |
| // Single, anonymous, operand. | |
| OperandRecords.push_back(Op.Rec); | |
| ++CurrentOffset; | |
| } else { | |
| for (const Init *Arg : MIOI->getArgs()) { | |
| OperandRecords.push_back(cast<DefInit>(Arg)->getDef()); | |
| ++CurrentOffset; | |
| } | |
| } | |
| } | |
| } | |
| // Emit the table of offsets (indexes) into the operand type table. | |
| // Size the unsigned integer offset to save space. | |
| assert(OperandRecords.size() <= UINT32_MAX && | |
| "Too many operands for offset table"); | |
| OS << " static constexpr " | |
| << getMinimalTypeForRange(OperandRecords.size()); | |
| OS << " Offsets[] = {\n"; | |
| for (const auto &[Idx, Offset] : enumerate(OperandOffsets)) | |
| OS << " " << Offset << ", // " << getInstrName(Idx) << '\n'; | |
| OS << " };\n"; | |
| // Add an entry for the end so that we don't need to special case it below. | |
| OperandOffsets.push_back(OperandRecords.size()); | |
| // Emit the actual operand types in a flat table. | |
| // Size the signed integer operand type to save space. | |
| assert(EnumVal <= INT16_MAX && | |
| "Too many operand types for operand types table"); | |
| OS << "\n using namespace OpTypes;\n"; | |
| OS << " static"; | |
| OS << (EnumVal <= INT8_MAX ? " constexpr int8_t" : " constexpr int16_t"); | |
| OS << " OpcodeOperandTypes[] = {"; | |
| size_t CurOffset = 0; | |
| for (auto [Idx, OpR] : enumerate(OperandRecords)) { | |
| // We print each Opcode's operands in its own row. | |
| if (Idx == OperandOffsets[CurOffset]) { | |
| OS << "\n /* " << getInstrName(CurOffset) << " */\n "; | |
| while (OperandOffsets[++CurOffset] == Idx) | |
| OS << "/* " << getInstrName(CurOffset) << " */\n "; | |
| } | |
| if ((OpR->isSubClassOf("Operand") || | |
| OpR->isSubClassOf("RegisterOperand") || | |
| OpR->isSubClassOf("RegisterClass")) && | |
| !OpR->isAnonymous()) | |
| OS << OpR->getName(); | |
| else | |
| OS << -1; | |
| OS << ", "; | |
| } | |
| OS << "\n };\n"; | |
| OS << " return OpcodeOperandTypes[Offsets[Opcode] + OpIdx];\n"; | |
| OS << "}\n"; | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_MEM_OPERAND_SIZE"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace).str()); | |
| OS << "LLVM_READONLY\n"; | |
| OS << "static int getMemOperandSize(int OpType) {\n"; | |
| OS << " switch (OpType) {\n"; | |
| std::map<int, SmallVector<StringRef, 0>> SizeToOperandName; | |
| for (const Record *Op : Operands) { | |
| if (!Op->isSubClassOf("X86MemOperand")) | |
| continue; | |
| if (int Size = Op->getValueAsInt("Size")) | |
| SizeToOperandName[Size].push_back(Op->getName()); | |
| } | |
| OS << " default: return 0;\n"; | |
| for (const auto &[Size, OperandNames] : SizeToOperandName) { | |
| for (const StringRef &OperandName : OperandNames) | |
| OS << " case OpTypes::" << OperandName << ":\n"; | |
| OS << " return " << Size << ";\n\n"; | |
| } | |
| OS << " }\n}\n"; | |
| } | |
| } | |
| // Fixed/Predefined instructions do not have UseLogicalOperandMappings | |
| // enabled, so we can just skip them. Hence accept TargetInstructions. | |
| void InstrInfoEmitter::emitLogicalOperandSizeMappings( | |
| raw_ostream &OS, StringRef Namespace, | |
| ArrayRef<const CodeGenInstruction *> TargetInstructions) { | |
| std::map<std::vector<unsigned>, unsigned> LogicalOpSizeMap; | |
| std::map<unsigned, std::vector<std::string>> InstMap; | |
| size_t LogicalOpListSize = 0U; | |
| std::vector<unsigned> LogicalOpList; | |
| for (const auto *Inst : TargetInstructions) { | |
| if (!Inst->TheDef->getValueAsBit("UseLogicalOperandMappings")) | |
| continue; | |
| LogicalOpList.clear(); | |
| llvm::transform(Inst->Operands, std::back_inserter(LogicalOpList), | |
| [](const CGIOperandList::OperandInfo &Op) -> unsigned { | |
| auto *MIOI = Op.MIOperandInfo; | |
| if (!MIOI || MIOI->getNumArgs() == 0) | |
| return 1; | |
| return MIOI->getNumArgs(); | |
| }); | |
| LogicalOpListSize = std::max(LogicalOpList.size(), LogicalOpListSize); | |
| auto I = | |
| LogicalOpSizeMap.try_emplace(LogicalOpList, LogicalOpSizeMap.size()) | |
| .first; | |
| InstMap[I->second].push_back((Namespace + "::" + Inst->getName()).str()); | |
| } | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace).str()); | |
| OS << "LLVM_READONLY static unsigned\n"; | |
| OS << "getLogicalOperandSize(uint32_t Opcode, uint16_t LogicalOpIdx) {\n"; | |
| if (!InstMap.empty()) { | |
| std::vector<const std::vector<unsigned> *> LogicalOpSizeList( | |
| LogicalOpSizeMap.size()); | |
| for (auto &P : LogicalOpSizeMap) { | |
| LogicalOpSizeList[P.second] = &P.first; | |
| } | |
| OS << " static const unsigned SizeMap[][" << LogicalOpListSize | |
| << "] = {\n"; | |
| for (auto &R : LogicalOpSizeList) { | |
| const auto &Row = *R; | |
| OS << " {"; | |
| int i; | |
| for (i = 0; i < static_cast<int>(Row.size()); ++i) { | |
| OS << Row[i] << ", "; | |
| } | |
| for (; i < static_cast<int>(LogicalOpListSize); ++i) { | |
| OS << "0, "; | |
| } | |
| OS << "}, \n"; | |
| } | |
| OS << " };\n"; | |
| OS << " switch (Opcode) {\n"; | |
| OS << " default: return LogicalOpIdx;\n"; | |
| for (auto &P : InstMap) { | |
| auto OpMapIdx = P.first; | |
| const auto &Insts = P.second; | |
| for (const auto &Inst : Insts) { | |
| OS << " case " << Inst << ":\n"; | |
| } | |
| OS << " return SizeMap[" << OpMapIdx << "][LogicalOpIdx];\n"; | |
| } | |
| OS << " }\n"; | |
| } else { | |
| OS << " return LogicalOpIdx;\n"; | |
| } | |
| OS << "}\n"; | |
| OS << "LLVM_READONLY static inline unsigned\n"; | |
| OS << "getLogicalOperandIdx(uint32_t Opcode, uint16_t LogicalOpIdx) {\n"; | |
| OS << " auto S = 0U;\n"; | |
| OS << " for (auto i = 0U; i < LogicalOpIdx; ++i)\n"; | |
| OS << " S += getLogicalOperandSize(Opcode, i);\n"; | |
| OS << " return S;\n"; | |
| OS << "}\n"; | |
| } | |
| void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS, | |
| StringRef TargetName) { | |
| ArrayRef<const Record *> TIIPredicates = | |
| Records.getAllDerivedDefinitions("TIIPredicate"); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_MC_HELPER_DECLS"); | |
| NamespaceEmitter LlvmNS(OS, "llvm"); | |
| OS << "class MCInst;\n"; | |
| OS << "class FeatureBitset;\n\n"; | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| ArrayRef<const Record *> RegClassByHwMode = Target.getAllRegClassByHwMode(); | |
| if (!RegClassByHwMode.empty()) { | |
| const CodeGenHwModes &CGH = Target.getHwModes(); | |
| unsigned NumModes = CGH.getNumModeIds(); | |
| unsigned NumClassesByHwMode = RegClassByHwMode.size(); | |
| OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables[" | |
| << NumModes << "][" << NumClassesByHwMode << "];\n\n"; | |
| } | |
| NamespaceEmitter TargetNS(OS, (TargetName + "_MC").str()); | |
| for (const Record *Rec : TIIPredicates) | |
| OS << "bool " << Rec->getValueAsString("FunctionName") | |
| << "(const MCInst &MI);\n"; | |
| OS << "void verifyInstructionPredicates(unsigned Opcode, const " | |
| "FeatureBitset " | |
| "&Features);\n"; | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_MC_HELPERS"); | |
| NamespaceEmitter NS(OS, ("llvm::" + TargetName + "_MC").str()); | |
| PredicateExpander PE(TargetName); | |
| PE.setExpandForMC(true); | |
| for (const Record *Rec : TIIPredicates) { | |
| OS << "bool " << Rec->getValueAsString("FunctionName"); | |
| OS << "(const MCInst &MI) {\n"; | |
| OS << PE.getIndent(); | |
| PE.expandStatement(OS, Rec->getValueAsDef("Body")); | |
| OS << "\n}\n\n"; | |
| } | |
| } | |
| } | |
| static std::string | |
| getNameForFeatureBitset(ArrayRef<const Record *> FeatureBitset) { | |
| std::string Name = "CEFBS"; | |
| for (const Record *Feature : FeatureBitset) | |
| Name += ("_" + Feature->getName()).str(); | |
| return Name; | |
| } | |
| void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS, | |
| const CodeGenTarget &Target) { | |
| const auto &All = SubtargetFeatureInfo::getAll(Records); | |
| SubtargetFeatureInfoMap SubtargetFeatures; | |
| SubtargetFeatures.insert(All.begin(), All.end()); | |
| OS << "#if (defined(ENABLE_INSTR_PREDICATE_VERIFIER) && !defined(NDEBUG)) " | |
| << "||\\\n" | |
| << " defined(GET_AVAILABLE_OPCODE_CHECKER)\n" | |
| << "#define GET_COMPUTE_FEATURES\n" | |
| << "#endif\n"; | |
| std::string Namespace = ("llvm::" + Target.getName() + "_MC").str(); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_COMPUTE_FEATURES"); | |
| NamespaceEmitter NS(OS, Namespace); | |
| // Emit the subtarget feature enumeration. | |
| SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures, | |
| OS); | |
| // Emit the available features compute function. | |
| OS << "inline "; | |
| SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( | |
| Target.getName(), "", "computeAvailableFeatures", SubtargetFeatures, | |
| OS); | |
| std::vector<std::vector<const Record *>> FeatureBitsets; | |
| for (const CodeGenInstruction *Inst : Target.getInstructions()) { | |
| FeatureBitsets.emplace_back(); | |
| for (const Record *Predicate : | |
| Inst->TheDef->getValueAsListOfDefs("Predicates")) { | |
| const auto &I = SubtargetFeatures.find(Predicate); | |
| if (I != SubtargetFeatures.end()) | |
| FeatureBitsets.back().push_back(I->second.TheDef); | |
| } | |
| } | |
| llvm::sort(FeatureBitsets, [&](ArrayRef<const Record *> A, | |
| ArrayRef<const Record *> B) { | |
| if (A.size() < B.size()) | |
| return true; | |
| if (A.size() > B.size()) | |
| return false; | |
| for (auto Pair : zip(A, B)) { | |
| if (std::get<0>(Pair)->getName() < std::get<1>(Pair)->getName()) | |
| return true; | |
| if (std::get<0>(Pair)->getName() > std::get<1>(Pair)->getName()) | |
| return false; | |
| } | |
| return false; | |
| }); | |
| FeatureBitsets.erase(llvm::unique(FeatureBitsets), FeatureBitsets.end()); | |
| OS << "inline FeatureBitset computeRequiredFeatures(unsigned Opcode) {\n" | |
| << " enum : " << getMinimalTypeForRange(FeatureBitsets.size()) << " {\n" | |
| << " CEFBS_None,\n"; | |
| for (const auto &FeatureBitset : FeatureBitsets) { | |
| if (FeatureBitset.empty()) | |
| continue; | |
| OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n"; | |
| } | |
| OS << " };\n\n" | |
| << " static constexpr FeatureBitset FeatureBitsets[] = {\n" | |
| << " {}, // CEFBS_None\n"; | |
| for (const auto &FeatureBitset : FeatureBitsets) { | |
| if (FeatureBitset.empty()) | |
| continue; | |
| OS << " {"; | |
| for (const auto &Feature : FeatureBitset) { | |
| const auto &I = SubtargetFeatures.find(Feature); | |
| assert(I != SubtargetFeatures.end() && "Didn't import predicate?"); | |
| OS << I->second.getEnumBitName() << ", "; | |
| } | |
| OS << "},\n"; | |
| } | |
| OS << " };\n" | |
| << " static constexpr " << getMinimalTypeForRange(FeatureBitsets.size()) | |
| << " RequiredFeaturesRefs[] = {\n"; | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions = | |
| Target.getInstructions(); | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| OS << " CEFBS"; | |
| unsigned NumPredicates = 0; | |
| for (const Record *Predicate : | |
| Inst->TheDef->getValueAsListOfDefs("Predicates")) { | |
| const auto &I = SubtargetFeatures.find(Predicate); | |
| if (I != SubtargetFeatures.end()) { | |
| OS << '_' << I->second.TheDef->getName(); | |
| NumPredicates++; | |
| } | |
| } | |
| if (!NumPredicates) | |
| OS << "_None"; | |
| OS << ", // " << Inst->getName() << '\n'; | |
| } | |
| OS << " };\n\n" | |
| << " assert(Opcode < " << NumberedInstructions.size() << ");\n" | |
| << " return FeatureBitsets[RequiredFeaturesRefs[Opcode]];\n" | |
| << "}\n\n"; | |
| } // end scope for GET_COMPUTE_FEATURES | |
| { | |
| IfDefEmitter IfDef(OS, "GET_AVAILABLE_OPCODE_CHECKER"); | |
| NamespaceEmitter NS(OS, Namespace); | |
| OS << "bool isOpcodeAvailable(" | |
| << "unsigned Opcode, const FeatureBitset &Features) {\n" | |
| << " FeatureBitset AvailableFeatures = " | |
| << "computeAvailableFeatures(Features);\n" | |
| << " FeatureBitset RequiredFeatures = " | |
| << "computeRequiredFeatures(Opcode);\n" | |
| << " FeatureBitset MissingFeatures =\n" | |
| << " (AvailableFeatures & RequiredFeatures) ^\n" | |
| << " RequiredFeatures;\n" | |
| << " return !MissingFeatures.any();\n" | |
| << "}\n"; | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "ENABLE_INSTR_PREDICATE_VERIFIER"); | |
| OS << "#include <sstream>\n\n"; | |
| NamespaceEmitter NS(OS, Namespace); | |
| // Emit the name table for error messages. | |
| OS << "#ifndef NDEBUG\n"; | |
| SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, OS); | |
| OS << "#endif // NDEBUG\n\n"; | |
| // Emit the predicate verifier. | |
| OS << "void verifyInstructionPredicates(\n" | |
| << " unsigned Opcode, const FeatureBitset &Features) {\n" | |
| << "#ifndef NDEBUG\n"; | |
| OS << " FeatureBitset AvailableFeatures = " | |
| "computeAvailableFeatures(Features);\n"; | |
| OS << " FeatureBitset RequiredFeatures = " | |
| << "computeRequiredFeatures(Opcode);\n"; | |
| OS << " FeatureBitset MissingFeatures =\n" | |
| << " (AvailableFeatures & RequiredFeatures) ^\n" | |
| << " RequiredFeatures;\n" | |
| << " if (MissingFeatures.any()) {\n" | |
| << " std::ostringstream Msg;\n" | |
| << " Msg << \"Attempting to emit \" << &" << Target.getName() | |
| << "InstrNameData[" << Target.getName() << "InstrNameIndices[Opcode]]\n" | |
| << " << \" instruction but the \";\n" | |
| << " for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)\n" | |
| << " if (MissingFeatures.test(i))\n" | |
| << " Msg << SubtargetFeatureNames[i] << \" \";\n" | |
| << " Msg << \"predicate(s) are not met\";\n" | |
| << " report_fatal_error(Msg.str().c_str());\n" | |
| << " }\n" | |
| << "#endif // NDEBUG\n"; | |
| OS << "}\n"; | |
| } | |
| } | |
| void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS, | |
| StringRef TargetName, | |
| bool ExpandDefinition) { | |
| ArrayRef<const Record *> TIIPredicates = | |
| Records.getAllDerivedDefinitions("TIIPredicate"); | |
| if (TIIPredicates.empty()) | |
| return; | |
| PredicateExpander PE(TargetName); | |
| PE.setExpandForMC(false); | |
| for (const Record *Rec : TIIPredicates) { | |
| OS << (ExpandDefinition ? "" : "static ") << "bool "; | |
| if (ExpandDefinition) | |
| OS << TargetName << "InstrInfo::"; | |
| OS << Rec->getValueAsString("FunctionName"); | |
| OS << "(const MachineInstr &MI)"; | |
| if (!ExpandDefinition) { | |
| OS << ";\n"; | |
| continue; | |
| } | |
| OS << " {\n"; | |
| OS << PE.getIndent(); | |
| PE.expandStatement(OS, Rec->getValueAsDef("Body")); | |
| OS << "\n}\n\n"; | |
| } | |
| } | |
| void InstrInfoEmitter::buildTargetSpecializedPseudoInstsMap() { | |
| ArrayRef<const Record *> SpecializedInsts = Records.getAllDerivedDefinitions( | |
| "TargetSpecializedStandardPseudoInstruction"); | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| for (const Record *SpecializedRec : SpecializedInsts) { | |
| const CodeGenInstruction &SpecializedInst = | |
| Target.getInstruction(SpecializedRec); | |
| const Record *BaseInstRec = SpecializedRec->getValueAsDef("Instruction"); | |
| const CodeGenInstruction &BaseInst = Target.getInstruction(BaseInstRec); | |
| if (!TargetSpecializedPseudoInsts.insert({&BaseInst, &SpecializedInst}) | |
| .second) | |
| PrintFatalError(SpecializedRec, "multiple overrides of '" + | |
| BaseInst.getName() + "' defined"); | |
| } | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Main Output. | |
| //===----------------------------------------------------------------------===// | |
| // run - Emit the main instruction description records for the target... | |
| void InstrInfoEmitter::run(raw_ostream &OS) { | |
| TGTimer &Timer = Records.getTimer(); | |
| Timer.startTimer("Analyze DAG patterns"); | |
| emitSourceFileHeader("Target Instruction Enum Values and Descriptors", OS); | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions = | |
| Target.getInstructions(); | |
| emitEnums(OS, NumberedInstructions); | |
| StringRef TargetName = Target.getName(); | |
| const Record *InstrInfo = Target.getInstructionSet(); | |
| // Collect all of the operand info records. | |
| Timer.startTimer("Collect operand info"); | |
| buildTargetSpecializedPseudoInstsMap(); | |
| OperandInfoListTy OperandInfoList; | |
| OperandInfoMapTy OperandInfoMap; | |
| unsigned OperandInfoSize = | |
| CollectOperandInfo(OperandInfoList, OperandInfoMap); | |
| // Collect all of the instruction's implicit uses and defs. | |
| // Also collect which features are enabled by instructions to control | |
| // emission of various mappings. | |
| bool HasUseLogicalOperandMappings = false; | |
| bool HasUseNamedOperandTable = false; | |
| Timer.startTimer("Collect uses/defs"); | |
| std::map<std::vector<const Record *>, unsigned> EmittedLists; | |
| std::vector<std::vector<const Record *>> ImplicitLists; | |
| unsigned ImplicitListSize = 0; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| HasUseLogicalOperandMappings |= | |
| Inst->TheDef->getValueAsBit("UseLogicalOperandMappings"); | |
| HasUseNamedOperandTable |= | |
| Inst->TheDef->getValueAsBit("UseNamedOperandTable"); | |
| std::vector<const Record *> ImplicitOps = Inst->ImplicitUses; | |
| llvm::append_range(ImplicitOps, Inst->ImplicitDefs); | |
| if (EmittedLists.try_emplace(ImplicitOps, ImplicitListSize).second) { | |
| ImplicitLists.push_back(ImplicitOps); | |
| ImplicitListSize += ImplicitOps.size(); | |
| } | |
| } | |
| { | |
| IfGuardEmitter IfGuard( | |
| OS, | |
| "defined(GET_INSTRINFO_MC_DESC) || defined(GET_INSTRINFO_CTOR_DTOR)"); | |
| NamespaceEmitter NS(OS, "llvm"); | |
| OS << "struct " << TargetName << "InstrTable {\n"; | |
| OS << " MCInstrDesc Insts[" << NumberedInstructions.size() << "];\n"; | |
| OS << " static_assert(alignof(MCInstrDesc) >= alignof(MCPhysReg), " | |
| "\"Unwanted padding between Insts and ImplicitOps\");\n"; | |
| OS << " MCPhysReg ImplicitOps[" << std::max(ImplicitListSize, 1U) | |
| << "];\n"; | |
| // Emit enough padding to make ImplicitOps plus Padding add up to the size | |
| // of a whole number of MCOperandInfo structs. This allows us to index into | |
| // the OperandInfo array starting from the end of the Insts array, by | |
| // biasing the indices by the OpInfoBase value calculated below. | |
| OS << " char Padding[sizeof(MCOperandInfo) - sizeof ImplicitOps % " | |
| "sizeof(MCOperandInfo)];\n"; | |
| OS << " static_assert(alignof(MCInstrDesc) >= alignof(MCOperandInfo), " | |
| "\"Unwanted padding between Insts and OperandInfo\");\n"; | |
| OS << " MCOperandInfo OperandInfo[" << OperandInfoSize << "];\n"; | |
| OS << "};"; | |
| } | |
| const CodeGenRegBank &RegBank = Target.getRegBank(); | |
| const CodeGenHwModes &CGH = Target.getHwModes(); | |
| unsigned NumModes = CGH.getNumModeIds(); | |
| ArrayRef<const Record *> RegClassByHwMode = Target.getAllRegClassByHwMode(); | |
| unsigned NumClassesByHwMode = RegClassByHwMode.size(); | |
| bool HasDeprecationFeatures = | |
| llvm::any_of(NumberedInstructions, [](const CodeGenInstruction *Inst) { | |
| return !Inst->HasComplexDeprecationPredicate && | |
| !Inst->DeprecatedReason.empty(); | |
| }); | |
| bool HasComplexDeprecationInfos = | |
| llvm::any_of(NumberedInstructions, [](const CodeGenInstruction *Inst) { | |
| return Inst->HasComplexDeprecationPredicate; | |
| }); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_MC_DESC"); | |
| NamespaceEmitter LlvmNS(OS, "llvm"); | |
| // Emit all of the MCInstrDesc records in reverse ENUM ordering. | |
| Timer.startTimer("Emit InstrDesc records"); | |
| OS << "static_assert((sizeof " << TargetName | |
| << "InstrTable::ImplicitOps + sizeof " << TargetName | |
| << "InstrTable::Padding) % sizeof(MCOperandInfo) == 0);\n"; | |
| OS << "static constexpr unsigned " << TargetName << "OpInfoBase = (sizeof " | |
| << TargetName << "InstrTable::ImplicitOps + sizeof " << TargetName | |
| << "InstrTable::Padding) / sizeof(MCOperandInfo);\n\n"; | |
| OS << "extern const " << TargetName << "InstrTable " << TargetName | |
| << "Descs = {\n {\n"; | |
| SequenceToOffsetTable<StringRef> InstrNames; | |
| unsigned Num = NumberedInstructions.size(); | |
| for (const CodeGenInstruction *Inst : reverse(NumberedInstructions)) { | |
| // Keep a list of the instruction names. | |
| InstrNames.add(Inst->getName()); | |
| auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst); | |
| if (OverrideEntry != TargetSpecializedPseudoInsts.end()) | |
| Inst = OverrideEntry->second; | |
| // Emit the record into the table. | |
| emitRecord(*Inst, --Num, InstrInfo, EmittedLists, OperandInfoMap, OS); | |
| } | |
| OS << " }, {\n"; | |
| // Emit all of the instruction's implicit uses and defs. | |
| Timer.startTimer("Emit uses/defs"); | |
| for (auto &List : ImplicitLists) { | |
| OS << " /* " << EmittedLists[List] << " */"; | |
| for (auto &Reg : List) | |
| OS << ' ' << getQualifiedName(Reg) << ','; | |
| OS << '\n'; | |
| } | |
| OS << " }, {\n"; | |
| // Emit the padding. | |
| OS << " 0\n"; | |
| OS << " }, {\n"; | |
| // Emit all of the operand info records. | |
| Timer.startTimer("Emit operand info"); | |
| EmitOperandInfo(OS, OperandInfoList); | |
| OS << " }\n};\n\n"; | |
| // Emit the array of instruction names. | |
| Timer.startTimer("Emit instruction names"); | |
| InstrNames.layout(); | |
| InstrNames.emitStringLiteralDef(OS, Twine("extern const char ") + | |
| TargetName + "InstrNameData[]"); | |
| OS << "extern const unsigned " << TargetName << "InstrNameIndices[] = {"; | |
| Num = 0; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| // Newline every eight entries. | |
| if (Num % 8 == 0) | |
| OS << "\n "; | |
| OS << InstrNames.get(Inst->getName()) << "U, "; | |
| ++Num; | |
| } | |
| OS << "\n};\n\n"; | |
| if (HasDeprecationFeatures) { | |
| OS << "extern const uint8_t " << TargetName | |
| << "InstrDeprecationFeatures[] = {"; | |
| Num = 0; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| if (Num % 8 == 0) | |
| OS << "\n "; | |
| if (!Inst->HasComplexDeprecationPredicate && | |
| !Inst->DeprecatedReason.empty()) | |
| OS << Target.getInstNamespace() << "::" << Inst->DeprecatedReason | |
| << ", "; | |
| else | |
| OS << "uint8_t(-1), "; | |
| ++Num; | |
| } | |
| OS << "\n};\n\n"; | |
| } | |
| if (HasComplexDeprecationInfos) { | |
| OS << "bool " << TargetName << "InstrComplexDeprecationInfo(" | |
| << "MCInst &Inst, const MCSubtargetInfo &STI, std::string &Info) {\n" | |
| << " switch (Inst.getOpcode()) {\n"; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| if (!Inst->HasComplexDeprecationPredicate) | |
| continue; | |
| OS << " case " << getQualifiedName(Inst->TheDef) << ": return get" | |
| << Inst->DeprecatedReason << "DeprecationInfo(Inst, STI, Info);\n"; | |
| } | |
| OS << " }\n return false;\n}\n\n"; | |
| } | |
| // MCInstrInfo initialization routine. | |
| Timer.startTimer("Emit initialization routine"); | |
| if (NumClassesByHwMode != 0) { | |
| OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables[" | |
| << NumModes << "][" << NumClassesByHwMode << "] = {\n"; | |
| for (unsigned M = 0; M < NumModes; ++M) { | |
| OS << " { // " << CGH.getModeName(M, /*IncludeDefault=*/true) << '\n'; | |
| for (unsigned I = 0; I != NumClassesByHwMode; ++I) { | |
| const Record *Class = RegClassByHwMode[I]; | |
| const HwModeSelect &ModeSelect = CGH.getHwModeSelect(Class); | |
| auto FoundMode = | |
| find_if(ModeSelect.Items, [=](const HwModeSelect::PairType P) { | |
| return P.first == M; | |
| }); | |
| if (FoundMode == ModeSelect.Items.end()) { | |
| // If a RegClassByHwMode doesn't have an entry corresponding to a | |
| // mode, pad with default register class. | |
| OS << indent(4) << "-1, // Missing mode entry for " | |
| << Class->getName() << "\n"; | |
| } else { | |
| const CodeGenRegisterClass *RegClass = | |
| RegBank.getRegClass(FoundMode->second); | |
| OS << indent(4) << RegClass->getQualifiedIdName() << ", // " | |
| << Class->getName() << "\n"; | |
| } | |
| } | |
| OS << " },\n"; | |
| } | |
| OS << "};\n\n"; | |
| } | |
| OS << "static inline void Init" << TargetName | |
| << "MCInstrInfo(MCInstrInfo *II) {\n"; | |
| OS << " II->InitMCInstrInfo(" << TargetName << "Descs.Insts, " | |
| << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, "; | |
| if (HasDeprecationFeatures) | |
| OS << TargetName << "InstrDeprecationFeatures, "; | |
| else | |
| OS << "nullptr, "; | |
| if (HasComplexDeprecationInfos) | |
| OS << TargetName << "InstrComplexDeprecationInfo, "; | |
| else | |
| OS << "nullptr, "; | |
| OS << NumberedInstructions.size() << ", "; | |
| if (NumClassesByHwMode != 0) { | |
| OS << '&' << TargetName << "RegClassByHwModeTables[0][0], " | |
| << NumClassesByHwMode; | |
| } else | |
| OS << "nullptr, 0"; | |
| OS << ");\n}\n\n"; | |
| } // end GET_INSTRINFO_MC_DESC scope. | |
| { | |
| // Create a TargetInstrInfo subclass to hide the MC layer initialization. | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_HEADER"); | |
| { | |
| NamespaceEmitter LlvmNS(OS, "llvm"); | |
| Twine ClassName = TargetName + "GenInstrInfo"; | |
| OS << "struct " << ClassName << " : public TargetInstrInfo {\n" | |
| << " explicit " << ClassName | |
| << "(const TargetSubtargetInfo &STI, const TargetRegisterInfo &TRI, " | |
| "unsigned CFSetupOpcode = ~0u, " | |
| "unsigned CFDestroyOpcode = ~0u, " | |
| "unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u);\n" | |
| << " ~" << ClassName << "() override = default;\n" | |
| << "};\n"; | |
| // Declare RegClassByHwModeTables, so that other files can use this | |
| // without having to indirect via MCInstInfo. | |
| if (NumClassesByHwMode != 0) { | |
| OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables[" | |
| << NumModes << "][" << NumClassesByHwMode << "];\n"; | |
| } | |
| } // end llvm namespace. | |
| OS << "\n"; | |
| NamespaceEmitter InstNS(OS, ("llvm::" + Target.getInstNamespace()).str()); | |
| for (const Record *R : Records.getAllDerivedDefinitions("Operand")) { | |
| if (R->isAnonymous()) | |
| continue; | |
| const DagInit *D = R->getValueAsDag("MIOperandInfo"); | |
| if (!D) | |
| continue; | |
| for (const auto &[Idx, Name] : enumerate(D->getArgNames())) { | |
| if (Name) | |
| OS << "constexpr unsigned SUBOP_" << R->getName() << "_" | |
| << Name->getValue() << " = " << Idx << ";\n"; | |
| } | |
| } | |
| } // end GET_INSTRINFO_HEADER scope. | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_HELPER_DECLS"); | |
| emitTIIHelperMethods(OS, TargetName, /* ExpandDefinition = */ false); | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_HELPERS"); | |
| emitTIIHelperMethods(OS, TargetName, /* ExpandDefinition = */ true); | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_CTOR_DTOR"); | |
| NamespaceEmitter LlvmNS(OS, "llvm"); | |
| OS << "extern const " << TargetName << "InstrTable " << TargetName | |
| << "Descs;\n"; | |
| OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; | |
| OS << "extern const char " << TargetName << "InstrNameData[];\n"; | |
| if (HasDeprecationFeatures) | |
| OS << "extern const uint8_t " << TargetName | |
| << "InstrDeprecationFeatures[];\n"; | |
| if (HasComplexDeprecationInfos) | |
| OS << "bool " << TargetName << "InstrComplexDeprecationInfo(" | |
| << "MCInst &Inst, const MCSubtargetInfo &STI, std::string &Info);\n"; | |
| Twine ClassName = TargetName + "GenInstrInfo"; | |
| OS << ClassName << "::" << ClassName | |
| << "(const TargetSubtargetInfo &STI, const TargetRegisterInfo &TRI, " | |
| "unsigned CFSetupOpcode, unsigned " | |
| "CFDestroyOpcode, unsigned CatchRetOpcode, unsigned ReturnOpcode)\n" | |
| << " : TargetInstrInfo(TRI, CFSetupOpcode, CFDestroyOpcode, " | |
| "CatchRetOpcode, " | |
| "ReturnOpcode"; | |
| if (NumClassesByHwMode != 0) | |
| OS << ", " << TargetName | |
| << "RegClassByHwModeTables[STI.getHwMode(MCSubtargetInfo::HwMode_" | |
| "RegInfo)]"; | |
| OS << ") {\n" | |
| << " InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName | |
| << "InstrNameIndices, " << TargetName << "InstrNameData, "; | |
| if (HasDeprecationFeatures) | |
| OS << TargetName << "InstrDeprecationFeatures, "; | |
| else | |
| OS << "nullptr, "; | |
| if (HasComplexDeprecationInfos) | |
| OS << TargetName << "InstrComplexDeprecationInfo, "; | |
| else | |
| OS << "nullptr, "; | |
| OS << NumberedInstructions.size(); | |
| if (NumClassesByHwMode != 0) { | |
| OS << ", &" << TargetName << "RegClassByHwModeTables[0][0], " | |
| << NumClassesByHwMode; | |
| } | |
| OS << ");\n" | |
| "}\n"; | |
| } // end GET_INSTRINFO_CTOR_DTOR scope. | |
| ArrayRef<const CodeGenInstruction *> TargetInstructions = | |
| Target.getTargetInstructions(); | |
| if (HasUseNamedOperandTable) { | |
| Timer.startTimer("Emit operand name mappings"); | |
| emitOperandNameMappings(OS, Target, TargetInstructions); | |
| } | |
| Timer.startTimer("Emit operand type mappings"); | |
| emitOperandTypeMappings(OS, Target, NumberedInstructions); | |
| if (HasUseLogicalOperandMappings) { | |
| Timer.startTimer("Emit logical operand size mappings"); | |
| emitLogicalOperandSizeMappings(OS, TargetName, TargetInstructions); | |
| } | |
| Timer.startTimer("Emit helper methods"); | |
| emitMCIIHelperMethods(OS, TargetName); | |
| Timer.startTimer("Emit verifier methods"); | |
| emitFeatureVerifier(OS, Target); | |
| Timer.startTimer("Emit map table"); | |
| EmitMapTable(Records, OS); | |
| } | |
| void InstrInfoEmitter::emitRecord( | |
| const CodeGenInstruction &Inst, unsigned Num, const Record *InstrInfo, | |
| std::map<std::vector<const Record *>, unsigned> &EmittedLists, | |
| const OperandInfoMapTy &OperandInfoMap, raw_ostream &OS) { | |
| int MinOperands = 0; | |
| if (!Inst.Operands.empty()) | |
| // Each logical operand can be multiple MI operands. | |
| MinOperands = | |
| Inst.Operands.back().MIOperandNo + Inst.Operands.back().MINumOperands; | |
| // Even the logical output operand may be multiple MI operands. | |
| int DefOperands = 0; | |
| if (Inst.Operands.NumDefs) { | |
| auto &Opnd = Inst.Operands[Inst.Operands.NumDefs - 1]; | |
| DefOperands = Opnd.MIOperandNo + Opnd.MINumOperands; | |
| } | |
| OS << " { "; | |
| OS << Num << ",\t" << MinOperands << ",\t" << DefOperands << ",\t" | |
| << Inst.TheDef->getValueAsInt("Size") << ",\t" | |
| << SchedModels.getSchedClassIdx(Inst) << ",\t"; | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| // Emit the implicit use/def list... | |
| OS << Inst.ImplicitUses.size() << ",\t" << Inst.ImplicitDefs.size() << ",\t"; | |
| std::vector<const Record *> ImplicitOps = Inst.ImplicitUses; | |
| llvm::append_range(ImplicitOps, Inst.ImplicitDefs); | |
| // Emit the operand info offset. | |
| OperandInfoTy OperandInfo = GetOperandInfo(Inst); | |
| OS << Target.getName() << "OpInfoBase + " | |
| << OperandInfoMap.find(OperandInfo)->second << ",\t"; | |
| // Emit implicit operand base. | |
| OS << EmittedLists[ImplicitOps] << ",\t0"; | |
| // Emit all of the target independent flags... | |
| if (Inst.isPreISelOpcode) | |
| OS << "|(1ULL<<MCID::PreISelOpcode)"; | |
| if (Inst.isPseudo) | |
| OS << "|(1ULL<<MCID::Pseudo)"; | |
| if (Inst.isMeta) | |
| OS << "|(1ULL<<MCID::Meta)"; | |
| if (Inst.isReturn) | |
| OS << "|(1ULL<<MCID::Return)"; | |
| if (Inst.isEHScopeReturn) | |
| OS << "|(1ULL<<MCID::EHScopeReturn)"; | |
| if (Inst.isBranch) | |
| OS << "|(1ULL<<MCID::Branch)"; | |
| if (Inst.isIndirectBranch) | |
| OS << "|(1ULL<<MCID::IndirectBranch)"; | |
| if (Inst.isCompare) | |
| OS << "|(1ULL<<MCID::Compare)"; | |
| if (Inst.isMoveImm) | |
| OS << "|(1ULL<<MCID::MoveImm)"; | |
| if (Inst.isMoveReg) | |
| OS << "|(1ULL<<MCID::MoveReg)"; | |
| if (Inst.isBitcast) | |
| OS << "|(1ULL<<MCID::Bitcast)"; | |
| if (Inst.isAdd) | |
| OS << "|(1ULL<<MCID::Add)"; | |
| if (Inst.isTrap) | |
| OS << "|(1ULL<<MCID::Trap)"; | |
| if (Inst.isSelect) | |
| OS << "|(1ULL<<MCID::Select)"; | |
| if (Inst.isBarrier) | |
| OS << "|(1ULL<<MCID::Barrier)"; | |
| if (Inst.hasDelaySlot) | |
| OS << "|(1ULL<<MCID::DelaySlot)"; | |
| if (Inst.isCall) | |
| OS << "|(1ULL<<MCID::Call)"; | |
| if (Inst.canFoldAsLoad) | |
| OS << "|(1ULL<<MCID::FoldableAsLoad)"; | |
| if (Inst.mayLoad) | |
| OS << "|(1ULL<<MCID::MayLoad)"; | |
| if (Inst.mayStore) | |
| OS << "|(1ULL<<MCID::MayStore)"; | |
| if (Inst.mayRaiseFPException) | |
| OS << "|(1ULL<<MCID::MayRaiseFPException)"; | |
| if (Inst.isPredicable) | |
| OS << "|(1ULL<<MCID::Predicable)"; | |
| if (Inst.isConvertibleToThreeAddress) | |
| OS << "|(1ULL<<MCID::ConvertibleTo3Addr)"; | |
| if (Inst.isCommutable) | |
| OS << "|(1ULL<<MCID::Commutable)"; | |
| if (Inst.isTerminator) | |
| OS << "|(1ULL<<MCID::Terminator)"; | |
| if (Inst.isReMaterializable) | |
| OS << "|(1ULL<<MCID::Rematerializable)"; | |
| if (Inst.isNotDuplicable) | |
| OS << "|(1ULL<<MCID::NotDuplicable)"; | |
| if (Inst.Operands.hasOptionalDef) | |
| OS << "|(1ULL<<MCID::HasOptionalDef)"; | |
| if (Inst.usesCustomInserter) | |
| OS << "|(1ULL<<MCID::UsesCustomInserter)"; | |
| if (Inst.hasPostISelHook) | |
| OS << "|(1ULL<<MCID::HasPostISelHook)"; | |
| if (Inst.Operands.isVariadic) | |
| OS << "|(1ULL<<MCID::Variadic)"; | |
| if (Inst.hasSideEffects) | |
| OS << "|(1ULL<<MCID::UnmodeledSideEffects)"; | |
| if (Inst.isAsCheapAsAMove) | |
| OS << "|(1ULL<<MCID::CheapAsAMove)"; | |
| if (!Target.getAllowRegisterRenaming() || Inst.hasExtraSrcRegAllocReq) | |
| OS << "|(1ULL<<MCID::ExtraSrcRegAllocReq)"; | |
| if (!Target.getAllowRegisterRenaming() || Inst.hasExtraDefRegAllocReq) | |
| OS << "|(1ULL<<MCID::ExtraDefRegAllocReq)"; | |
| if (Inst.isRegSequence) | |
| OS << "|(1ULL<<MCID::RegSequence)"; | |
| if (Inst.isExtractSubreg) | |
| OS << "|(1ULL<<MCID::ExtractSubreg)"; | |
| if (Inst.isInsertSubreg) | |
| OS << "|(1ULL<<MCID::InsertSubreg)"; | |
| if (Inst.isConvergent) | |
| OS << "|(1ULL<<MCID::Convergent)"; | |
| if (Inst.variadicOpsAreDefs) | |
| OS << "|(1ULL<<MCID::VariadicOpsAreDefs)"; | |
| if (Inst.isAuthenticated) | |
| OS << "|(1ULL<<MCID::Authenticated)"; | |
| // Emit all of the target-specific flags... | |
| const BitsInit *TSF = Inst.TheDef->getValueAsBitsInit("TSFlags"); | |
| if (!TSF) | |
| PrintFatalError(Inst.TheDef->getLoc(), "no TSFlags?"); | |
| std::optional<uint64_t> Value = TSF->convertInitializerToInt(); | |
| if (!Value) | |
| PrintFatalError(Inst.TheDef, "Invalid TSFlags bit in " + Inst.getName()); | |
| OS << ", 0x"; | |
| OS.write_hex(*Value); | |
| OS << "ULL"; | |
| OS << " }, // " << Inst.getName() << '\n'; | |
| } | |
| // emitEnums - Print out enum values for all of the instructions. | |
| void InstrInfoEmitter::emitEnums( | |
| raw_ostream &OS, | |
| ArrayRef<const CodeGenInstruction *> NumberedInstructions) { | |
| const CodeGenTarget &Target = CDP.getTargetInfo(); | |
| StringRef Namespace = Target.getInstNamespace(); | |
| if (Namespace.empty()) | |
| PrintFatalError("No instructions defined!"); | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_ENUM"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace).str()); | |
| auto II = llvm::max_element( | |
| NumberedInstructions, | |
| [](const CodeGenInstruction *InstA, const CodeGenInstruction *InstB) { | |
| return InstA->getName().size() < InstB->getName().size(); | |
| }); | |
| size_t MaxNameSize = (*II)->getName().size(); | |
| OS << " enum {\n"; | |
| for (const CodeGenInstruction *Inst : NumberedInstructions) { | |
| OS << " " << left_justify(Inst->getName(), MaxNameSize) << " = " | |
| << Target.getInstrIntValue(Inst->TheDef) << ", // " | |
| << SrcMgr.getFormattedLocationNoOffset(Inst->TheDef->getLoc().front()) | |
| << '\n'; | |
| } | |
| OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << '\n'; | |
| OS << " };\n"; | |
| ArrayRef<const Record *> RegClassesByHwMode = | |
| Target.getAllRegClassByHwMode(); | |
| if (!RegClassesByHwMode.empty()) { | |
| OS << " enum RegClassByHwModeUses : uint16_t {\n"; | |
| for (const Record *ClassByHwMode : RegClassesByHwMode) | |
| OS << indent(4) << ClassByHwMode->getName() << ",\n"; | |
| OS << " };\n"; | |
| } | |
| } | |
| { | |
| IfDefEmitter IfDef(OS, "GET_INSTRINFO_SCHED_ENUM"); | |
| NamespaceEmitter NS(OS, ("llvm::" + Namespace + "::Sched").str()); | |
| OS << " enum {\n"; | |
| auto ExplictClasses = SchedModels.explicitSchedClasses(); | |
| for (const auto &[Idx, Class] : enumerate(ExplictClasses)) | |
| OS << " " << Class.Name << "\t= " << Idx << ",\n"; | |
| OS << " SCHED_LIST_END = " << ExplictClasses.size() << '\n'; | |
| OS << " };\n"; | |
| } | |
| } | |
| static TableGen::Emitter::OptClass<InstrInfoEmitter> | |
| X("gen-instr-info", "Generate instruction descriptions"); |