Difference from the previous version is that this one doesn't actually encode opcodes in matcher tables as 32 bits, but still as 16 bits.
1474 lines
54 KiB
C++
1474 lines
54 KiB
C++
//===- 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), 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")) {
|
|
Res += Namespace;
|
|
Res += "::";
|
|
Res += OpR->getName();
|
|
Res += ", ";
|
|
} else if (OpR->isSubClassOf("RegisterClass"))
|
|
Res += getQualifiedName(OpR) + "RegClassID, ";
|
|
else if (OpR->isSubClassOf("PointerLikeRegClass")) {
|
|
if (Inst.isPseudo) {
|
|
// TODO: Verify this is a fixed pseudo
|
|
PrintError(Inst.TheDef,
|
|
"missing target override for pseudoinstruction "
|
|
"using PointerLikeRegClass");
|
|
PrintNote(OpR->getLoc(),
|
|
"target should define equivalent instruction "
|
|
"with RegisterClassLike replacement; (use "
|
|
"RemapAllTargetPseudoPointerOperands?)");
|
|
} else {
|
|
PrintError(Inst.TheDef,
|
|
"non-pseudoinstruction user of PointerLikeRegClass");
|
|
}
|
|
} 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";
|
|
|
|
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 << "extern const MCInstrInfo::ComplexDeprecationPredicate "
|
|
<< TargetName << "InstrComplexDeprecationInfos[] = {";
|
|
Num = 0;
|
|
for (const CodeGenInstruction *Inst : NumberedInstructions) {
|
|
if (Num % 8 == 0)
|
|
OS << "\n ";
|
|
if (Inst->HasComplexDeprecationPredicate)
|
|
// Emit a function pointer to the complex predicate method.
|
|
OS << "&get" << Inst->DeprecatedReason << "DeprecationInfo, ";
|
|
else
|
|
OS << "nullptr, ";
|
|
++Num;
|
|
}
|
|
OS << "\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 << "InstrComplexDeprecationInfos, ";
|
|
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 << "extern const MCInstrInfo::ComplexDeprecationPredicate "
|
|
<< TargetName << "InstrComplexDeprecationInfos[];\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 << "InstrComplexDeprecationInfos, ";
|
|
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");
|