1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00
llvm-mirror/lib/CodeGen/DFAPacketizer.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

319 lines
11 KiB
C++

//=- llvm/CodeGen/DFAPacketizer.cpp - DFA Packetizer for VLIW -*- 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 class implements a deterministic finite automaton (DFA) based
// packetizing mechanism for VLIW architectures. It provides APIs to
// determine whether there exists a legal mapping of instructions to
// functional unit assignments in a packet. The DFA is auto-generated from
// the target's Schedule.td file.
//
// A DFA consists of 3 major elements: states, inputs, and transitions. For
// the packetizing mechanism, the input is the set of instruction classes for
// a target. The state models all possible combinations of functional unit
// consumption for a given set of instructions in a packet. A transition
// models the addition of an instruction to a packet. In the DFA constructed
// by this class, if an instruction can be added to a packet, then a valid
// transition exists from the corresponding state. Invalid transitions
// indicate that the instruction cannot be added to the current packet.
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/DFAPacketizer.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBundle.h"
#include "llvm/CodeGen/ScheduleDAG.h"
#include "llvm/CodeGen/ScheduleDAGInstrs.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrItineraries.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <iterator>
#include <memory>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "packets"
static cl::opt<unsigned> InstrLimit("dfa-instr-limit", cl::Hidden,
cl::init(0), cl::desc("If present, stops packetizing after N instructions"));
static unsigned InstrCount = 0;
// Check if the resources occupied by a MCInstrDesc are available in the
// current state.
bool DFAPacketizer::canReserveResources(const MCInstrDesc *MID) {
unsigned Action = ItinActions[MID->getSchedClass()];
if (MID->getSchedClass() == 0 || Action == 0)
return false;
return A.canAdd(Action);
}
// Reserve the resources occupied by a MCInstrDesc and change the current
// state to reflect that change.
void DFAPacketizer::reserveResources(const MCInstrDesc *MID) {
unsigned Action = ItinActions[MID->getSchedClass()];
if (MID->getSchedClass() == 0 || Action == 0)
return;
A.add(Action);
}
// Check if the resources occupied by a machine instruction are available
// in the current state.
bool DFAPacketizer::canReserveResources(MachineInstr &MI) {
const MCInstrDesc &MID = MI.getDesc();
return canReserveResources(&MID);
}
// Reserve the resources occupied by a machine instruction and change the
// current state to reflect that change.
void DFAPacketizer::reserveResources(MachineInstr &MI) {
const MCInstrDesc &MID = MI.getDesc();
reserveResources(&MID);
}
unsigned DFAPacketizer::getUsedResources(unsigned InstIdx) {
ArrayRef<NfaPath> NfaPaths = A.getNfaPaths();
assert(!NfaPaths.empty() && "Invalid bundle!");
const NfaPath &RS = NfaPaths.front();
// RS stores the cumulative resources used up to and including the I'th
// instruction. The 0th instruction is the base case.
if (InstIdx == 0)
return RS[0];
// Return the difference between the cumulative resources used by InstIdx and
// its predecessor.
return RS[InstIdx] ^ RS[InstIdx - 1];
}
namespace llvm {
// This class extends ScheduleDAGInstrs and overrides the schedule method
// to build the dependence graph.
class DefaultVLIWScheduler : public ScheduleDAGInstrs {
private:
AAResults *AA;
/// Ordered list of DAG postprocessing steps.
std::vector<std::unique_ptr<ScheduleDAGMutation>> Mutations;
public:
DefaultVLIWScheduler(MachineFunction &MF, MachineLoopInfo &MLI,
AAResults *AA);
// Actual scheduling work.
void schedule() override;
/// DefaultVLIWScheduler takes ownership of the Mutation object.
void addMutation(std::unique_ptr<ScheduleDAGMutation> Mutation) {
Mutations.push_back(std::move(Mutation));
}
protected:
void postprocessDAG();
};
} // end namespace llvm
DefaultVLIWScheduler::DefaultVLIWScheduler(MachineFunction &MF,
MachineLoopInfo &MLI,
AAResults *AA)
: ScheduleDAGInstrs(MF, &MLI), AA(AA) {
CanHandleTerminators = true;
}
/// Apply each ScheduleDAGMutation step in order.
void DefaultVLIWScheduler::postprocessDAG() {
for (auto &M : Mutations)
M->apply(this);
}
void DefaultVLIWScheduler::schedule() {
// Build the scheduling graph.
buildSchedGraph(AA);
postprocessDAG();
}
VLIWPacketizerList::VLIWPacketizerList(MachineFunction &mf,
MachineLoopInfo &mli, AAResults *aa)
: MF(mf), TII(mf.getSubtarget().getInstrInfo()), AA(aa) {
ResourceTracker = TII->CreateTargetScheduleState(MF.getSubtarget());
ResourceTracker->setTrackResources(true);
VLIWScheduler = new DefaultVLIWScheduler(MF, mli, AA);
}
VLIWPacketizerList::~VLIWPacketizerList() {
delete VLIWScheduler;
delete ResourceTracker;
}
// End the current packet, bundle packet instructions and reset DFA state.
void VLIWPacketizerList::endPacket(MachineBasicBlock *MBB,
MachineBasicBlock::iterator MI) {
LLVM_DEBUG({
if (!CurrentPacketMIs.empty()) {
dbgs() << "Finalizing packet:\n";
unsigned Idx = 0;
for (MachineInstr *MI : CurrentPacketMIs) {
unsigned R = ResourceTracker->getUsedResources(Idx++);
dbgs() << " * [res:0x" << utohexstr(R) << "] " << *MI;
}
}
});
if (CurrentPacketMIs.size() > 1) {
MachineInstr &MIFirst = *CurrentPacketMIs.front();
finalizeBundle(*MBB, MIFirst.getIterator(), MI.getInstrIterator());
}
CurrentPacketMIs.clear();
ResourceTracker->clearResources();
LLVM_DEBUG(dbgs() << "End packet\n");
}
// Bundle machine instructions into packets.
void VLIWPacketizerList::PacketizeMIs(MachineBasicBlock *MBB,
MachineBasicBlock::iterator BeginItr,
MachineBasicBlock::iterator EndItr) {
assert(VLIWScheduler && "VLIW Scheduler is not initialized!");
VLIWScheduler->startBlock(MBB);
VLIWScheduler->enterRegion(MBB, BeginItr, EndItr,
std::distance(BeginItr, EndItr));
VLIWScheduler->schedule();
LLVM_DEBUG({
dbgs() << "Scheduling DAG of the packetize region\n";
VLIWScheduler->dump();
});
// Generate MI -> SU map.
MIToSUnit.clear();
for (SUnit &SU : VLIWScheduler->SUnits)
MIToSUnit[SU.getInstr()] = &SU;
bool LimitPresent = InstrLimit.getPosition();
// The main packetizer loop.
for (; BeginItr != EndItr; ++BeginItr) {
if (LimitPresent) {
if (InstrCount >= InstrLimit) {
EndItr = BeginItr;
break;
}
InstrCount++;
}
MachineInstr &MI = *BeginItr;
initPacketizerState();
// End the current packet if needed.
if (isSoloInstruction(MI)) {
endPacket(MBB, MI);
continue;
}
// Ignore pseudo instructions.
if (ignorePseudoInstruction(MI, MBB))
continue;
SUnit *SUI = MIToSUnit[&MI];
assert(SUI && "Missing SUnit Info!");
// Ask DFA if machine resource is available for MI.
LLVM_DEBUG(dbgs() << "Checking resources for adding MI to packet " << MI);
bool ResourceAvail = ResourceTracker->canReserveResources(MI);
LLVM_DEBUG({
if (ResourceAvail)
dbgs() << " Resources are available for adding MI to packet\n";
else
dbgs() << " Resources NOT available\n";
});
if (ResourceAvail && shouldAddToPacket(MI)) {
// Dependency check for MI with instructions in CurrentPacketMIs.
for (auto MJ : CurrentPacketMIs) {
SUnit *SUJ = MIToSUnit[MJ];
assert(SUJ && "Missing SUnit Info!");
LLVM_DEBUG(dbgs() << " Checking against MJ " << *MJ);
// Is it legal to packetize SUI and SUJ together.
if (!isLegalToPacketizeTogether(SUI, SUJ)) {
LLVM_DEBUG(dbgs() << " Not legal to add MI, try to prune\n");
// Allow packetization if dependency can be pruned.
if (!isLegalToPruneDependencies(SUI, SUJ)) {
// End the packet if dependency cannot be pruned.
LLVM_DEBUG(dbgs()
<< " Could not prune dependencies for adding MI\n");
endPacket(MBB, MI);
break;
}
LLVM_DEBUG(dbgs() << " Pruned dependence for adding MI\n");
}
}
} else {
LLVM_DEBUG(if (ResourceAvail) dbgs()
<< "Resources are available, but instruction should not be "
"added to packet\n "
<< MI);
// End the packet if resource is not available, or if the instruction
// shoud not be added to the current packet.
endPacket(MBB, MI);
}
// Add MI to the current packet.
LLVM_DEBUG(dbgs() << "* Adding MI to packet " << MI << '\n');
BeginItr = addToPacket(MI);
} // For all instructions in the packetization range.
// End any packet left behind.
endPacket(MBB, EndItr);
VLIWScheduler->exitRegion();
VLIWScheduler->finishBlock();
}
bool VLIWPacketizerList::alias(const MachineMemOperand &Op1,
const MachineMemOperand &Op2,
bool UseTBAA) const {
if (!Op1.getValue() || !Op2.getValue())
return true;
int64_t MinOffset = std::min(Op1.getOffset(), Op2.getOffset());
int64_t Overlapa = Op1.getSize() + Op1.getOffset() - MinOffset;
int64_t Overlapb = Op2.getSize() + Op2.getOffset() - MinOffset;
AliasResult AAResult =
AA->alias(MemoryLocation(Op1.getValue(), Overlapa,
UseTBAA ? Op1.getAAInfo() : AAMDNodes()),
MemoryLocation(Op2.getValue(), Overlapb,
UseTBAA ? Op2.getAAInfo() : AAMDNodes()));
return AAResult != NoAlias;
}
bool VLIWPacketizerList::alias(const MachineInstr &MI1,
const MachineInstr &MI2,
bool UseTBAA) const {
if (MI1.memoperands_empty() || MI2.memoperands_empty())
return true;
for (const MachineMemOperand *Op1 : MI1.memoperands())
for (const MachineMemOperand *Op2 : MI2.memoperands())
if (alias(*Op1, *Op2, UseTBAA))
return true;
return false;
}
// Add a DAG mutation object to the ordered list.
void VLIWPacketizerList::addMutation(
std::unique_ptr<ScheduleDAGMutation> Mutation) {
VLIWScheduler->addMutation(std::move(Mutation));
}