#include "HexagonVLIWPacketizer.h"
#include "Hexagon.h"
#include "HexagonInstrInfo.h"
#include "HexagonRegisterInfo.h"
#include "HexagonSubtarget.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBundle.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/ScheduleDAG.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdint>
#include <iterator>
using namespace llvm;
#define DEBUG_TYPE "packets"
static cl::opt<bool>
DisablePacketizer("disable-packetizer", cl::Hidden,
cl::desc("Disable Hexagon packetizer pass"));
static cl::opt<bool> Slot1Store("slot1-store-slot0-load", cl::Hidden,
cl::init(true),
cl::desc("Allow slot1 store and slot0 load"));
static cl::opt<bool> PacketizeVolatiles(
"hexagon-packetize-volatiles", cl::Hidden, cl::init(true),
cl::desc("Allow non-solo packetization of volatile memory references"));
static cl::opt<bool>
EnableGenAllInsnClass("enable-gen-insn", cl::Hidden,
cl::desc("Generate all instruction with TC"));
static cl::opt<bool>
DisableVecDblNVStores("disable-vecdbl-nv-stores", cl::Hidden,
cl::desc("Disable vector double new-value-stores"));
extern cl::opt<bool> ScheduleInlineAsm;
namespace llvm {
FunctionPass *createHexagonPacketizer(bool Minimal);
void initializeHexagonPacketizerPass(PassRegistry&);
}
namespace {
class HexagonPacketizer : public MachineFunctionPass {
public:
static char ID;
HexagonPacketizer(bool Min = false)
: MachineFunctionPass(ID), Minimal(Min) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<AAResultsWrapperPass>();
AU.addRequired<MachineBranchProbabilityInfo>();
AU.addRequired<MachineDominatorTree>();
AU.addRequired<MachineLoopInfo>();
AU.addPreserved<MachineDominatorTree>();
AU.addPreserved<MachineLoopInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
StringRef getPassName() const override { return "Hexagon Packetizer"; }
bool runOnMachineFunction(MachineFunction &Fn) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
private:
const HexagonInstrInfo *HII = nullptr;
const HexagonRegisterInfo *HRI = nullptr;
const bool Minimal = false;
};
}
char HexagonPacketizer::ID = 0;
INITIALIZE_PASS_BEGIN(HexagonPacketizer, "hexagon-packetizer",
"Hexagon Packetizer", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_DEPENDENCY(MachineBranchProbabilityInfo)
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_END(HexagonPacketizer, "hexagon-packetizer",
"Hexagon Packetizer", false, false)
HexagonPacketizerList::HexagonPacketizerList(MachineFunction &MF,
MachineLoopInfo &MLI, AAResults *AA,
const MachineBranchProbabilityInfo *MBPI, bool Minimal)
: VLIWPacketizerList(MF, MLI, AA), MBPI(MBPI), MLI(&MLI),
Minimal(Minimal) {
HII = MF.getSubtarget<HexagonSubtarget>().getInstrInfo();
HRI = MF.getSubtarget<HexagonSubtarget>().getRegisterInfo();
addMutation(std::make_unique<HexagonSubtarget::UsrOverflowMutation>());
addMutation(std::make_unique<HexagonSubtarget::HVXMemLatencyMutation>());
addMutation(std::make_unique<HexagonSubtarget::BankConflictMutation>());
}
static bool hasWriteToReadDep(const MachineInstr &FirstI,
const MachineInstr &SecondI,
const TargetRegisterInfo *TRI) {
for (auto &MO : FirstI.operands()) {
if (!MO.isReg() || !MO.isDef())
continue;
Register R = MO.getReg();
if (SecondI.readsRegister(R, TRI))
return true;
}
return false;
}
static MachineBasicBlock::iterator moveInstrOut(MachineInstr &MI,
MachineBasicBlock::iterator BundleIt, bool Before) {
MachineBasicBlock::instr_iterator InsertPt;
if (Before)
InsertPt = BundleIt.getInstrIterator();
else
InsertPt = std::next(BundleIt).getInstrIterator();
MachineBasicBlock &B = *MI.getParent();
assert(MI.isBundledWithPred());
if (MI.isBundledWithSucc()) {
MI.clearFlag(MachineInstr::BundledSucc);
MI.clearFlag(MachineInstr::BundledPred);
} else {
MI.unbundleFromPred();
}
B.splice(InsertPt, &B, MI.getIterator());
MachineBasicBlock::const_instr_iterator I = BundleIt.getInstrIterator();
MachineBasicBlock::const_instr_iterator E = B.instr_end();
unsigned Size = 0;
for (++I; I != E && I->isBundledWithPred(); ++I)
++Size;
if (Size > 1)
return BundleIt;
MachineBasicBlock::iterator NextIt = std::next(BundleIt);
MachineInstr &SingleI = *BundleIt->getNextNode();
SingleI.unbundleFromPred();
assert(!SingleI.isBundledWithSucc());
BundleIt->eraseFromParent();
return NextIt;
}
bool HexagonPacketizer::runOnMachineFunction(MachineFunction &MF) {
MF.getProperties().set(
MachineFunctionProperties::Property::FailsVerification);
auto &HST = MF.getSubtarget<HexagonSubtarget>();
HII = HST.getInstrInfo();
HRI = HST.getRegisterInfo();
auto &MLI = getAnalysis<MachineLoopInfo>();
auto *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
auto *MBPI = &getAnalysis<MachineBranchProbabilityInfo>();
if (EnableGenAllInsnClass)
HII->genAllInsnTimingClasses(MF);
bool MinOnly = Minimal || DisablePacketizer || !HST.usePackets() ||
skipFunction(MF.getFunction());
HexagonPacketizerList Packetizer(MF, MLI, AA, MBPI, MinOnly);
assert(Packetizer.getResourceTracker() && "Empty DFA table!");
for (MachineBasicBlock &MB : MF) {
for (MachineInstr &MI : llvm::make_early_inc_range(MB))
if (MI.isKill())
MB.erase(&MI);
}
if (HST.isTinyCoreWithDuplex())
HII->translateInstrsForDup(MF, true);
for (auto &MB : MF) {
auto Begin = MB.begin(), End = MB.end();
while (Begin != End) {
MachineBasicBlock::iterator RB = Begin;
while (RB != End && HII->isSchedulingBoundary(*RB, &MB, MF))
++RB;
MachineBasicBlock::iterator RE = RB;
while (RE != End && !HII->isSchedulingBoundary(*RE, &MB, MF))
++RE;
if (RE != End)
++RE;
if (RB != End)
Packetizer.PacketizeMIs(&MB, RB, RE);
Begin = RE;
}
}
if (HST.isTinyCoreWithDuplex())
HII->translateInstrsForDup(MF, false);
Packetizer.unpacketizeSoloInstrs(MF);
return true;
}
void HexagonPacketizerList::reserveResourcesForConstExt() {
if (!tryAllocateResourcesForConstExt(true))
llvm_unreachable("Resources not available");
}
bool HexagonPacketizerList::canReserveResourcesForConstExt() {
return tryAllocateResourcesForConstExt(false);
}
bool HexagonPacketizerList::tryAllocateResourcesForConstExt(bool Reserve) {
auto *ExtMI = MF.CreateMachineInstr(HII->get(Hexagon::A4_ext), DebugLoc());
bool Avail = ResourceTracker->canReserveResources(*ExtMI);
if (Reserve && Avail)
ResourceTracker->reserveResources(*ExtMI);
MF.deleteMachineInstr(ExtMI);
return Avail;
}
bool HexagonPacketizerList::isCallDependent(const MachineInstr &MI,
SDep::Kind DepType, unsigned DepReg) {
if (DepReg == HRI->getRARegister())
return true;
if (HII->isDeallocRet(MI))
if (DepReg == HRI->getFrameRegister() || DepReg == HRI->getStackRegister())
return true;
if (DepType == SDep::Data) {
for (const MachineOperand &MO : MI.operands())
if (MO.isReg() && MO.getReg() == DepReg && !MO.isImplicit())
return true;
}
return false;
}
static bool isRegDependence(const SDep::Kind DepType) {
return DepType == SDep::Data || DepType == SDep::Anti ||
DepType == SDep::Output;
}
static bool isDirectJump(const MachineInstr &MI) {
return MI.getOpcode() == Hexagon::J2_jump;
}
static bool isSchedBarrier(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case Hexagon::Y2_barrier:
return true;
}
return false;
}
static bool isControlFlow(const MachineInstr &MI) {
return MI.getDesc().isTerminator() || MI.getDesc().isCall();
}
static bool doesModifyCalleeSavedReg(const MachineInstr &MI,
const TargetRegisterInfo *TRI) {
const MachineFunction &MF = *MI.getParent()->getParent();
for (auto *CSR = TRI->getCalleeSavedRegs(&MF); CSR && *CSR; ++CSR)
if (MI.modifiesRegister(*CSR, TRI))
return true;
return false;
}
bool HexagonPacketizerList::isNewifiable(const MachineInstr &MI,
const TargetRegisterClass *NewRC) {
if (NewRC == &Hexagon::PredRegsRegClass) {
if (HII->isHVXVec(MI) && MI.mayStore())
return false;
return HII->isPredicated(MI) && HII->getDotNewPredOp(MI, nullptr) > 0;
}
return HII->mayBeNewStore(MI);
}
bool HexagonPacketizerList::promoteToDotCur(MachineInstr &MI,
SDep::Kind DepType, MachineBasicBlock::iterator &MII,
const TargetRegisterClass* RC) {
assert(DepType == SDep::Data);
int CurOpcode = HII->getDotCurOp(MI);
MI.setDesc(HII->get(CurOpcode));
return true;
}
void HexagonPacketizerList::cleanUpDotCur() {
MachineInstr *MI = nullptr;
for (auto BI : CurrentPacketMIs) {
LLVM_DEBUG(dbgs() << "Cleanup packet has "; BI->dump(););
if (HII->isDotCurInst(*BI)) {
MI = BI;
continue;
}
if (MI) {
for (auto &MO : BI->operands())
if (MO.isReg() && MO.getReg() == MI->getOperand(0).getReg())
return;
}
}
if (!MI)
return;
MI->setDesc(HII->get(HII->getNonDotCurOp(*MI)));
LLVM_DEBUG(dbgs() << "Demoted CUR "; MI->dump(););
}
bool HexagonPacketizerList::canPromoteToDotCur(const MachineInstr &MI,
const SUnit *PacketSU, unsigned DepReg, MachineBasicBlock::iterator &MII,
const TargetRegisterClass *RC) {
if (!HII->isHVXVec(MI))
return false;
if (!HII->isHVXVec(*MII))
return false;
if (HII->isDotCurInst(MI) && !HII->mayBeCurLoad(MI))
return false;
if (!HII->mayBeCurLoad(MI))
return false;
if (PacketSU->getInstr()->isInlineAsm())
return false;
LLVM_DEBUG(dbgs() << "Can we DOT Cur Vector MI\n"; MI.dump();
dbgs() << "in packet\n";);
MachineInstr &MJ = *MII;
LLVM_DEBUG({
dbgs() << "Checking CUR against ";
MJ.dump();
});
Register DestReg = MI.getOperand(0).getReg();
bool FoundMatch = false;
for (auto &MO : MJ.operands())
if (MO.isReg() && MO.getReg() == DestReg)
FoundMatch = true;
if (!FoundMatch)
return false;
for (auto BI : CurrentPacketMIs) {
LLVM_DEBUG(dbgs() << "packet has "; BI->dump(););
if (BI->readsRegister(DepReg, MF.getSubtarget().getRegisterInfo()))
return false;
}
LLVM_DEBUG(dbgs() << "Can Dot CUR MI\n"; MI.dump(););
return true;
}
bool HexagonPacketizerList::promoteToDotNew(MachineInstr &MI,
SDep::Kind DepType, MachineBasicBlock::iterator &MII,
const TargetRegisterClass* RC) {
assert(DepType == SDep::Data);
int NewOpcode;
if (RC == &Hexagon::PredRegsRegClass)
NewOpcode = HII->getDotNewPredOp(MI, MBPI);
else
NewOpcode = HII->getDotNewOp(MI);
MI.setDesc(HII->get(NewOpcode));
return true;
}
bool HexagonPacketizerList::demoteToDotOld(MachineInstr &MI) {
int NewOpcode = HII->getDotOldOp(MI);
MI.setDesc(HII->get(NewOpcode));
return true;
}
bool HexagonPacketizerList::useCallersSP(MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
switch (Opc) {
case Hexagon::S2_storerd_io:
case Hexagon::S2_storeri_io:
case Hexagon::S2_storerh_io:
case Hexagon::S2_storerb_io:
break;
default:
llvm_unreachable("Unexpected instruction");
}
unsigned FrameSize = MF.getFrameInfo().getStackSize();
MachineOperand &Off = MI.getOperand(1);
int64_t NewOff = Off.getImm() - (FrameSize + HEXAGON_LRFP_SIZE);
if (HII->isValidOffset(Opc, NewOff, HRI)) {
Off.setImm(NewOff);
return true;
}
return false;
}
void HexagonPacketizerList::useCalleesSP(MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
switch (Opc) {
case Hexagon::S2_storerd_io:
case Hexagon::S2_storeri_io:
case Hexagon::S2_storerh_io:
case Hexagon::S2_storerb_io:
break;
default:
llvm_unreachable("Unexpected instruction");
}
unsigned FrameSize = MF.getFrameInfo().getStackSize();
MachineOperand &Off = MI.getOperand(1);
Off.setImm(Off.getImm() + FrameSize + HEXAGON_LRFP_SIZE);
}
bool HexagonPacketizerList::updateOffset(SUnit *SUI, SUnit *SUJ) {
assert(SUI->getInstr() && SUJ->getInstr());
MachineInstr &MI = *SUI->getInstr();
MachineInstr &MJ = *SUJ->getInstr();
unsigned BPI, OPI;
if (!HII->getBaseAndOffsetPosition(MI, BPI, OPI))
return false;
unsigned BPJ, OPJ;
if (!HII->getBaseAndOffsetPosition(MJ, BPJ, OPJ))
return false;
Register Reg = MI.getOperand(BPI).getReg();
if (Reg != MJ.getOperand(BPJ).getReg())
return false;
for (const auto &PI : SUI->Preds)
if (PI.getKind() != SDep::Anti &&
(PI.getKind() != SDep::Data || PI.getReg() != Reg))
return false;
int Incr;
if (!HII->getIncrementValue(MJ, Incr))
return false;
int64_t Offset = MI.getOperand(OPI).getImm();
if (!HII->isValidOffset(MI.getOpcode(), Offset+Incr, HRI))
return false;
MI.getOperand(OPI).setImm(Offset + Incr);
ChangedOffset = Offset;
return true;
}
void HexagonPacketizerList::undoChangedOffset(MachineInstr &MI) {
unsigned BP, OP;
if (!HII->getBaseAndOffsetPosition(MI, BP, OP))
llvm_unreachable("Unable to find base and offset operands.");
MI.getOperand(OP).setImm(ChangedOffset);
}
enum PredicateKind {
PK_False,
PK_True,
PK_Unknown
};
static PredicateKind getPredicateSense(const MachineInstr &MI,
const HexagonInstrInfo *HII) {
if (!HII->isPredicated(MI))
return PK_Unknown;
if (HII->isPredicatedTrue(MI))
return PK_True;
return PK_False;
}
static const MachineOperand &getPostIncrementOperand(const MachineInstr &MI,
const HexagonInstrInfo *HII) {
assert(HII->isPostIncrement(MI) && "Not a post increment operation.");
#ifndef NDEBUG
DenseSet<unsigned> DefRegsSet;
for (auto &MO : MI.operands())
if (MO.isReg() && MO.isDef())
DefRegsSet.insert(MO.getReg());
for (auto &MO : MI.operands())
if (MO.isReg() && MO.isUse() && DefRegsSet.count(MO.getReg()))
return MO;
#else
if (MI.mayLoad()) {
const MachineOperand &Op1 = MI.getOperand(1);
assert(Op1.isReg() && "Post increment operand has be to a register.");
return Op1;
}
if (MI.getDesc().mayStore()) {
const MachineOperand &Op0 = MI.getOperand(0);
assert(Op0.isReg() && "Post increment operand has be to a register.");
return Op0;
}
#endif
llvm_unreachable("mayLoad or mayStore not set for Post Increment operation");
}
static const MachineOperand& getStoreValueOperand(const MachineInstr &MI) {
return MI.getOperand(MI.getNumOperands()-1);
}
static bool isLoadAbsSet(const MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
switch (Opc) {
case Hexagon::L4_loadrd_ap:
case Hexagon::L4_loadrb_ap:
case Hexagon::L4_loadrh_ap:
case Hexagon::L4_loadrub_ap:
case Hexagon::L4_loadruh_ap:
case Hexagon::L4_loadri_ap:
return true;
}
return false;
}
static const MachineOperand &getAbsSetOperand(const MachineInstr &MI) {
assert(isLoadAbsSet(MI));
return MI.getOperand(1);
}
bool HexagonPacketizerList::canPromoteToNewValueStore(const MachineInstr &MI,
const MachineInstr &PacketMI, unsigned DepReg) {
if (!HII->mayBeNewStore(MI))
return false;
const MachineOperand &Val = getStoreValueOperand(MI);
if (Val.isReg() && Val.getReg() != DepReg)
return false;
const MCInstrDesc& MCID = PacketMI.getDesc();
const TargetRegisterClass *PacketRC = HII->getRegClass(MCID, 0, HRI, MF);
if (PacketRC == &Hexagon::DoubleRegsRegClass)
return false;
for (auto I : CurrentPacketMIs) {
SUnit *PacketSU = MIToSUnit.find(I)->second;
if (PacketSU->getInstr()->mayStore())
return false;
}
if (HII->isPostIncrement(MI) &&
getPostIncrementOperand(MI, HII).getReg() == DepReg) {
return false;
}
if (HII->isPostIncrement(PacketMI) && PacketMI.mayLoad() &&
getPostIncrementOperand(PacketMI, HII).getReg() == DepReg) {
return false;
}
if (isLoadAbsSet(PacketMI) && getAbsSetOperand(PacketMI).getReg() == DepReg)
return false;
if (HII->isPredicated(PacketMI)) {
if (!HII->isPredicated(MI))
return false;
unsigned predRegNumSrc = 0;
unsigned predRegNumDst = 0;
const TargetRegisterClass* predRegClass = nullptr;
for (auto &MO : PacketMI.operands()) {
if (!MO.isReg())
continue;
predRegNumSrc = MO.getReg();
predRegClass = HRI->getMinimalPhysRegClass(predRegNumSrc);
if (predRegClass == &Hexagon::PredRegsRegClass)
break;
}
assert((predRegClass == &Hexagon::PredRegsRegClass) &&
"predicate register not found in a predicated PacketMI instruction");
for (auto &MO : MI.operands()) {
if (!MO.isReg())
continue;
predRegNumDst = MO.getReg();
predRegClass = HRI->getMinimalPhysRegClass(predRegNumDst);
if (predRegClass == &Hexagon::PredRegsRegClass)
break;
}
assert((predRegClass == &Hexagon::PredRegsRegClass) &&
"predicate register not found in a predicated MI instruction");
if (predRegNumDst != predRegNumSrc ||
HII->isDotNewInst(PacketMI) != HII->isDotNewInst(MI) ||
getPredicateSense(MI, HII) != getPredicateSense(PacketMI, HII))
return false;
}
unsigned StartCheck = 0;
for (auto I : CurrentPacketMIs) {
SUnit *TempSU = MIToSUnit.find(I)->second;
MachineInstr &TempMI = *TempSU->getInstr();
if (&TempMI != &PacketMI && !StartCheck) continue;
StartCheck = 1;
if (&TempMI == &PacketMI) continue;
for (auto &MO : MI.operands())
if (MO.isReg() && TempSU->getInstr()->modifiesRegister(MO.getReg(), HRI))
return false;
}
if (!HII->isPostIncrement(MI)) {
for (unsigned opNum = 0; opNum < MI.getNumOperands()-1; opNum++) {
const MachineOperand &MO = MI.getOperand(opNum);
if (MO.isReg() && MO.getReg() == DepReg)
return false;
}
}
for (auto &MO : PacketMI.operands()) {
if (MO.isRegMask() && MO.clobbersPhysReg(DepReg))
return false;
if (!MO.isReg() || !MO.isDef() || !MO.isImplicit())
continue;
Register R = MO.getReg();
if (R == DepReg || HRI->isSuperRegister(DepReg, R))
return false;
}
for (auto &MO : MI.operands()) {
if (MO.isReg() && MO.isUse() && MO.isImplicit() && MO.getReg() == DepReg)
return false;
}
return true;
}
bool HexagonPacketizerList::canPromoteToNewValue(const MachineInstr &MI,
const SUnit *PacketSU, unsigned DepReg,
MachineBasicBlock::iterator &MII) {
if (!HII->mayBeNewStore(MI))
return false;
MachineInstr &PacketMI = *PacketSU->getInstr();
if (canPromoteToNewValueStore(MI, PacketMI, DepReg))
return true;
return false;
}
static bool isImplicitDependency(const MachineInstr &I, bool CheckDef,
unsigned DepReg) {
for (auto &MO : I.operands()) {
if (CheckDef && MO.isRegMask() && MO.clobbersPhysReg(DepReg))
return true;
if (!MO.isReg() || MO.getReg() != DepReg || !MO.isImplicit())
continue;
if (CheckDef == MO.isDef())
return true;
}
return false;
}
bool HexagonPacketizerList::canPromoteToDotNew(const MachineInstr &MI,
const SUnit *PacketSU, unsigned DepReg, MachineBasicBlock::iterator &MII,
const TargetRegisterClass* RC) {
if (HII->isDotNewInst(MI) && !HII->mayBeNewStore(MI))
return false;
if (!isNewifiable(MI, RC))
return false;
const MachineInstr &PI = *PacketSU->getInstr();
if (PI.isInlineAsm())
return false;
if (PI.isImplicitDef())
return false;
if (isImplicitDependency(PI, true, DepReg) ||
isImplicitDependency(MI, false, DepReg))
return false;
const MCInstrDesc& MCID = PI.getDesc();
const TargetRegisterClass *VecRC = HII->getRegClass(MCID, 0, HRI, MF);
if (DisableVecDblNVStores && VecRC == &Hexagon::HvxWRRegClass)
return false;
if (RC == &Hexagon::PredRegsRegClass)
return HII->predCanBeUsedAsDotNew(PI, DepReg);
if (RC != &Hexagon::PredRegsRegClass && !HII->mayBeNewStore(MI))
return false;
int NewOpcode = (RC != &Hexagon::PredRegsRegClass) ? HII->getDotNewOp(MI) :
HII->getDotNewPredOp(MI, MBPI);
const MCInstrDesc &D = HII->get(NewOpcode);
MachineInstr *NewMI = MF.CreateMachineInstr(D, DebugLoc());
bool ResourcesAvailable = ResourceTracker->canReserveResources(*NewMI);
MF.deleteMachineInstr(NewMI);
if (!ResourcesAvailable)
return false;
if (!canPromoteToNewValue(MI, PacketSU, DepReg, MII))
return false;
return true;
}
bool HexagonPacketizerList::restrictingDepExistInPacket(MachineInstr &MI,
unsigned DepReg) {
SUnit *PacketSUDep = MIToSUnit.find(&MI)->second;
for (auto I : CurrentPacketMIs) {
if (!HII->isPredicated(*I))
continue;
SUnit *PacketSU = MIToSUnit.find(I)->second;
if (PacketSU->isSucc(PacketSUDep)) {
for (unsigned i = 0; i < PacketSU->Succs.size(); ++i) {
auto &Dep = PacketSU->Succs[i];
if (Dep.getSUnit() == PacketSUDep && Dep.getKind() == SDep::Anti &&
Dep.getReg() == DepReg)
return true;
}
}
}
return false;
}
static unsigned getPredicatedRegister(MachineInstr &MI,
const HexagonInstrInfo *QII) {
assert(QII->isPredicated(MI) && "Must be predicated instruction");
for (auto &Op : MI.operands()) {
if (Op.isReg() && Op.getReg() && Op.isUse() &&
Hexagon::PredRegsRegClass.contains(Op.getReg()))
return Op.getReg();
}
llvm_unreachable("Unknown instruction operand layout");
return 0;
}
bool HexagonPacketizerList::arePredicatesComplements(MachineInstr &MI1,
MachineInstr &MI2) {
if (getPredicateSense(MI1, HII) == PK_Unknown ||
getPredicateSense(MI2, HII) == PK_Unknown)
return false;
SUnit *SU = MIToSUnit[&MI1];
for (auto I : CurrentPacketMIs) {
SUnit *PacketSU = MIToSUnit.find(I)->second;
if (PacketSU->isSucc(SU)) {
for (unsigned i = 0; i < PacketSU->Succs.size(); ++i) {
auto Dep = PacketSU->Succs[i];
if (Dep.getSUnit() == SU && Dep.getKind() == SDep::Data &&
Hexagon::PredRegsRegClass.contains(Dep.getReg())) {
if (restrictingDepExistInPacket(*I, Dep.getReg()))
return false;
}
}
}
}
unsigned PReg1 = getPredicatedRegister(MI1, HII);
unsigned PReg2 = getPredicatedRegister(MI2, HII);
return PReg1 == PReg2 &&
Hexagon::PredRegsRegClass.contains(PReg1) &&
Hexagon::PredRegsRegClass.contains(PReg2) &&
getPredicateSense(MI1, HII) != getPredicateSense(MI2, HII) &&
HII->isDotNewInst(MI1) == HII->isDotNewInst(MI2);
}
void HexagonPacketizerList::initPacketizerState() {
Dependence = false;
PromotedToDotNew = false;
GlueToNewValueJump = false;
GlueAllocframeStore = false;
FoundSequentialDependence = false;
ChangedOffset = INT64_MAX;
}
bool HexagonPacketizerList::ignorePseudoInstruction(const MachineInstr &MI,
const MachineBasicBlock *) {
if (MI.isDebugInstr())
return true;
if (MI.isCFIInstruction())
return false;
if (MI.isInlineAsm())
return false;
if (MI.isImplicitDef())
return false;
const MCInstrDesc& TID = MI.getDesc();
auto *IS = ResourceTracker->getInstrItins()->beginStage(TID.getSchedClass());
return !IS->getUnits();
}
bool HexagonPacketizerList::isSoloInstruction(const MachineInstr &MI) {
if (MI.isBundle())
return true;
if (MI.isEHLabel() || MI.isCFIInstruction())
return true;
if (MI.isInlineAsm() && !ScheduleInlineAsm)
return true;
if (isSchedBarrier(MI))
return true;
if (HII->isSolo(MI))
return true;
if (MI.getOpcode() == Hexagon::PATCHABLE_FUNCTION_ENTER ||
MI.getOpcode() == Hexagon::PATCHABLE_FUNCTION_EXIT ||
MI.getOpcode() == Hexagon::PATCHABLE_TAIL_CALL)
return true;
if (MI.getOpcode() == Hexagon::A2_nop)
return true;
return false;
}
static bool cannotCoexistAsymm(const MachineInstr &MI, const MachineInstr &MJ,
const HexagonInstrInfo &HII) {
const MachineFunction *MF = MI.getParent()->getParent();
if (MF->getSubtarget<HexagonSubtarget>().hasV60OpsOnly() &&
HII.isHVXMemWithAIndirect(MI, MJ))
return true;
if (MI.mayStore() && HII.isRestrictNoSlot1Store(MJ) && HII.isPureSlot0(MJ))
return true;
if (MI.isInlineAsm())
return MJ.isInlineAsm() || MJ.isBranch() || MJ.isBarrier() ||
MJ.isCall() || MJ.isTerminator();
if (HII.isNewValueStore(MI) && MJ.mayStore())
return true;
switch (MI.getOpcode()) {
case Hexagon::S2_storew_locked:
case Hexagon::S4_stored_locked:
case Hexagon::L2_loadw_locked:
case Hexagon::L4_loadd_locked:
case Hexagon::Y2_dccleana:
case Hexagon::Y2_dccleaninva:
case Hexagon::Y2_dcinva:
case Hexagon::Y2_dczeroa:
case Hexagon::Y4_l2fetch:
case Hexagon::Y5_l2fetch: {
unsigned TJ = HII.getType(MJ);
if (TJ != HexagonII::TypeALU32_2op &&
TJ != HexagonII::TypeALU32_3op &&
TJ != HexagonII::TypeALU32_ADDI)
return true;
break;
}
default:
break;
}
return false;
}
bool HexagonPacketizerList::cannotCoexist(const MachineInstr &MI,
const MachineInstr &MJ) {
return cannotCoexistAsymm(MI, MJ, *HII) || cannotCoexistAsymm(MJ, MI, *HII);
}
void HexagonPacketizerList::unpacketizeSoloInstrs(MachineFunction &MF) {
for (auto &B : MF) {
MachineBasicBlock::iterator BundleIt;
for (MachineInstr &MI : llvm::make_early_inc_range(B.instrs())) {
if (MI.isBundle())
BundleIt = MI.getIterator();
if (!MI.isInsideBundle())
continue;
bool InsertBeforeBundle;
if (MI.isInlineAsm())
InsertBeforeBundle = !hasWriteToReadDep(MI, *BundleIt, HRI);
else if (MI.isDebugValue())
InsertBeforeBundle = true;
else
continue;
BundleIt = moveInstrOut(MI, BundleIt, InsertBeforeBundle);
}
}
}
static bool isSystemInstr(const MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
switch (Opc) {
case Hexagon::Y2_barrier:
case Hexagon::Y2_dcfetchbo:
case Hexagon::Y4_l2fetch:
case Hexagon::Y5_l2fetch:
return true;
}
return false;
}
bool HexagonPacketizerList::hasDeadDependence(const MachineInstr &I,
const MachineInstr &J) {
if (I.isCall() || J.isCall())
return false;
if (HII->isPredicated(I) || HII->isPredicated(J))
return false;
BitVector DeadDefs(Hexagon::NUM_TARGET_REGS);
for (auto &MO : I.operands()) {
if (!MO.isReg() || !MO.isDef() || !MO.isDead())
continue;
DeadDefs[MO.getReg()] = true;
}
for (auto &MO : J.operands()) {
if (!MO.isReg() || !MO.isDef() || !MO.isDead())
continue;
Register R = MO.getReg();
if (R != Hexagon::USR_OVF && DeadDefs[R])
return true;
}
return false;
}
bool HexagonPacketizerList::hasControlDependence(const MachineInstr &I,
const MachineInstr &J) {
if ((HII->isSaveCalleeSavedRegsCall(I) &&
doesModifyCalleeSavedReg(J, HRI)) ||
(HII->isSaveCalleeSavedRegsCall(J) &&
doesModifyCalleeSavedReg(I, HRI)))
return true;
if (isControlFlow(I) && isControlFlow(J))
return true;
auto isBadForLoopN = [this] (const MachineInstr &MI) -> bool {
if (MI.isCall() || HII->isDeallocRet(MI) || HII->isNewValueJump(MI))
return true;
if (HII->isPredicated(MI) && HII->isPredicatedNew(MI) && HII->isJumpR(MI))
return true;
return false;
};
if (HII->isLoopN(I) && isBadForLoopN(J))
return true;
if (HII->isLoopN(J) && isBadForLoopN(I))
return true;
return HII->isDeallocRet(I) &&
(J.isBranch() || J.isCall() || J.isBarrier());
}
bool HexagonPacketizerList::hasRegMaskDependence(const MachineInstr &I,
const MachineInstr &J) {
for (const MachineOperand &OpJ : J.operands()) {
if (!OpJ.isRegMask())
continue;
assert((J.isCall() || HII->isTailCall(J)) && "Regmask on a non-call");
for (const MachineOperand &OpI : I.operands()) {
if (OpI.isReg()) {
if (OpJ.clobbersPhysReg(OpI.getReg()))
return true;
} else if (OpI.isRegMask()) {
return true;
}
}
}
return false;
}
bool HexagonPacketizerList::hasDualStoreDependence(const MachineInstr &I,
const MachineInstr &J) {
bool SysI = isSystemInstr(I), SysJ = isSystemInstr(J);
bool StoreI = I.mayStore(), StoreJ = J.mayStore();
if ((SysI && StoreJ) || (SysJ && StoreI))
return true;
if (StoreI && StoreJ) {
if (HII->isNewValueInst(J) || HII->isMemOp(J) || HII->isMemOp(I))
return true;
} else {
bool MopStI = HII->isMemOp(I) || StoreI;
bool MopStJ = HII->isMemOp(J) || StoreJ;
if (MopStI && MopStJ)
return true;
}
return (StoreJ && HII->isDeallocRet(I)) || (StoreI && HII->isDeallocRet(J));
}
bool HexagonPacketizerList::isLegalToPacketizeTogether(SUnit *SUI, SUnit *SUJ) {
assert(SUI->getInstr() && SUJ->getInstr());
MachineInstr &I = *SUI->getInstr();
MachineInstr &J = *SUJ->getInstr();
if (CurrentPacketMIs.size() == 1)
IgnoreDepMIs.clear();
MachineBasicBlock::iterator II = I.getIterator();
assert(!isSoloInstruction(I) && "Unexpected solo instr!");
if (cannotCoexist(I, J))
return false;
Dependence = hasDeadDependence(I, J) || hasControlDependence(I, J);
if (Dependence)
return false;
Dependence = hasRegMaskDependence(I, J);
if (Dependence)
return false;
Dependence = hasDualStoreDependence(I, J);
if (Dependence)
return false;
MachineBasicBlock::iterator NextMII = I.getIterator();
++NextMII;
if (NextMII != I.getParent()->end() && HII->isNewValueJump(*NextMII)) {
MachineInstr &NextMI = *NextMII;
bool secondRegMatch = false;
const MachineOperand &NOp0 = NextMI.getOperand(0);
const MachineOperand &NOp1 = NextMI.getOperand(1);
if (NOp1.isReg() && I.getOperand(0).getReg() == NOp1.getReg())
secondRegMatch = true;
for (MachineInstr *PI : CurrentPacketMIs) {
if (PI->isCall()) {
Dependence = true;
break;
}
if (PI->getOpcode() == Hexagon::S2_allocframe || PI->mayStore() ||
HII->isLoopN(*PI)) {
Dependence = true;
break;
}
const MachineOperand &OpR = secondRegMatch ? NOp0 : NOp1;
if (OpR.isReg() && PI->modifiesRegister(OpR.getReg(), HRI)) {
Dependence = true;
break;
}
}
GlueToNewValueJump = true;
if (Dependence)
return false;
}
if (!SUJ->isSucc(SUI))
return true;
for (unsigned i = 0; i < SUJ->Succs.size(); ++i) {
if (FoundSequentialDependence)
break;
if (SUJ->Succs[i].getSUnit() != SUI)
continue;
SDep::Kind DepType = SUJ->Succs[i].getKind();
unsigned DepReg = 0;
const TargetRegisterClass *RC = nullptr;
if (DepType == SDep::Data) {
DepReg = SUJ->Succs[i].getReg();
RC = HRI->getMinimalPhysRegClass(DepReg);
}
if (I.isCall() || HII->isJumpR(I) || I.isReturn() || HII->isTailCall(I)) {
if (!isRegDependence(DepType))
continue;
if (!isCallDependent(I, DepType, SUJ->Succs[i].getReg()))
continue;
}
if (DepType == SDep::Data) {
if (canPromoteToDotCur(J, SUJ, DepReg, II, RC))
if (promoteToDotCur(J, DepType, II, RC))
continue;
}
if (DepType == SDep::Data && HII->isDotCurInst(J)) {
if (HII->isHVXVec(I))
continue;
}
if (DepType == SDep::Data) {
if (canPromoteToDotNew(I, SUJ, DepReg, II, RC)) {
if (promoteToDotNew(I, DepType, II, RC)) {
PromotedToDotNew = true;
if (cannotCoexist(I, J))
FoundSequentialDependence = true;
continue;
}
}
if (HII->isNewValueJump(I))
continue;
}
if (HII->isPredicated(I) && HII->isPredicated(J) &&
arePredicatesComplements(I, J)) {
auto Itr = find(IgnoreDepMIs, &J);
if (Itr != IgnoreDepMIs.end()) {
Dependence = true;
return false;
}
IgnoreDepMIs.push_back(&I);
continue;
}
if (isDirectJump(I) && !J.isBranch() && !J.isCall() &&
DepType == SDep::Order)
continue;
if (I.isConditionalBranch() && DepType != SDep::Data &&
DepType != SDep::Output)
continue;
if (DepType == SDep::Output) {
FoundSequentialDependence = true;
break;
}
if (DepType == SDep::Order) {
if (!PacketizeVolatiles) {
bool OrdRefs = I.hasOrderedMemoryRef() || J.hasOrderedMemoryRef();
if (OrdRefs) {
FoundSequentialDependence = true;
break;
}
}
bool LoadJ = J.mayLoad(), StoreJ = J.mayStore();
bool LoadI = I.mayLoad(), StoreI = I.mayStore();
bool NVStoreJ = HII->isNewValueStore(J);
bool NVStoreI = HII->isNewValueStore(I);
bool IsVecJ = HII->isHVXVec(J);
bool IsVecI = HII->isHVXVec(I);
if (LoadJ && LoadI && HII->isPureSlot0(J)) {
FoundSequentialDependence = true;
break;
}
if (Slot1Store && MF.getSubtarget<HexagonSubtarget>().hasV65Ops() &&
((LoadJ && StoreI && !NVStoreI) ||
(StoreJ && LoadI && !NVStoreJ)) &&
(J.getOpcode() != Hexagon::S2_allocframe &&
I.getOpcode() != Hexagon::S2_allocframe) &&
(J.getOpcode() != Hexagon::L2_deallocframe &&
I.getOpcode() != Hexagon::L2_deallocframe) &&
(!HII->isMemOp(J) && !HII->isMemOp(I)) && (!IsVecJ && !IsVecI))
setmemShufDisabled(true);
else
if (StoreJ && LoadI && alias(J, I)) {
FoundSequentialDependence = true;
break;
}
if (!StoreJ)
if (!LoadJ || (!LoadI && !StoreI)) {
FoundSequentialDependence = true;
break;
}
continue;
}
if (DepType == SDep::Data && J.getOpcode() == Hexagon::S2_allocframe) {
unsigned Opc = I.getOpcode();
switch (Opc) {
case Hexagon::S2_storerd_io:
case Hexagon::S2_storeri_io:
case Hexagon::S2_storerh_io:
case Hexagon::S2_storerb_io:
if (I.getOperand(0).getReg() == HRI->getStackRegister()) {
GlueAllocframeStore = useCallersSP(I);
if (GlueAllocframeStore)
continue;
}
break;
default:
break;
}
}
if ((DepType == SDep::Anti || DepType == SDep::Output) && J.isCall()) {
for (const MachineOperand &Op : I.operands()) {
if (Op.isReg() && Op.isDef()) {
Register R = Op.getReg();
if (!J.readsRegister(R, HRI) && !J.modifiesRegister(R, HRI))
continue;
} else if (!Op.isRegMask()) {
continue;
}
FoundSequentialDependence = true;
break;
}
}
if (DepType != SDep::Anti) {
FoundSequentialDependence = true;
break;
}
}
if (FoundSequentialDependence) {
Dependence = true;
return false;
}
return true;
}
bool HexagonPacketizerList::isLegalToPruneDependencies(SUnit *SUI, SUnit *SUJ) {
assert(SUI->getInstr() && SUJ->getInstr());
MachineInstr &I = *SUI->getInstr();
MachineInstr &J = *SUJ->getInstr();
bool Coexist = !cannotCoexist(I, J);
if (Coexist && !Dependence)
return true;
if (PromotedToDotNew)
demoteToDotOld(I);
cleanUpDotCur();
if (GlueAllocframeStore) {
useCalleesSP(I);
GlueAllocframeStore = false;
}
if (ChangedOffset != INT64_MAX)
undoChangedOffset(I);
if (GlueToNewValueJump) {
GlueToNewValueJump = false;
return false;
}
if (!Coexist)
return false;
if (ChangedOffset == INT64_MAX && updateOffset(SUI, SUJ)) {
FoundSequentialDependence = false;
Dependence = false;
return true;
}
return false;
}
bool HexagonPacketizerList::foundLSInPacket() {
bool FoundLoad = false;
bool FoundStore = false;
for (auto MJ : CurrentPacketMIs) {
unsigned Opc = MJ->getOpcode();
if (Opc == Hexagon::S2_allocframe || Opc == Hexagon::L2_deallocframe)
continue;
if (HII->isMemOp(*MJ))
continue;
if (MJ->mayLoad())
FoundLoad = true;
if (MJ->mayStore() && !HII->isNewValueStore(*MJ))
FoundStore = true;
}
return FoundLoad && FoundStore;
}
MachineBasicBlock::iterator
HexagonPacketizerList::addToPacket(MachineInstr &MI) {
MachineBasicBlock::iterator MII = MI.getIterator();
MachineBasicBlock *MBB = MI.getParent();
if (CurrentPacketMIs.empty()) {
PacketStalls = false;
PacketStallCycles = 0;
}
PacketStalls |= producesStall(MI);
PacketStallCycles = std::max(PacketStallCycles, calcStall(MI));
if (MI.isImplicitDef()) {
CurrentPacketMIs.push_back(&MI);
return MII;
}
assert(ResourceTracker->canReserveResources(MI));
bool ExtMI = HII->isExtended(MI) || HII->isConstExtended(MI);
bool Good = true;
if (GlueToNewValueJump) {
MachineInstr &NvjMI = *++MII;
ResourceTracker->reserveResources(MI);
if (ExtMI)
Good = tryAllocateResourcesForConstExt(true);
bool ExtNvjMI = HII->isExtended(NvjMI) || HII->isConstExtended(NvjMI);
if (Good) {
if (ResourceTracker->canReserveResources(NvjMI))
ResourceTracker->reserveResources(NvjMI);
else
Good = false;
}
if (Good && ExtNvjMI)
Good = tryAllocateResourcesForConstExt(true);
if (!Good) {
endPacket(MBB, MI);
assert(ResourceTracker->canReserveResources(MI));
ResourceTracker->reserveResources(MI);
if (ExtMI) {
assert(canReserveResourcesForConstExt());
tryAllocateResourcesForConstExt(true);
}
assert(ResourceTracker->canReserveResources(NvjMI));
ResourceTracker->reserveResources(NvjMI);
if (ExtNvjMI) {
assert(canReserveResourcesForConstExt());
reserveResourcesForConstExt();
}
}
CurrentPacketMIs.push_back(&MI);
CurrentPacketMIs.push_back(&NvjMI);
return MII;
}
ResourceTracker->reserveResources(MI);
if (ExtMI && !tryAllocateResourcesForConstExt(true)) {
endPacket(MBB, MI);
if (PromotedToDotNew)
demoteToDotOld(MI);
if (GlueAllocframeStore) {
useCalleesSP(MI);
GlueAllocframeStore = false;
}
ResourceTracker->reserveResources(MI);
reserveResourcesForConstExt();
}
CurrentPacketMIs.push_back(&MI);
return MII;
}
void HexagonPacketizerList::endPacket(MachineBasicBlock *MBB,
MachineBasicBlock::iterator EndMI) {
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;
}
}
});
bool memShufDisabled = getmemShufDisabled();
if (memShufDisabled && !foundLSInPacket()) {
setmemShufDisabled(false);
LLVM_DEBUG(dbgs() << " Not added to NoShufPacket\n");
}
memShufDisabled = getmemShufDisabled();
OldPacketMIs.clear();
for (MachineInstr *MI : CurrentPacketMIs) {
MachineBasicBlock::instr_iterator NextMI = std::next(MI->getIterator());
for (auto &I : make_range(HII->expandVGatherPseudo(*MI), NextMI))
OldPacketMIs.push_back(&I);
}
CurrentPacketMIs.clear();
if (OldPacketMIs.size() > 1) {
MachineBasicBlock::instr_iterator FirstMI(OldPacketMIs.front());
MachineBasicBlock::instr_iterator LastMI(EndMI.getInstrIterator());
finalizeBundle(*MBB, FirstMI, LastMI);
auto BundleMII = std::prev(FirstMI);
if (memShufDisabled)
HII->setBundleNoShuf(BundleMII);
setmemShufDisabled(false);
}
PacketHasDuplex = false;
PacketHasSLOT0OnlyInsn = false;
ResourceTracker->clearResources();
LLVM_DEBUG(dbgs() << "End packet\n");
}
bool HexagonPacketizerList::shouldAddToPacket(const MachineInstr &MI) {
if (Minimal)
return false;
if (producesStall(MI))
return false;
auto &HST = MI.getParent()->getParent()->getSubtarget<HexagonSubtarget>();
if (HST.isTinyCoreWithDuplex() && CurrentPacketMIs.size() > 0 &&
!PacketHasDuplex) {
for (auto &MJ : CurrentPacketMIs)
PacketHasSLOT0OnlyInsn |= HII->isPureSlot0(*MJ);
int Opcode = HII->getDuplexOpcode(MI, false);
if (Opcode >= 0) {
for (auto &MJ : CurrentPacketMIs) {
if (HII->isDuplexPair(MI, *MJ) && !PacketHasSLOT0OnlyInsn) {
PacketHasDuplex = true;
return true;
}
}
MachineInstr &MIRef = const_cast<MachineInstr &>(MI);
MIRef.setDesc(HII->get(Opcode));
return ResourceTracker->canReserveResources(MIRef);
}
}
return true;
}
unsigned int HexagonPacketizerList::calcStall(const MachineInstr &I) {
if (!OldPacketMIs.empty()) {
auto *OldBB = OldPacketMIs.front()->getParent();
auto *ThisBB = I.getParent();
if (MLI->getLoopFor(OldBB) != MLI->getLoopFor(ThisBB))
return 0;
}
SUnit *SUI = MIToSUnit[const_cast<MachineInstr *>(&I)];
if (!SUI)
return 0;
for (auto J : CurrentPacketMIs) {
SUnit *SUJ = MIToSUnit[J];
for (auto &Pred : SUI->Preds)
if (Pred.getSUnit() == SUJ)
if ((Pred.getLatency() == 0 && Pred.isAssignedRegDep()) ||
HII->isNewValueJump(I) || HII->isToBeScheduledASAP(*J, I))
return 0;
}
for (auto J : OldPacketMIs) {
SUnit *SUJ = MIToSUnit[J];
for (auto &Pred : SUI->Preds)
if (Pred.getSUnit() == SUJ && Pred.getLatency() > 1)
return Pred.getLatency();
}
return 0;
}
bool HexagonPacketizerList::producesStall(const MachineInstr &I) {
unsigned int Latency = calcStall(I);
if (Latency == 0)
return false;
if (PacketStalls)
return Latency > PacketStallCycles;
return true;
}
FunctionPass *llvm::createHexagonPacketizer(bool Minimal) {
return new HexagonPacketizer(Minimal);
}