1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00
llvm-mirror/utils/TableGen/DFAPacketizerEmitter.cpp
jmolloy 548ef37194 [DFAPacketizer] Allow up to 64 functional units
Summary:
To drive the automaton we used a uint64_t as an action type. This
contained the transition's resource requirements as a conjunction:

  (a OR b) AND (b OR c)

We encoded this conjunction as a sequence of four 16-bit bitmasks.
This limited the number of addressable functional units to 16, which
is quite low and has bitten many people in the past.

Instead, the DFAEmitter now generates a lookup table from InstrItinerary
class (index of the ItinData inside the ProcItineraries) to an internal
action index which is essentially a dense embedding of the conjunctive
form. Because we never materialize the conjunctive form, we no longer
have the 16 FU restriction.

In this patch we limit to 64 functional units due to using a uint64_t
bitmask in the DFAEmitter. Now that we've decoupled these representations
we can increase this in future.

Reviewers: ThomasRaoux, kparzysz, majnemer

Reviewed By: ThomasRaoux

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D69110
2019-11-05 15:41:42 +00:00

364 lines
13 KiB
C++

//===- DFAPacketizerEmitter.cpp - Packetization DFA for a VLIW machine ----===//
//
// 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 class parses the Schedule.td file and produces an API that can be used
// to reason about whether an instruction can be added to a packet on a VLIW
// architecture. The class internally generates a deterministic finite
// automaton (DFA) that models all possible mappings of machine instructions
// to functional units as instructions are added to a packet.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "dfa-emitter"
#include "CodeGenSchedule.h"
#include "CodeGenTarget.h"
#include "DFAEmitter.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <cassert>
#include <cstdint>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
using namespace llvm;
// We use a uint64_t to represent a resource bitmask.
#define DFA_MAX_RESOURCES 64
namespace {
using ResourceVector = SmallVector<uint64_t, 4>;
struct ScheduleClass {
/// The parent itinerary index (processor model ID).
unsigned ItineraryID;
/// Index within this itinerary of the schedule class.
unsigned Idx;
/// The index within the uniqued set of required resources of Resources.
unsigned ResourcesIdx;
/// Conjunctive list of resource requirements:
/// {a|b, b|c} => (a OR b) AND (b or c).
/// Resources are unique across all itineraries.
ResourceVector Resources;
};
// Generates and prints out the DFA for resource tracking.
class DFAPacketizerEmitter {
private:
std::string TargetName;
RecordKeeper &Records;
UniqueVector<ResourceVector> UniqueResources;
std::vector<ScheduleClass> ScheduleClasses;
std::map<std::string, uint64_t> FUNameToBitsMap;
std::map<unsigned, uint64_t> ComboBitToBitsMap;
public:
DFAPacketizerEmitter(RecordKeeper &R);
// Construct a map of function unit names to bits.
int collectAllFuncUnits(
ArrayRef<const CodeGenProcModel *> ProcModels);
// Construct a map from a combo function unit bit to the bits of all included
// functional units.
int collectAllComboFuncs(ArrayRef<Record *> ComboFuncList);
ResourceVector getResourcesForItinerary(Record *Itinerary);
void createScheduleClasses(unsigned ItineraryIdx, const RecVec &Itineraries);
// Emit code for a subset of itineraries.
void emitForItineraries(raw_ostream &OS,
std::vector<const CodeGenProcModel *> &ProcItinList,
std::string DFAName);
void run(raw_ostream &OS);
};
} // end anonymous namespace
DFAPacketizerEmitter::DFAPacketizerEmitter(RecordKeeper &R)
: TargetName(CodeGenTarget(R).getName()), Records(R) {}
int DFAPacketizerEmitter::collectAllFuncUnits(
ArrayRef<const CodeGenProcModel *> ProcModels) {
LLVM_DEBUG(dbgs() << "-------------------------------------------------------"
"----------------------\n");
LLVM_DEBUG(dbgs() << "collectAllFuncUnits");
LLVM_DEBUG(dbgs() << " (" << ProcModels.size() << " itineraries)\n");
std::set<Record *> ProcItinList;
for (const CodeGenProcModel *Model : ProcModels)
ProcItinList.insert(Model->ItinsDef);
int totalFUs = 0;
// Parse functional units for all the itineraries.
for (Record *Proc : ProcItinList) {
std::vector<Record *> FUs = Proc->getValueAsListOfDefs("FU");
LLVM_DEBUG(dbgs() << " FU:"
<< " (" << FUs.size() << " FUs) " << Proc->getName());
// Convert macros to bits for each stage.
unsigned numFUs = FUs.size();
for (unsigned j = 0; j < numFUs; ++j) {
assert((j < DFA_MAX_RESOURCES) &&
"Exceeded maximum number of representable resources");
uint64_t FuncResources = 1ULL << j;
FUNameToBitsMap[FUs[j]->getName()] = FuncResources;
LLVM_DEBUG(dbgs() << " " << FUs[j]->getName() << ":0x"
<< Twine::utohexstr(FuncResources));
}
totalFUs += numFUs;
LLVM_DEBUG(dbgs() << "\n");
}
return totalFUs;
}
int DFAPacketizerEmitter::collectAllComboFuncs(ArrayRef<Record *> ComboFuncList) {
LLVM_DEBUG(dbgs() << "-------------------------------------------------------"
"----------------------\n");
LLVM_DEBUG(dbgs() << "collectAllComboFuncs");
LLVM_DEBUG(dbgs() << " (" << ComboFuncList.size() << " sets)\n");
int numCombos = 0;
for (unsigned i = 0, N = ComboFuncList.size(); i < N; ++i) {
Record *Func = ComboFuncList[i];
std::vector<Record *> FUs = Func->getValueAsListOfDefs("CFD");
LLVM_DEBUG(dbgs() << " CFD:" << i << " (" << FUs.size() << " combo FUs) "
<< Func->getName() << "\n");
// Convert macros to bits for each stage.
for (unsigned j = 0, N = FUs.size(); j < N; ++j) {
assert((j < DFA_MAX_RESOURCES) &&
"Exceeded maximum number of DFA resources");
Record *FuncData = FUs[j];
Record *ComboFunc = FuncData->getValueAsDef("TheComboFunc");
const std::vector<Record *> &FuncList =
FuncData->getValueAsListOfDefs("FuncList");
const std::string &ComboFuncName = ComboFunc->getName();
uint64_t ComboBit = FUNameToBitsMap[ComboFuncName];
uint64_t ComboResources = ComboBit;
LLVM_DEBUG(dbgs() << " combo: " << ComboFuncName << ":0x"
<< Twine::utohexstr(ComboResources) << "\n");
for (unsigned k = 0, M = FuncList.size(); k < M; ++k) {
std::string FuncName = FuncList[k]->getName();
uint64_t FuncResources = FUNameToBitsMap[FuncName];
LLVM_DEBUG(dbgs() << " " << FuncName << ":0x"
<< Twine::utohexstr(FuncResources) << "\n");
ComboResources |= FuncResources;
}
ComboBitToBitsMap[ComboBit] = ComboResources;
numCombos++;
LLVM_DEBUG(dbgs() << " => combo bits: " << ComboFuncName << ":0x"
<< Twine::utohexstr(ComboBit) << " = 0x"
<< Twine::utohexstr(ComboResources) << "\n");
}
}
return numCombos;
}
ResourceVector
DFAPacketizerEmitter::getResourcesForItinerary(Record *Itinerary) {
ResourceVector Resources;
assert(Itinerary);
for (Record *StageDef : Itinerary->getValueAsListOfDefs("Stages")) {
uint64_t StageResources = 0;
for (Record *Unit : StageDef->getValueAsListOfDefs("Units")) {
StageResources |= FUNameToBitsMap[Unit->getName()];
}
if (StageResources != 0)
Resources.push_back(StageResources);
}
return Resources;
}
void DFAPacketizerEmitter::createScheduleClasses(unsigned ItineraryIdx,
const RecVec &Itineraries) {
unsigned Idx = 0;
for (Record *Itinerary : Itineraries) {
if (!Itinerary) {
ScheduleClasses.push_back({ItineraryIdx, Idx++, 0, ResourceVector{}});
continue;
}
ResourceVector Resources = getResourcesForItinerary(Itinerary);
ScheduleClasses.push_back(
{ItineraryIdx, Idx++, UniqueResources.insert(Resources), Resources});
}
}
//
// Run the worklist algorithm to generate the DFA.
//
void DFAPacketizerEmitter::run(raw_ostream &OS) {
OS << "\n"
<< "#include \"llvm/CodeGen/DFAPacketizer.h\"\n";
OS << "namespace llvm {\n";
CodeGenTarget CGT(Records);
CodeGenSchedModels CGS(Records, CGT);
std::unordered_map<std::string, std::vector<const CodeGenProcModel *>>
ItinsByNamespace;
for (const CodeGenProcModel &ProcModel : CGS.procModels()) {
if (ProcModel.hasItineraries()) {
auto NS = ProcModel.ItinsDef->getValueAsString("PacketizerNamespace");
ItinsByNamespace[NS].push_back(&ProcModel);
}
}
for (auto &KV : ItinsByNamespace)
emitForItineraries(OS, KV.second, KV.first);
OS << "} // end namespace llvm\n";
}
void DFAPacketizerEmitter::emitForItineraries(
raw_ostream &OS, std::vector<const CodeGenProcModel *> &ProcModels,
std::string DFAName) {
OS << "} // end namespace llvm\n\n";
OS << "namespace {\n";
collectAllFuncUnits(ProcModels);
collectAllComboFuncs(Records.getAllDerivedDefinitions("ComboFuncUnits"));
// Collect the itineraries.
DenseMap<const CodeGenProcModel *, unsigned> ProcModelStartIdx;
for (const CodeGenProcModel *Model : ProcModels) {
assert(Model->hasItineraries());
ProcModelStartIdx[Model] = ScheduleClasses.size();
createScheduleClasses(Model->Index, Model->ItinDefList);
}
// Output the mapping from ScheduleClass to ResourcesIdx.
unsigned Idx = 0;
OS << "unsigned " << TargetName << DFAName << "ResourceIndices[] = {";
for (const ScheduleClass &SC : ScheduleClasses) {
if (Idx++ % 32 == 0)
OS << "\n ";
OS << SC.ResourcesIdx << ", ";
}
OS << "\n};\n\n";
// And the mapping from Itinerary index into the previous table.
OS << "unsigned " << TargetName << DFAName
<< "ProcResourceIndexStart[] = {\n";
OS << " 0, // NoSchedModel\n";
for (const CodeGenProcModel *Model : ProcModels) {
OS << " " << ProcModelStartIdx[Model] << ", // " << Model->ModelName
<< "\n";
}
OS << ScheduleClasses.size() << "\n};\n\n";
// The type of a state in the nondeterministic automaton we're defining.
using NfaStateTy = uint64_t;
// Given a resource state, return all resource states by applying
// InsnClass.
auto applyInsnClass = [&](const ResourceVector &InsnClass,
NfaStateTy State) -> std::deque<NfaStateTy> {
std::deque<NfaStateTy> V(1, State);
// Apply every stage in the class individually.
for (NfaStateTy Stage : InsnClass) {
// Apply this stage to every existing member of V in turn.
size_t Sz = V.size();
for (unsigned I = 0; I < Sz; ++I) {
NfaStateTy S = V.front();
V.pop_front();
// For this stage, state combination, try all possible resources.
for (unsigned J = 0; J < DFA_MAX_RESOURCES; ++J) {
NfaStateTy ResourceMask = 1ULL << J;
if ((ResourceMask & Stage) == 0)
// This resource isn't required by this stage.
continue;
NfaStateTy Combo = ComboBitToBitsMap[ResourceMask];
if (Combo && ((~S & Combo) != Combo))
// This combo units bits are not available.
continue;
NfaStateTy ResultingResourceState = S | ResourceMask | Combo;
if (ResultingResourceState == S)
continue;
V.push_back(ResultingResourceState);
}
}
}
return V;
};
// Given a resource state, return a quick (conservative) guess as to whether
// InsnClass can be applied. This is a filter for the more heavyweight
// applyInsnClass.
auto canApplyInsnClass = [](const ResourceVector &InsnClass,
NfaStateTy State) -> bool {
for (NfaStateTy Resources : InsnClass) {
if ((State | Resources) == State)
return false;
}
return true;
};
DfaEmitter Emitter;
std::deque<NfaStateTy> Worklist(1, 0);
std::set<NfaStateTy> SeenStates;
SeenStates.insert(Worklist.front());
while (!Worklist.empty()) {
NfaStateTy State = Worklist.front();
Worklist.pop_front();
for (const ResourceVector &Resources : UniqueResources) {
if (!canApplyInsnClass(Resources, State))
continue;
unsigned ResourcesID = UniqueResources.idFor(Resources);
for (uint64_t NewState : applyInsnClass(Resources, State)) {
if (SeenStates.emplace(NewState).second)
Worklist.emplace_back(NewState);
Emitter.addTransition(State, NewState, ResourcesID);
}
}
}
std::string TargetAndDFAName = TargetName + DFAName;
Emitter.emit(TargetAndDFAName, OS);
OS << "} // end anonymous namespace\n\n";
std::string SubTargetClassName = TargetName + "GenSubtargetInfo";
OS << "namespace llvm {\n";
OS << "DFAPacketizer *" << SubTargetClassName << "::"
<< "create" << DFAName
<< "DFAPacketizer(const InstrItineraryData *IID) const {\n"
<< " static Automaton<uint64_t> A(ArrayRef<" << TargetAndDFAName
<< "Transition>(" << TargetAndDFAName << "Transitions), "
<< TargetAndDFAName << "TransitionInfo);\n"
<< " unsigned ProcResIdxStart = " << TargetAndDFAName
<< "ProcResourceIndexStart[IID->SchedModel.ProcID];\n"
<< " unsigned ProcResIdxNum = " << TargetAndDFAName
<< "ProcResourceIndexStart[IID->SchedModel.ProcID + 1] - "
"ProcResIdxStart;\n"
<< " return new DFAPacketizer(IID, A, {&" << TargetAndDFAName
<< "ResourceIndices[ProcResIdxStart], ProcResIdxNum});\n"
<< "\n}\n\n";
}
namespace llvm {
void EmitDFAPacketizer(RecordKeeper &RK, raw_ostream &OS) {
emitSourceFileHeader("Target DFA Packetizer Tables", OS);
DFAPacketizerEmitter(RK).run(OS);
}
} // end namespace llvm