|  | //===- 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. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "MCTargetDesc/HexagonShuffler.h" | 
|  | #include "MCTargetDesc/HexagonBaseInfo.h" | 
|  | #include "MCTargetDesc/HexagonMCInstrInfo.h" | 
|  | #include "MCTargetDesc/HexagonMCTargetDesc.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/ADT/StringExtras.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 <optional> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #define DEBUG_TYPE "hexagon-shuffle" | 
|  |  | 
|  | 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 / llvm::popcount(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 = llvm::popcount(Units); | 
|  | unsigned Cttz = llvm::countr_zero(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), BundleFlags(), MCII(MCII), STI(STI), | 
|  | ReportErrors(ReportErrors), CheckFailure() { | 
|  | 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, | 
|  | const bool DoShuffle) { | 
|  | // 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, DoShuffle); | 
|  | 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).has_value(); | 
|  | if (HasShuffledPacket) | 
|  | return; | 
|  |  | 
|  | // if yes, great, if not then restore original slot mask | 
|  | // restore original values | 
|  | Packet = PacketSave; | 
|  | } | 
|  |  | 
|  | reportResourceError(Summary, "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) { | 
|  | std::optional<HexagonPacket> ShuffledPacket = tryAuction(Summary); | 
|  |  | 
|  | if (!ShuffledPacket) { | 
|  | reportResourceError(Summary, "slot error"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify the CVI slot subscriptions. | 
|  | llvm::stable_sort(*ShuffledPacket, HexagonInstr::lessCVI); | 
|  | // create vector of hvx instructions to check | 
|  | HVXInstsT hvxInsts; | 
|  | hvxInsts.clear(); | 
|  | for (const auto &I : *ShuffledPacket) { | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | Packet = *ShuffledPacket; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | static std::string SlotMaskToText(unsigned SlotMask) { | 
|  | SmallVector<std::string, HEXAGON_PRESHUFFLE_PACKET_SIZE> Slots; | 
|  | for (unsigned SlotNum = 0; SlotNum < HEXAGON_PACKET_SIZE; SlotNum++) | 
|  | if ((SlotMask & (1 << SlotNum)) != 0) | 
|  | Slots.push_back(utostr(SlotNum)); | 
|  |  | 
|  | return llvm::join(Slots, StringRef(", ")); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | const unsigned ReservedSlots = | 
|  | HexagonMCInstrInfo::getOtherReservedSlots(MCII, STI, ID); | 
|  | Summary.ReservedSlotMask |= ReservedSlots; | 
|  | if (ReservedSlots != 0) | 
|  | AppliedRestrictions.push_back(std::make_pair(ID.getLoc(), | 
|  | (Twine("Instruction has reserved slots: ") + | 
|  | SlotMaskToText(ReservedSlots)).str())); | 
|  |  | 
|  | switch (HexagonMCInstrInfo::getType(MCII, ID)) { | 
|  | case HexagonII::TypeS_2op: | 
|  | case HexagonII::TypeS_3op: | 
|  | case HexagonII::TypeALU64: | 
|  | break; | 
|  | case HexagonII::TypeJ: | 
|  | if (HexagonMCInstrInfo::IsABranchingInst(MCII, STI, *ISJ->ID)) | 
|  | 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; | 
|  | [[fallthrough]]; | 
|  | case HexagonII::TypeCVI_ZW: | 
|  | ++Summary.AllCVIloads; | 
|  | [[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; | 
|  | [[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, | 
|  | const bool DoShuffle) { | 
|  | // 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 && DoShuffle; | 
|  |  | 
|  | 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).has_value(); | 
|  | if (HasShuffledPacket) | 
|  | return; | 
|  |  | 
|  | PrefSlot3Inst->Core.setUnits(saveUnits); | 
|  | } | 
|  |  | 
|  | /// Check that the packet is legal and enforce relative insn order. | 
|  | bool HexagonShuffler::check(const bool RequireShuffle) { | 
|  | const HexagonPacketSummary Summary = GetPacketSummary(); | 
|  | if (!applySlotRestrictions(Summary, RequireShuffle)) | 
|  | return false; | 
|  |  | 
|  | if (!ValidPacketMemoryOps(Summary)) { | 
|  | reportError("invalid instruction packet"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (RequireShuffle) | 
|  | ValidResourceUsage(Summary); | 
|  |  | 
|  | return !CheckFailure; | 
|  | } | 
|  |  | 
|  | std::optional<HexagonShuffler::HexagonPacket> | 
|  | HexagonShuffler::tryAuction(HexagonPacketSummary const &Summary) { | 
|  | 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"; | 
|  | ); | 
|  |  | 
|  | std::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("invalid instruction packet"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Check and prepare packet. | 
|  | bool Ok = check(); | 
|  | if (size() > 1 && Ok) | 
|  | // 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::reportResourceError(HexagonPacketSummary const &Summary, StringRef Err) { | 
|  | if (ReportErrors) | 
|  | reportResourceUsage(Summary); | 
|  | reportError(Twine("invalid instruction packet: ") + Err); | 
|  | } | 
|  |  | 
|  |  | 
|  | void HexagonShuffler::reportResourceUsage(HexagonPacketSummary const &Summary) { | 
|  | auto SM = Context.getSourceManager(); | 
|  | if (SM) { | 
|  | for (HexagonInstr const &I : insts()) { | 
|  | const unsigned Units = I.Core.getUnits(); | 
|  |  | 
|  | if (HexagonMCInstrInfo::requiresSlot(STI, *I.ID)) { | 
|  | const std::string UnitsText = Units ? SlotMaskToText(Units) : "<None>"; | 
|  | SM->PrintMessage(I.ID->getLoc(), SourceMgr::DK_Note, | 
|  | Twine("Instruction can utilize slots: ") + | 
|  | UnitsText); | 
|  | } | 
|  | else if (!HexagonMCInstrInfo::isImmext(*I.ID)) | 
|  | SM->PrintMessage(I.ID->getLoc(), SourceMgr::DK_Note, | 
|  | "Instruction does not require a slot"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  | } |