698 lines
22 KiB
C++
698 lines
22 KiB
C++
|
//===- HexagonShuffler.cpp - Instruction bundle shuffling -----------------===//
|
||
|
//
|
||
|
// 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 implements the shuffling of insns inside a bundle according to the
|
||
|
// packet formation rules of the Hexagon ISA.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#define DEBUG_TYPE "hexagon-shuffle"
|
||
|
|
||
|
#include "MCTargetDesc/HexagonShuffler.h"
|
||
|
#include "MCTargetDesc/HexagonBaseInfo.h"
|
||
|
#include "MCTargetDesc/HexagonMCInstrInfo.h"
|
||
|
#include "MCTargetDesc/HexagonMCTargetDesc.h"
|
||
|
#include "llvm/ADT/SmallVector.h"
|
||
|
#include "llvm/ADT/Twine.h"
|
||
|
#include "llvm/MC/MCContext.h"
|
||
|
#include "llvm/MC/MCInst.h"
|
||
|
#include "llvm/MC/MCInstrDesc.h"
|
||
|
#include "llvm/MC/MCSubtargetInfo.h"
|
||
|
#include "llvm/Support/Compiler.h"
|
||
|
#include "llvm/Support/Debug.h"
|
||
|
#include "llvm/Support/MathExtras.h"
|
||
|
#include "llvm/Support/SourceMgr.h"
|
||
|
#include "llvm/Support/raw_ostream.h"
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
using namespace llvm;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// Insn shuffling priority.
|
||
|
class HexagonBid {
|
||
|
// The priority is directly proportional to how restricted the insn is based
|
||
|
// on its flexibility to run on the available slots. So, the fewer slots it
|
||
|
// may run on, the higher its priority.
|
||
|
enum { MAX = 360360 }; // LCD of 1/2, 1/3, 1/4,... 1/15.
|
||
|
unsigned Bid = 0;
|
||
|
|
||
|
public:
|
||
|
HexagonBid() = default;
|
||
|
HexagonBid(unsigned B) { Bid = B ? MAX / countPopulation(B) : 0; }
|
||
|
|
||
|
// Check if the insn priority is overflowed.
|
||
|
bool isSold() const { return (Bid >= MAX); }
|
||
|
|
||
|
HexagonBid &operator+=(const HexagonBid &B) {
|
||
|
Bid += B.Bid;
|
||
|
return *this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Slot shuffling allocation.
|
||
|
class HexagonUnitAuction {
|
||
|
HexagonBid Scores[HEXAGON_PACKET_SIZE];
|
||
|
// Mask indicating which slot is unavailable.
|
||
|
unsigned isSold : HEXAGON_PACKET_SIZE;
|
||
|
|
||
|
public:
|
||
|
HexagonUnitAuction(unsigned cs = 0) : isSold(cs) {}
|
||
|
|
||
|
// Allocate slots.
|
||
|
bool bid(unsigned B) {
|
||
|
// Exclude already auctioned slots from the bid.
|
||
|
unsigned b = B & ~isSold;
|
||
|
if (b) {
|
||
|
for (unsigned i = 0; i < HEXAGON_PACKET_SIZE; ++i)
|
||
|
if (b & (1 << i)) {
|
||
|
// Request candidate slots.
|
||
|
Scores[i] += HexagonBid(b);
|
||
|
isSold |= Scores[i].isSold() << i;
|
||
|
}
|
||
|
return true;
|
||
|
} else
|
||
|
// Error if the desired slots are already full.
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // end anonymous namespace
|
||
|
|
||
|
unsigned HexagonResource::setWeight(unsigned s) {
|
||
|
const unsigned SlotWeight = 8;
|
||
|
const unsigned MaskWeight = SlotWeight - 1;
|
||
|
unsigned Units = getUnits();
|
||
|
unsigned Key = ((1u << s) & Units) != 0;
|
||
|
|
||
|
// Calculate relative weight of the insn for the given slot, weighing it the
|
||
|
// heavier the more restrictive the insn is and the lowest the slots that the
|
||
|
// insn may be executed in.
|
||
|
if (Key == 0 || Units == 0 || (SlotWeight * s >= 32))
|
||
|
return Weight = 0;
|
||
|
|
||
|
unsigned Ctpop = countPopulation(Units);
|
||
|
unsigned Cttz = countTrailingZeros(Units);
|
||
|
Weight = (1u << (SlotWeight * s)) * ((MaskWeight - Ctpop) << Cttz);
|
||
|
return Weight;
|
||
|
}
|
||
|
|
||
|
HexagonCVIResource::HexagonCVIResource(MCInstrInfo const &MCII,
|
||
|
MCSubtargetInfo const &STI,
|
||
|
unsigned s,
|
||
|
MCInst const *id)
|
||
|
: HexagonResource(s) {
|
||
|
|
||
|
const unsigned ItinUnits = HexagonMCInstrInfo::getCVIResources(MCII, STI, *id);
|
||
|
unsigned Lanes;
|
||
|
const unsigned Units = HexagonConvertUnits(ItinUnits, &Lanes);
|
||
|
|
||
|
if (Units == 0 && Lanes == 0) {
|
||
|
// For core insns.
|
||
|
Valid = false;
|
||
|
setUnits(0);
|
||
|
setLanes(0);
|
||
|
setLoad(false);
|
||
|
setStore(false);
|
||
|
} else {
|
||
|
// For an HVX insn.
|
||
|
Valid = true;
|
||
|
setUnits(Units);
|
||
|
setLanes(Lanes);
|
||
|
setLoad(HexagonMCInstrInfo::getDesc(MCII, *id).mayLoad());
|
||
|
setStore(HexagonMCInstrInfo::getDesc(MCII, *id).mayStore());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct CVIUnits {
|
||
|
unsigned Units;
|
||
|
unsigned Lanes;
|
||
|
};
|
||
|
using HVXInstsT = SmallVector<struct CVIUnits, 8>;
|
||
|
|
||
|
static unsigned makeAllBits(unsigned startBit, unsigned Lanes)
|
||
|
{
|
||
|
for (unsigned i = 1; i < Lanes; ++i)
|
||
|
startBit = (startBit << 1) | startBit;
|
||
|
return startBit;
|
||
|
}
|
||
|
|
||
|
static bool checkHVXPipes(const HVXInstsT &hvxInsts, unsigned startIdx,
|
||
|
unsigned usedUnits) {
|
||
|
if (startIdx < hvxInsts.size()) {
|
||
|
if (!hvxInsts[startIdx].Units)
|
||
|
return checkHVXPipes(hvxInsts, startIdx + 1, usedUnits);
|
||
|
for (unsigned b = 0x1; b <= 0x8; b <<= 1) {
|
||
|
if ((hvxInsts[startIdx].Units & b) == 0)
|
||
|
continue;
|
||
|
unsigned allBits = makeAllBits(b, hvxInsts[startIdx].Lanes);
|
||
|
if ((allBits & usedUnits) == 0) {
|
||
|
if (checkHVXPipes(hvxInsts, startIdx + 1, usedUnits | allBits))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
HexagonShuffler::HexagonShuffler(MCContext &Context, bool ReportErrors,
|
||
|
MCInstrInfo const &MCII,
|
||
|
MCSubtargetInfo const &STI)
|
||
|
: Context(Context), MCII(MCII), STI(STI), ReportErrors(ReportErrors) {
|
||
|
reset();
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::reset() {
|
||
|
Packet.clear();
|
||
|
BundleFlags = 0;
|
||
|
CheckFailure = false;
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::append(MCInst const &ID, MCInst const *Extender,
|
||
|
unsigned S) {
|
||
|
HexagonInstr PI(MCII, STI, &ID, Extender, S);
|
||
|
|
||
|
Packet.push_back(PI);
|
||
|
}
|
||
|
|
||
|
|
||
|
static const unsigned Slot0Mask = 1 << 0;
|
||
|
static const unsigned Slot1Mask = 1 << 1;
|
||
|
static const unsigned Slot3Mask = 1 << 3;
|
||
|
static const unsigned slotSingleLoad = Slot0Mask;
|
||
|
static const unsigned slotSingleStore = Slot0Mask;
|
||
|
|
||
|
void HexagonShuffler::restrictSlot1AOK(HexagonPacketSummary const &Summary) {
|
||
|
if (Summary.Slot1AOKLoc)
|
||
|
for (HexagonInstr &ISJ : insts()) {
|
||
|
MCInst const &Inst = ISJ.getDesc();
|
||
|
const unsigned Type = HexagonMCInstrInfo::getType(MCII, Inst);
|
||
|
if (Type != HexagonII::TypeALU32_2op &&
|
||
|
Type != HexagonII::TypeALU32_3op &&
|
||
|
Type != HexagonII::TypeALU32_ADDI) {
|
||
|
const unsigned Units = ISJ.Core.getUnits();
|
||
|
|
||
|
if (Units & Slot1Mask) {
|
||
|
AppliedRestrictions.push_back(std::make_pair(
|
||
|
Inst.getLoc(),
|
||
|
"Instruction was restricted from being in slot 1"));
|
||
|
AppliedRestrictions.push_back(std::make_pair(
|
||
|
*Summary.Slot1AOKLoc, "Instruction can only be combined "
|
||
|
"with an ALU instruction in slot 1"));
|
||
|
ISJ.Core.setUnits(Units & ~Slot1Mask);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::restrictNoSlot1Store(
|
||
|
HexagonPacketSummary const &Summary) {
|
||
|
// If this packet contains an instruction that bars slot-1 stores,
|
||
|
// we should mask off slot 1 from all of the store instructions in
|
||
|
// this packet.
|
||
|
|
||
|
if (!Summary.NoSlot1StoreLoc)
|
||
|
return;
|
||
|
|
||
|
bool AppliedRestriction = false;
|
||
|
|
||
|
for (HexagonInstr &ISJ : insts()) {
|
||
|
MCInst const &Inst = ISJ.getDesc();
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, Inst).mayStore()) {
|
||
|
unsigned Units = ISJ.Core.getUnits();
|
||
|
if (Units & Slot1Mask) {
|
||
|
AppliedRestriction = true;
|
||
|
AppliedRestrictions.push_back(std::make_pair(
|
||
|
Inst.getLoc(), "Instruction was restricted from being in slot 1"));
|
||
|
ISJ.Core.setUnits(Units & ~Slot1Mask);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (AppliedRestriction)
|
||
|
AppliedRestrictions.push_back(
|
||
|
std::make_pair(*Summary.NoSlot1StoreLoc,
|
||
|
"Instruction does not allow a store in slot 1"));
|
||
|
}
|
||
|
|
||
|
bool HexagonShuffler::applySlotRestrictions(
|
||
|
HexagonPacketSummary const &Summary) {
|
||
|
// These restrictions can modify the slot masks in the instructions
|
||
|
// in the Packet member. They should run unconditionally and their
|
||
|
// order does not matter.
|
||
|
restrictSlot1AOK(Summary);
|
||
|
restrictNoSlot1Store(Summary);
|
||
|
|
||
|
permitNonSlot();
|
||
|
|
||
|
// These restrictions can modify the slot masks in the instructions
|
||
|
// in the Packet member, but they can also detect constraint failures
|
||
|
// which are fatal.
|
||
|
if (!CheckFailure)
|
||
|
restrictStoreLoadOrder(Summary);
|
||
|
if (!CheckFailure)
|
||
|
restrictBranchOrder(Summary);
|
||
|
if (!CheckFailure)
|
||
|
restrictPreferSlot3(Summary);
|
||
|
return !CheckFailure;
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::restrictBranchOrder(HexagonPacketSummary const &Summary) {
|
||
|
// preserve branch order
|
||
|
const bool HasMultipleBranches = Summary.branchInsts.size() > 1;
|
||
|
if (!HasMultipleBranches)
|
||
|
return;
|
||
|
|
||
|
if (Summary.branchInsts.size() > 2) {
|
||
|
reportError(Twine("too many branches in packet"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const static std::pair<unsigned, unsigned> jumpSlots[] = {
|
||
|
{8, 4}, {8, 2}, {8, 1}, {4, 2}, {4, 1}, {2, 1}};
|
||
|
// try all possible choices
|
||
|
for (std::pair<unsigned, unsigned> jumpSlot : jumpSlots) {
|
||
|
// validate first jump with this slot rule
|
||
|
if (!(jumpSlot.first & Summary.branchInsts[0]->Core.getUnits()))
|
||
|
continue;
|
||
|
|
||
|
// validate second jump with this slot rule
|
||
|
if (!(jumpSlot.second & Summary.branchInsts[1]->Core.getUnits()))
|
||
|
continue;
|
||
|
|
||
|
// both valid for this configuration, set new slot rules
|
||
|
const HexagonPacket PacketSave = Packet;
|
||
|
Summary.branchInsts[0]->Core.setUnits(jumpSlot.first);
|
||
|
Summary.branchInsts[1]->Core.setUnits(jumpSlot.second);
|
||
|
|
||
|
const bool HasShuffledPacket = tryAuction(Summary).hasValue();
|
||
|
if (HasShuffledPacket)
|
||
|
return;
|
||
|
|
||
|
// if yes, great, if not then restore original slot mask
|
||
|
// restore original values
|
||
|
Packet = PacketSave;
|
||
|
}
|
||
|
|
||
|
reportError("invalid instruction packet: out of slots");
|
||
|
}
|
||
|
|
||
|
|
||
|
void HexagonShuffler::permitNonSlot() {
|
||
|
for (HexagonInstr &ISJ : insts()) {
|
||
|
const bool RequiresSlot = HexagonMCInstrInfo::requiresSlot(STI, *ISJ.ID);
|
||
|
if (!RequiresSlot)
|
||
|
ISJ.Core.setAllUnits();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool HexagonShuffler::ValidResourceUsage(HexagonPacketSummary const &Summary) {
|
||
|
Optional<HexagonPacket> ShuffledPacket = tryAuction(Summary);
|
||
|
|
||
|
if (!ShuffledPacket) {
|
||
|
reportError("invalid instruction packet: slot error");
|
||
|
return false;
|
||
|
} else {
|
||
|
Packet = *ShuffledPacket;
|
||
|
}
|
||
|
|
||
|
// Verify the CVI slot subscriptions.
|
||
|
llvm::stable_sort(*this, HexagonInstr::lessCVI);
|
||
|
// create vector of hvx instructions to check
|
||
|
HVXInstsT hvxInsts;
|
||
|
hvxInsts.clear();
|
||
|
for (const_iterator I = cbegin(); I != cend(); ++I) {
|
||
|
struct CVIUnits inst;
|
||
|
inst.Units = I->CVI.getUnits();
|
||
|
inst.Lanes = I->CVI.getLanes();
|
||
|
if (inst.Units == 0)
|
||
|
continue; // not an hvx inst or an hvx inst that doesn't uses any pipes
|
||
|
hvxInsts.push_back(inst);
|
||
|
}
|
||
|
|
||
|
// if there are any hvx instructions in this packet, check pipe usage
|
||
|
if (hvxInsts.size() > 0) {
|
||
|
unsigned startIdx, usedUnits;
|
||
|
startIdx = usedUnits = 0x0;
|
||
|
if (!checkHVXPipes(hvxInsts, startIdx, usedUnits)) {
|
||
|
// too many pipes used to be valid
|
||
|
reportError(Twine("invalid instruction packet: slot error"));
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool HexagonShuffler::restrictStoreLoadOrder(
|
||
|
HexagonPacketSummary const &Summary) {
|
||
|
// Modify packet accordingly.
|
||
|
// TODO: need to reserve slots #0 and #1 for duplex insns.
|
||
|
static const unsigned slotFirstLoadStore = Slot1Mask;
|
||
|
static const unsigned slotLastLoadStore = Slot0Mask;
|
||
|
unsigned slotLoadStore = slotFirstLoadStore;
|
||
|
|
||
|
for (iterator ISJ = begin(); ISJ != end(); ++ISJ) {
|
||
|
MCInst const &ID = ISJ->getDesc();
|
||
|
|
||
|
if (!ISJ->Core.getUnits())
|
||
|
// Error if insn may not be executed in any slot.
|
||
|
return false;
|
||
|
|
||
|
// A single load must use slot #0.
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, ID).mayLoad()) {
|
||
|
if (Summary.loads == 1 && Summary.loads == Summary.memory &&
|
||
|
Summary.memops == 0)
|
||
|
// Pin the load to slot #0.
|
||
|
switch (ID.getOpcode()) {
|
||
|
case Hexagon::V6_vgathermw:
|
||
|
case Hexagon::V6_vgathermh:
|
||
|
case Hexagon::V6_vgathermhw:
|
||
|
case Hexagon::V6_vgathermwq:
|
||
|
case Hexagon::V6_vgathermhq:
|
||
|
case Hexagon::V6_vgathermhwq:
|
||
|
// Slot1 only loads
|
||
|
break;
|
||
|
default:
|
||
|
ISJ->Core.setUnits(ISJ->Core.getUnits() & slotSingleLoad);
|
||
|
break;
|
||
|
}
|
||
|
else if (Summary.loads >= 1 && isMemReorderDisabled()) { // }:mem_noshuf
|
||
|
// Loads must keep the original order ONLY if
|
||
|
// isMemReorderDisabled() == true
|
||
|
if (slotLoadStore < slotLastLoadStore) {
|
||
|
// Error if no more slots available for loads.
|
||
|
reportError("invalid instruction packet: too many loads");
|
||
|
return false;
|
||
|
}
|
||
|
// Pin the load to the highest slot available to it.
|
||
|
ISJ->Core.setUnits(ISJ->Core.getUnits() & slotLoadStore);
|
||
|
// Update the next highest slot available to loads.
|
||
|
slotLoadStore >>= 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// A single store must use slot #0.
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, ID).mayStore()) {
|
||
|
if (!Summary.store0) {
|
||
|
const bool PacketHasNoOnlySlot0 =
|
||
|
llvm::none_of(insts(), [&](HexagonInstr const &I) {
|
||
|
return I.Core.getUnits() == Slot0Mask &&
|
||
|
I.ID->getOpcode() != ID.getOpcode();
|
||
|
});
|
||
|
const bool SafeToMoveToSlot0 =
|
||
|
(Summary.loads == 0) ||
|
||
|
(!isMemReorderDisabled() && PacketHasNoOnlySlot0);
|
||
|
|
||
|
if (Summary.stores == 1 && SafeToMoveToSlot0)
|
||
|
// Pin the store to slot #0 only if isMemReorderDisabled() == false
|
||
|
ISJ->Core.setUnits(ISJ->Core.getUnits() & slotSingleStore);
|
||
|
else if (Summary.stores >= 1) {
|
||
|
if (slotLoadStore < slotLastLoadStore) {
|
||
|
// Error if no more slots available for stores.
|
||
|
reportError("invalid instruction packet: too many stores");
|
||
|
return false;
|
||
|
}
|
||
|
// Pin the store to the highest slot available to it.
|
||
|
ISJ->Core.setUnits(ISJ->Core.getUnits() & slotLoadStore);
|
||
|
// Update the next highest slot available to stores.
|
||
|
slotLoadStore >>= 1;
|
||
|
}
|
||
|
}
|
||
|
if (Summary.store1 && Summary.stores > 1) {
|
||
|
// Error if a single store with another store.
|
||
|
reportError("invalid instruction packet: too many stores");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
HexagonShuffler::HexagonPacketSummary HexagonShuffler::GetPacketSummary() {
|
||
|
HexagonPacketSummary Summary = HexagonPacketSummary();
|
||
|
|
||
|
// Collect information from the insns in the packet.
|
||
|
for (iterator ISJ = begin(); ISJ != end(); ++ISJ) {
|
||
|
MCInst const &ID = ISJ->getDesc();
|
||
|
|
||
|
if (HexagonMCInstrInfo::isRestrictSlot1AOK(MCII, ID))
|
||
|
Summary.Slot1AOKLoc = ID.getLoc();
|
||
|
if (HexagonMCInstrInfo::isRestrictNoSlot1Store(MCII, ID))
|
||
|
Summary.NoSlot1StoreLoc = ID.getLoc();
|
||
|
|
||
|
if (HexagonMCInstrInfo::prefersSlot3(MCII, ID)) {
|
||
|
++Summary.pSlot3Cnt;
|
||
|
Summary.PrefSlot3Inst = ISJ;
|
||
|
}
|
||
|
Summary.ReservedSlotMask |=
|
||
|
HexagonMCInstrInfo::getOtherReservedSlots(MCII, STI, ID);
|
||
|
|
||
|
switch (HexagonMCInstrInfo::getType(MCII, ID)) {
|
||
|
case HexagonII::TypeS_2op:
|
||
|
case HexagonII::TypeS_3op:
|
||
|
case HexagonII::TypeALU64:
|
||
|
break;
|
||
|
case HexagonII::TypeJ:
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
break;
|
||
|
case HexagonII::TypeCVI_VM_VP_LDU:
|
||
|
case HexagonII::TypeCVI_VM_LD:
|
||
|
case HexagonII::TypeCVI_VM_TMP_LD:
|
||
|
case HexagonII::TypeCVI_GATHER:
|
||
|
case HexagonII::TypeCVI_GATHER_DV:
|
||
|
case HexagonII::TypeCVI_GATHER_RST:
|
||
|
++Summary.NonZCVIloads;
|
||
|
LLVM_FALLTHROUGH;
|
||
|
case HexagonII::TypeCVI_ZW:
|
||
|
++Summary.AllCVIloads;
|
||
|
LLVM_FALLTHROUGH;
|
||
|
case HexagonII::TypeLD:
|
||
|
++Summary.loads;
|
||
|
++Summary.memory;
|
||
|
if (ISJ->Core.getUnits() == slotSingleLoad ||
|
||
|
HexagonMCInstrInfo::getType(MCII, ID) == HexagonII::TypeCVI_VM_VP_LDU)
|
||
|
++Summary.load0;
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, ID).isReturn())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
break;
|
||
|
case HexagonII::TypeCVI_VM_STU:
|
||
|
case HexagonII::TypeCVI_VM_ST:
|
||
|
case HexagonII::TypeCVI_VM_NEW_ST:
|
||
|
case HexagonII::TypeCVI_SCATTER:
|
||
|
case HexagonII::TypeCVI_SCATTER_DV:
|
||
|
case HexagonII::TypeCVI_SCATTER_RST:
|
||
|
case HexagonII::TypeCVI_SCATTER_NEW_RST:
|
||
|
case HexagonII::TypeCVI_SCATTER_NEW_ST:
|
||
|
++Summary.CVIstores;
|
||
|
LLVM_FALLTHROUGH;
|
||
|
case HexagonII::TypeST:
|
||
|
++Summary.stores;
|
||
|
++Summary.memory;
|
||
|
if (ISJ->Core.getUnits() == slotSingleStore ||
|
||
|
HexagonMCInstrInfo::getType(MCII, ID) == HexagonII::TypeCVI_VM_STU)
|
||
|
++Summary.store0;
|
||
|
break;
|
||
|
case HexagonII::TypeV4LDST:
|
||
|
++Summary.loads;
|
||
|
++Summary.stores;
|
||
|
++Summary.store1;
|
||
|
++Summary.memops;
|
||
|
++Summary.memory;
|
||
|
break;
|
||
|
case HexagonII::TypeNCJ:
|
||
|
++Summary.memory; // NV insns are memory-like.
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
break;
|
||
|
case HexagonII::TypeV2LDST:
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, ID).mayLoad()) {
|
||
|
++Summary.loads;
|
||
|
++Summary.memory;
|
||
|
if (ISJ->Core.getUnits() == slotSingleLoad ||
|
||
|
HexagonMCInstrInfo::getType(MCII, ID) ==
|
||
|
HexagonII::TypeCVI_VM_VP_LDU)
|
||
|
++Summary.load0;
|
||
|
} else {
|
||
|
assert(HexagonMCInstrInfo::getDesc(MCII, ID).mayStore());
|
||
|
++Summary.memory;
|
||
|
++Summary.stores;
|
||
|
}
|
||
|
break;
|
||
|
case HexagonII::TypeCR:
|
||
|
// Legacy conditional branch predicated on a register.
|
||
|
case HexagonII::TypeCJ:
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, ID).isBranch())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
break;
|
||
|
case HexagonII::TypeDUPLEX: {
|
||
|
++Summary.duplex;
|
||
|
MCInst const &Inst0 = *ID.getOperand(0).getInst();
|
||
|
MCInst const &Inst1 = *ID.getOperand(1).getInst();
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, Inst0).isBranch())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, Inst1).isBranch())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, Inst0).isReturn())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
if (HexagonMCInstrInfo::getDesc(MCII, Inst1).isReturn())
|
||
|
Summary.branchInsts.push_back(ISJ);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return Summary;
|
||
|
}
|
||
|
|
||
|
bool HexagonShuffler::ValidPacketMemoryOps(
|
||
|
HexagonPacketSummary const &Summary) const {
|
||
|
// Check if the packet is legal.
|
||
|
const unsigned ZCVIloads = Summary.AllCVIloads - Summary.NonZCVIloads;
|
||
|
const bool ValidHVXMem =
|
||
|
Summary.NonZCVIloads <= 1 && ZCVIloads <= 1 && Summary.CVIstores <= 1;
|
||
|
const bool InvalidPacket =
|
||
|
((Summary.load0 > 1 || Summary.store0 > 1 || !ValidHVXMem) ||
|
||
|
(Summary.duplex > 1 || (Summary.duplex && Summary.memory)));
|
||
|
|
||
|
return !InvalidPacket;
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::restrictPreferSlot3(HexagonPacketSummary const &Summary) {
|
||
|
// flag if an instruction requires to be in slot 3
|
||
|
const bool HasOnlySlot3 = llvm::any_of(insts(), [&](HexagonInstr const &I) {
|
||
|
return (I.Core.getUnits() == Slot3Mask);
|
||
|
});
|
||
|
const bool NeedsPrefSlot3Shuffle =
|
||
|
(Summary.branchInsts.size() <= 1 && !HasOnlySlot3 &&
|
||
|
Summary.pSlot3Cnt == 1 && Summary.PrefSlot3Inst);
|
||
|
|
||
|
if (!NeedsPrefSlot3Shuffle)
|
||
|
return;
|
||
|
|
||
|
HexagonInstr *PrefSlot3Inst = *Summary.PrefSlot3Inst;
|
||
|
// save off slot mask of instruction marked with A_PREFER_SLOT3
|
||
|
// and then pin it to slot #3
|
||
|
const unsigned saveUnits = PrefSlot3Inst->Core.getUnits();
|
||
|
PrefSlot3Inst->Core.setUnits(saveUnits & Slot3Mask);
|
||
|
const bool HasShuffledPacket = tryAuction(Summary).hasValue();
|
||
|
if (HasShuffledPacket)
|
||
|
return;
|
||
|
|
||
|
PrefSlot3Inst->Core.setUnits(saveUnits);
|
||
|
}
|
||
|
|
||
|
/// Check that the packet is legal and enforce relative insn order.
|
||
|
bool HexagonShuffler::check() {
|
||
|
const HexagonPacketSummary Summary = GetPacketSummary();
|
||
|
if (!applySlotRestrictions(Summary))
|
||
|
return false;
|
||
|
|
||
|
if (!ValidPacketMemoryOps(Summary)) {
|
||
|
reportError("invalid instruction packet");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ValidResourceUsage(Summary);
|
||
|
|
||
|
return !CheckFailure;
|
||
|
}
|
||
|
|
||
|
llvm::Optional<HexagonShuffler::HexagonPacket>
|
||
|
HexagonShuffler::tryAuction(HexagonPacketSummary const &Summary) const {
|
||
|
HexagonPacket PacketResult = Packet;
|
||
|
HexagonUnitAuction AuctionCore(Summary.ReservedSlotMask);
|
||
|
llvm::stable_sort(PacketResult, HexagonInstr::lessCore);
|
||
|
|
||
|
const bool ValidSlots =
|
||
|
llvm::all_of(insts(PacketResult), [&AuctionCore](HexagonInstr const &I) {
|
||
|
return AuctionCore.bid(I.Core.getUnits());
|
||
|
});
|
||
|
|
||
|
LLVM_DEBUG(
|
||
|
dbgs() << "Shuffle attempt: " << (ValidSlots ? "passed" : "failed")
|
||
|
<< "\n";
|
||
|
for (HexagonInstr const &ISJ : insts(PacketResult))
|
||
|
dbgs() << "\t" << HexagonMCInstrInfo::getName(MCII, *ISJ.ID) << ": "
|
||
|
<< llvm::format_hex(ISJ.Core.getUnits(), 4, true) << "\n";
|
||
|
);
|
||
|
|
||
|
Optional<HexagonPacket> Res;
|
||
|
if (ValidSlots)
|
||
|
Res = PacketResult;
|
||
|
|
||
|
return Res;
|
||
|
}
|
||
|
|
||
|
bool HexagonShuffler::shuffle() {
|
||
|
if (size() > HEXAGON_PACKET_SIZE) {
|
||
|
// Ignore a packet with with more than what a packet can hold
|
||
|
// or with compound or duplex insns for now.
|
||
|
reportError(Twine("invalid instruction packet"));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check and prepare packet.
|
||
|
bool Ok = true;
|
||
|
if (size() > 1 && (Ok = check()))
|
||
|
// Reorder the handles for each slot.
|
||
|
for (unsigned nSlot = 0, emptySlots = 0; nSlot < HEXAGON_PACKET_SIZE;
|
||
|
++nSlot) {
|
||
|
iterator ISJ, ISK;
|
||
|
unsigned slotSkip, slotWeight;
|
||
|
|
||
|
// Prioritize the handles considering their restrictions.
|
||
|
for (ISJ = ISK = Packet.begin(), slotSkip = slotWeight = 0;
|
||
|
ISK != Packet.end(); ++ISK, ++slotSkip)
|
||
|
if (slotSkip < nSlot - emptySlots)
|
||
|
// Note which handle to begin at.
|
||
|
++ISJ;
|
||
|
else
|
||
|
// Calculate the weight of the slot.
|
||
|
slotWeight += ISK->Core.setWeight(HEXAGON_PACKET_SIZE - nSlot - 1);
|
||
|
|
||
|
if (slotWeight)
|
||
|
// Sort the packet, favoring source order,
|
||
|
// beginning after the previous slot.
|
||
|
std::stable_sort(ISJ, Packet.end());
|
||
|
else
|
||
|
// Skip unused slot.
|
||
|
++emptySlots;
|
||
|
}
|
||
|
|
||
|
LLVM_DEBUG(
|
||
|
for (HexagonInstr const &ISJ : insts()) {
|
||
|
dbgs().write_hex(ISJ.Core.getUnits());
|
||
|
if (ISJ.CVI.isValid()) {
|
||
|
dbgs() << '/';
|
||
|
dbgs().write_hex(ISJ.CVI.getUnits()) << '|';
|
||
|
dbgs() << ISJ.CVI.getLanes();
|
||
|
}
|
||
|
dbgs() << ':'
|
||
|
<< HexagonMCInstrInfo::getDesc(MCII, ISJ.getDesc()).getOpcode()
|
||
|
<< '\n';
|
||
|
} dbgs() << '\n';
|
||
|
);
|
||
|
|
||
|
return Ok;
|
||
|
}
|
||
|
|
||
|
void HexagonShuffler::reportError(Twine const &Msg) {
|
||
|
CheckFailure = true;
|
||
|
if (ReportErrors) {
|
||
|
for (auto const &I : AppliedRestrictions) {
|
||
|
auto SM = Context.getSourceManager();
|
||
|
if (SM)
|
||
|
SM->PrintMessage(I.first, SourceMgr::DK_Note, I.second);
|
||
|
}
|
||
|
Context.reportError(Loc, Msg);
|
||
|
}
|
||
|
}
|