Index: include/llvm/MC/MCSchedule.h =================================================================== --- include/llvm/MC/MCSchedule.h +++ include/llvm/MC/MCSchedule.h @@ -129,6 +129,15 @@ bool isVariant() const { return NumMicroOps == VariantNumMicroOps; } + bool operator==(const MCSchedClassDesc &RHS) const { + return NumMicroOps == RHS.NumMicroOps && BeginGroup == RHS.BeginGroup && + EndGroup == RHS.EndGroup && WriteProcResIdx == RHS.WriteProcResIdx && + NumWriteProcResEntries == RHS.NumWriteProcResEntries && + WriteLatencyIdx == RHS.WriteLatencyIdx && + NumWriteLatencyEntries == RHS.NumWriteLatencyEntries && + ReadAdvanceIdx == RHS.ReadAdvanceIdx && + NumReadAdvanceEntries == RHS.NumReadAdvanceEntries; + } }; /// Specify the cost of a register definition in terms of number of physical Index: utils/TableGen/CodeGenSchedule.h =================================================================== --- utils/TableGen/CodeGenSchedule.h +++ utils/TableGen/CodeGenSchedule.h @@ -300,7 +300,8 @@ // Map each instruction to its unique SchedClass index considering the // combination of it's itinerary class, SchedRW list, and InstRW records. - using InstClassMapTy = DenseMap; + // Stores both the original SchedRW class index, and the InstRW override. + using InstClassMapTy = DenseMap>; InstClassMapTy InstrClassMap; public: @@ -408,6 +409,12 @@ // for NoItinerary. unsigned getSchedClassIdx(const CodeGenInstruction &Inst) const; + // Get the original and InstRW SchedClass indices for an instruction. + // Instructions with no itinerary, no SchedReadWrites, and no InstrReadWrites + // references return 0 for NoItinerary. + std::pair + getSchedClassIdxPair(const CodeGenInstruction &Inst) const; + using SchedClassIter = std::vector::const_iterator; SchedClassIter schedClassBegin() const { return SchedClasses.begin(); } SchedClassIter schedClassEnd() const { return SchedClasses.end(); } Index: utils/TableGen/CodeGenSchedule.cpp =================================================================== --- utils/TableGen/CodeGenSchedule.cpp +++ utils/TableGen/CodeGenSchedule.cpp @@ -615,7 +615,7 @@ // ProcIdx == 0 indicates the class applies to all processors. unsigned SCIdx = addSchedClass(ItinDef, Writes, Reads, /*ProcIndices*/{0}); - InstrClassMap[Inst->TheDef] = SCIdx; + InstrClassMap[Inst->TheDef] = std::make_pair(SCIdx, SCIdx); } // Create classes for InstRW defs. RecVec InstRWDefs = Records.getAllDerivedDefinitions("InstRW"); @@ -702,6 +702,11 @@ // Get the SchedClass index for an instruction. unsigned CodeGenSchedModels::getSchedClassIdx(const CodeGenInstruction &Inst) const { + return InstrClassMap.lookup(Inst.TheDef).second; +} + +std::pair +CodeGenSchedModels::getSchedClassIdxPair(const CodeGenInstruction &Inst) const { return InstrClassMap.lookup(Inst.TheDef); } @@ -790,7 +795,7 @@ InstClassMapTy::const_iterator Pos = InstrClassMap.find(InstDef); if (Pos == InstrClassMap.end()) PrintFatalError(InstDef->getLoc(), "No sched class for instruction."); - unsigned SCIdx = Pos->second; + unsigned SCIdx = Pos->second.second; ClassInstrs[SCIdx].push_back(InstDef); } // For each set of Instrs, create a new class if necessary, and map or remap @@ -804,10 +809,9 @@ const RecVec &RWDefs = SchedClasses[OldSCIdx].InstRWs; if (!RWDefs.empty()) { const RecVec *OrigInstDefs = Sets.expand(RWDefs[0]); - unsigned OrigNumInstrs = - count_if(*OrigInstDefs, [&](Record *OIDef) { - return InstrClassMap[OIDef] == OldSCIdx; - }); + unsigned OrigNumInstrs = count_if(*OrigInstDefs, [&](Record *OIDef) { + return InstrClassMap[OIDef].second == OldSCIdx; + }); if (OrigNumInstrs == InstDefs.size()) { assert(SchedClasses[OldSCIdx].ProcIndices[0] == 0 && "expected a generic SchedClass"); @@ -862,7 +866,7 @@ } // Map each Instr to this new class. for (Record *InstDef : InstDefs) - InstrClassMap[InstDef] = SCIdx; + InstrClassMap[InstDef].second = SCIdx; SC.InstRWs.push_back(InstRWDef); } } @@ -1001,7 +1005,7 @@ const RecVec *InstDefs = Sets.expand(Rec); RecIter II = InstDefs->begin(), IE = InstDefs->end(); for (; II != IE; ++II) { - if (InstrClassMap[*II] == SCIdx) + if (InstrClassMap[*II].second == SCIdx) break; } // If this class no longer has any instructions mapped to it, it has become Index: utils/TableGen/SubtargetEmitter.cpp =================================================================== --- utils/TableGen/SubtargetEmitter.cpp +++ utils/TableGen/SubtargetEmitter.cpp @@ -11,17 +11,20 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenTarget.h" #include "CodeGenSchedule.h" +#include "CodeGenTarget.h" #include "PredicateExpander.h" -#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInstrItineraries.h" #include "llvm/MC/MCSchedule.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" @@ -39,6 +42,16 @@ #define DEBUG_TYPE "subtarget-emitter" +#ifdef EXPENSIVE_CHECKS +// FIXME: TableGen is failed iff EXPENSIVE_CHECKS defined +static constexpr bool OptCheckSchedClassTable = true; +#else +// FIXME: the default value should be false +static cl::opt OptCheckSchedClassTable( + "check-sched-class-table", cl::init(true), cl::Hidden, + cl::desc("Check sched class table on different types of inconsistencies")); +#endif + namespace { class SubtargetEmitter { @@ -109,6 +122,7 @@ const CodeGenProcModel &ProcModel); void GenSchedClassTables(const CodeGenProcModel &ProcModel, SchedClassTables &SchedTables); + void CheckSchedClassTables(SchedClassTables &SchedTables); void EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS); void EmitProcessorModels(raw_ostream &OS); void EmitProcessorLookup(raw_ostream &OS); @@ -1232,6 +1246,146 @@ } } +// Check SchedClass tables for unnecessary duplication. +void SubtargetEmitter::CheckSchedClassTables(SchedClassTables &SchedTables) { + if (!OptCheckSchedClassTable) + return; + ArrayRef SchedClasses = SchedModels.schedClasses(); + unsigned NumInstrSchedClasses = SchedModels.numInstrSchedClasses(); + unsigned NumProcModels = SchedModels.procModels().size(); + + std::string str; + raw_string_ostream OS(str); + + // Tag which classes are overriden. + SmallVector OverriddenSC( + NumProcModels, APInt::getAllOnesValue(NumInstrSchedClasses)); + + // Check if the 2 MCSchedClassDesc are either identical or only differ + // by ReadAdvanceEntries (x86 overrides are often missing these). + auto IsEquivalent = [&SchedTables](const CodeGenProcModel &PM, + const MCSchedClassDesc &Orig, + const MCSchedClassDesc &Inst) { + if (Orig == Inst) + return true; + if (Orig.NumReadAdvanceEntries && !Inst.NumReadAdvanceEntries) { + return Orig.NumMicroOps == Inst.NumMicroOps && + Orig.BeginGroup == Inst.BeginGroup && + Orig.EndGroup == Inst.EndGroup && + Orig.WriteProcResIdx == Inst.WriteProcResIdx && + Orig.NumWriteProcResEntries == Inst.NumWriteProcResEntries && + Orig.WriteLatencyIdx == Inst.WriteLatencyIdx && + Orig.NumWriteLatencyEntries == Inst.NumWriteLatencyEntries; + } + return false; + }; + + // Check each instruction for each model to see if its overridden but has + // the same schedule as its default class. + for (const CodeGenInstruction *Inst : TGT.getInstructionsByEnumValue()) { + const std::pair ScIdx = + SchedModels.getSchedClassIdxPair(*Inst); + if (ScIdx.first == 0) + continue; + + const CodeGenSchedClass &CgOrig = SchedClasses[ScIdx.first]; + const CodeGenSchedClass &CgInst = SchedClasses[ScIdx.second]; + + for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), + PE = SchedModels.procModelEnd(); + PI != PE; ++PI) { + unsigned ProcIdx = PI - SchedModels.procModelBegin(); + if (!PI->hasInstrSchedModel()) + continue; + + std::vector &SCTab = + SchedTables.ProcSchedClasses[1 + ProcIdx]; + if (SCTab.size() != NumInstrSchedClasses) + continue; + + if (ScIdx.first == ScIdx.second) { + OverriddenSC[ProcIdx].clearBit(ScIdx.first); + continue; + } + + const MCSchedClassDesc &ScOrig = SCTab[ScIdx.first]; + const MCSchedClassDesc &ScInst = SCTab[ScIdx.second]; + if (IsEquivalent(*PI, ScOrig, ScInst)) { + OverriddenSC[ProcIdx].clearBit(ScIdx.first); + + for (const Record *InstrRW : CgInst.InstRWs) { + const Record *InstModelDef = InstrRW->getValueAsDef("SchedModel"); + if (InstModelDef->getName() == PI->ModelName) { + OS << "Model '" << PI->ModelName + << "' unnecessarily overrides instruction '" + << Inst->TheDef->getName() << "' from '" << CgOrig.Name; + PrintWarning(OS.str()); + str.clear(); + } + } + } + } + } + // Check if an instruction is always overriden (candidate for a new class?). + for (unsigned ScIdx = 1; ScIdx < NumInstrSchedClasses; ++ScIdx) { + const CodeGenSchedClass &CgSc = SchedClasses[ScIdx]; + for (const CodeGenInstruction *Inst : TGT.getInstructionsByEnumValue()) { + const std::pair ScPair = + SchedModels.getSchedClassIdxPair(*Inst); + if (ScPair.first == ScIdx && ScPair.second != ScIdx) { + for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), + PE = SchedModels.procModelEnd(); + PI != PE; ++PI) { + unsigned ProcIdx = PI - SchedModels.procModelBegin(); + if (!PI->hasInstrSchedModel()) + continue; + + std::vector &SCTab = + SchedTables.ProcSchedClasses[1 + ProcIdx]; + if (SCTab.size() != NumInstrSchedClasses) + continue; + + const MCSchedClassDesc &ScOrig = SCTab[ScPair.first]; + const MCSchedClassDesc &ScInst = SCTab[ScPair.second]; + if (!IsEquivalent(*PI, ScOrig, ScInst)) { + OS << "Class '" << CgSc.Name << "' never uses instruction '" + << Inst->TheDef->getName() << "' on model: '" << PI->ModelName; + PrintWarning(OS.str()); + str.clear(); + } + } + } + } + } + // Check if a model never uses the default class - overriding all + // instructions. + for (unsigned ScIdx = 1; ScIdx < NumInstrSchedClasses; ++ScIdx) { + const CodeGenSchedClass &CgSc = SchedClasses[ScIdx]; + if (!StringRef(CgSc.Name).startswith("Write")) + continue; + + for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), + PE = SchedModels.procModelEnd(); + PI != PE; ++PI) { + unsigned ProcIdx = PI - SchedModels.procModelBegin(); + if (!PI->hasInstrSchedModel()) + continue; + if (OverriddenSC[ProcIdx][ScIdx]) { + OS << "Model '" << PI->ModelName << "' completely overrides class '" + << CgSc.Name << "' for instrs:"; + PrintWarning(OS.str()); + str.clear(); + for (const CodeGenInstruction *Inst : + TGT.getInstructionsByEnumValue()) { + if (SchedModels.getSchedClassIdxPair(*Inst).first == ScIdx) { + PrintWarning({"\t", Inst->TheDef->getName()}); + } + } + } + } + } +} + // Emit SchedClass tables for all processors and associated global tables. void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS) { @@ -1439,6 +1593,7 @@ for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { GenSchedClassTables(ProcModel, SchedTables); } + CheckSchedClassTables(SchedTables); EmitSchedClassTables(SchedTables, OS); // Emit the processor machine model