diff --git a/llvm/include/llvm/MC/MCSchedule.h b/llvm/include/llvm/MC/MCSchedule.h --- a/llvm/include/llvm/MC/MCSchedule.h +++ b/llvm/include/llvm/MC/MCSchedule.h @@ -130,6 +130,23 @@ bool isVariant() const { return NumMicroOps == VariantNumMicroOps; } + bool equals(const MCSchedClassDesc &RHS, + bool IgnoreRHSReadAdvance = false) const { + // If IgnoreRHSReadAdvance is set and this contains ReadAdvanceTable + // entries, then ignore any mismatch with RHS (override class) as overrides + // are often missing them. + return NumMicroOps == RHS.NumMicroOps && + BeginGroup == RHS.BeginGroup && + EndGroup == RHS.EndGroup && + RetireOOO == RHS.RetireOOO && + WriteProcResIdx == RHS.WriteProcResIdx && + NumWriteProcResEntries == RHS.NumWriteProcResEntries && + WriteLatencyIdx == RHS.WriteLatencyIdx && + NumWriteLatencyEntries == RHS.NumWriteLatencyEntries && + (IgnoreRHSReadAdvance || NumReadAdvanceEntries == 0 || + (ReadAdvanceIdx == RHS.ReadAdvanceIdx && + NumReadAdvanceEntries == RHS.NumReadAdvanceEntries)); + } }; /// Specify the cost of a register definition in terms of number of physical diff --git a/llvm/utils/TableGen/CodeGenSchedule.h b/llvm/utils/TableGen/CodeGenSchedule.h --- a/llvm/utils/TableGen/CodeGenSchedule.h +++ b/llvm/utils/TableGen/CodeGenSchedule.h @@ -437,7 +437,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; std::vector STIPredicates; @@ -548,6 +549,12 @@ // for NoItinerary. unsigned getSchedClassIdx(const CodeGenInstruction &Inst) const; + // Get the SchedClass SchedRW/InstRW indices for an instruction. Instructions + // with no itinerary, no SchedReadWrites, and no InstrReadWrites references + // return None for NoItinerary. + llvm::Optional> + getSchedClassIdxPair(const CodeGenInstruction &Inst) const; + using SchedClassIter = std::vector::const_iterator; SchedClassIter schedClassBegin() const { return SchedClasses.begin(); } SchedClassIter schedClassEnd() const { return SchedClasses.end(); } diff --git a/llvm/utils/TableGen/CodeGenSchedule.cpp b/llvm/utils/TableGen/CodeGenSchedule.cpp --- a/llvm/utils/TableGen/CodeGenSchedule.cpp +++ b/llvm/utils/TableGen/CodeGenSchedule.cpp @@ -883,7 +883,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"); @@ -969,7 +969,18 @@ // Get the SchedClass index for an instruction. unsigned CodeGenSchedModels::getSchedClassIdx(const CodeGenInstruction &Inst) const { - return InstrClassMap.lookup(Inst.TheDef); + if (auto SchedPair = getSchedClassIdxPair(Inst)) + return SchedPair->second; + return 0; +} + +// Get the SchedClass SchedRW/InstRW pair for an instruction. +llvm::Optional> +CodeGenSchedModels::getSchedClassIdxPair(const CodeGenInstruction &Inst) const { + auto It = InstrClassMap.find(Inst.TheDef); + if (It != InstrClassMap.end()) + return It->second; + return None; } std::string @@ -1057,7 +1068,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 @@ -1071,10 +1082,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"); @@ -1136,7 +1146,7 @@ } // Map each Instr to this new class. for (Record *InstDef : InstDefs) - InstrClassMap[InstDef] = SCIdx; + InstrClassMap[InstDef].second = SCIdx; SC.InstRWs.push_back(InstRWDef); } } @@ -1274,7 +1284,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 diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "CodeGenInstruction.h" #include "CodeGenSchedule.h" #include "CodeGenTarget.h" #include "PredicateExpander.h" @@ -20,6 +21,7 @@ #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/Format.h" #include "llvm/Support/raw_ostream.h" @@ -38,6 +40,15 @@ #define DEBUG_TYPE "subtarget-emitter" +static cl::opt OptCheckSchedClassTable( + "check-sched-class-table", +#ifdef EXPENSIVE_CHECKS + cl::init(true), +#else + cl::init(false), +#endif + cl::Hidden, cl::desc("Check sched class tables for inconsistencies")); + namespace { class SubtargetEmitter { @@ -113,6 +124,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 EmitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS); @@ -1270,6 +1282,47 @@ } } +// Check SchedClass tables for unnecessary duplication. +void SubtargetEmitter::CheckSchedClassTables(SchedClassTables &SchedTables) { + ArrayRef SchedClasses = SchedModels.schedClasses(); + + for (const CodeGenInstruction *Inst : TGT.getInstructionsByEnumValue()) { + llvm::Optional> ScIdx = + SchedModels.getSchedClassIdxPair(*Inst); + if (!ScIdx || ScIdx->first == 0 || ScIdx->first == ScIdx->second) + 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) { + if (!PI->hasInstrSchedModel()) + continue; + + // If the schedule has been overridden with values that match the base + // class then the override is redundant and could be removed. + // NOTE: Ignores missing/incorrect ReadAdvance attributes on the override. + const std::vector &SCTab = + SchedTables.ProcSchedClasses[1 + (PI - SchedModels.procModelBegin())]; + const MCSchedClassDesc &ScOrig = SCTab[ScIdx->first]; + const MCSchedClassDesc &ScInst = SCTab[ScIdx->second]; + if (ScOrig.equals(ScInst, /*IgnoreReadAdvance*/ true)) { + for (const Record *InstrRW : CgInst.InstRWs) { + const Record *InstModelDef = InstrRW->getValueAsDef("SchedModel"); + if (InstModelDef->getName() == PI->ModelName) { + dbgs() << "'" << PI->ModelName + << "' unnecessarily overrides instruction '" + << Inst->TheDef->getName() << "' from '" << CgOrig.Name + << "'\n"; + } + } + } + } + } +} + // Emit SchedClass tables for all processors and associated global tables. void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS) { @@ -1449,6 +1502,10 @@ for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { GenSchedClassTables(ProcModel, SchedTables); } + + if (OptCheckSchedClassTable) + CheckSchedClassTables(SchedTables); + EmitSchedClassTables(SchedTables, OS); OS << "\n#undef DBGFIELD\n";