Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/include/llvm/Target/CodeGenFormat.td
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class InstSlot<string slotname, int Size, bit isDefault=false> : Operand<OtherV
// Namespace.
bit isDefaultSlot = isDefault;

// Mark a slot as artificial. This will exclude it from any resource
// estimations.
bit Artificial = false;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for e.g. nop slots that are just added to drive format selection, but don't occupy existing slots.


let DecoderMethod = "decode" # SlotName # "Slot";
}

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AIE/AIE2PSlotInclude.td
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ let Namespace = "AIE2P" in
// 64 bit one.
def nop_slot : InstSlot<"Nop", 1> {
let FieldToFind = "nop";
let Artificial = true;
}
}
1 change: 1 addition & 0 deletions llvm/lib/Target/AIE/AIE2Slots.td
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ let Namespace = "AIE2" in
// 64 bit one.
def nop_slot : InstSlot<"Nop", 1> {
let FieldToFind = "nop";
let Artificial = true;
}
}

Expand Down
15 changes: 11 additions & 4 deletions llvm/lib/Target/AIE/AIEBundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,17 @@ template <class I> class Bundle {
// Verify there is a format that can accommodate the new slots
MCSlotKind Slot = FormatInterface->getSlotKind(InstOpCode);
assert(Slot != MCSlotKind());
SlotBits NewSlots = FormatInterface->getSlotInfo(Slot)->getSlotSet();
return (OccupiedSlots & NewSlots) == 0 &&
FormatInterface->getPacketFormats().getFormat(OccupiedSlots |
NewSlots);

auto *SlotInfo = FormatInterface->getSlotInfo(Slot);
// ConflictBits is a fast predictor of missing formats
SlotBits ConflictBits = SlotInfo->getConflictSet();
if (OccupiedSlots & ConflictBits) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't the OccupiedSlots be the ConflictBits of a Bundle?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all your comments are covered by this one answer. We keep Slots since they represent the true base class from which the instructions derive. Each instruction has exactly one slot. These are also the slots that are listed in the ISA, in the formats, they define the operands of the format instructions, etc.
The conflicts have no such direct interpretation. I use them for which they were designed, which is as an easy way to detect conflicts given the regular slot occupation bits without scanning the format table.
They end up in FuncUnitWrapper since that is at the heart of the conflict query interface. I cannot enter them in the ScoreBoard, since both X and M would set the XM bit, which would not allow X to be in the same bundle as M.

return false;
}
const SlotBits NewSlots = OccupiedSlots | SlotInfo->getSlotSet();
// Note: Now that we have the conflict bits we may no longer need this
// final check, but it is cheap and represents proven technology.
return FormatInterface->isFormatAvailable(NewSlots);
}

/// Add an instruction to the bundle
Expand Down
46 changes: 33 additions & 13 deletions llvm/lib/Target/AIE/AIEHazardRecognizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ void FuncUnitWrapper::setFormatInterface(const AIEBaseMCFormats *Formats) {

bool FuncUnitWrapper::operator==(const FuncUnitWrapper &Other) const {
return Required == Other.Required && Reserved == Other.Reserved &&
Slots == Other.Slots && MemoryBanks == Other.MemoryBanks &&
Slots == Other.Slots && Conflicts == Other.Conflicts &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to check slots and conflicts separately?

MemoryBanks == Other.MemoryBanks &&
MemObjectsBits == Other.MemObjectsBits;
}

Expand Down Expand Up @@ -95,6 +96,7 @@ void FuncUnitWrapper::clearResources() {
Required.clear();
Reserved.clear();
Slots = 0;
Conflicts = 0;
MemoryBanks = 0;
MemObjectsBits = 0;
}
Expand All @@ -117,6 +119,7 @@ FuncUnitWrapper &FuncUnitWrapper::operator|=(const FuncUnitWrapper &Other) {
Required |= Other.Required;
Reserved |= Other.Reserved;
Slots |= Other.Slots;
Conflicts |= Other.Conflicts;
MemoryBanks |= Other.MemoryBanks;
MemObjectsBits |= Other.MemObjectsBits;
return *this;
Expand All @@ -125,6 +128,7 @@ FuncUnitWrapper &FuncUnitWrapper::operator|=(const FuncUnitWrapper &Other) {
bool FuncUnitWrapper::conflict(const FuncUnitWrapper &Other) const {
if ((Slots & Other.Slots) != 0 || (MemoryBanks & Other.MemoryBanks) != 0 ||
(MemObjectsBits & Other.MemObjectsBits) != 0 ||
(Conflicts & Other.Slots) != 0 || (Slots & Other.Conflicts) != 0 ||
Required.overlap(Other.Required) || Reserved.overlap(Other.Required) ||
Required.overlap(Other.Reserved)) {

Expand All @@ -135,7 +139,7 @@ bool FuncUnitWrapper::conflict(const FuncUnitWrapper &Other) const {
// This allows representing a blocked cycle (Slots = ~0) without knowing
// the slot and format details.
return Slots && Other.Slots &&
!FormatInterface->getPacketFormats().getFormat(Slots | Other.Slots);
!FormatInterface->isFormatAvailable(Slots | Other.Slots);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why aren't we checking conflict bits here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are, at line 131, returning early if we find a conflict.

}

namespace {
Expand Down Expand Up @@ -482,6 +486,15 @@ static SlotBits getSlotSet(const MCInstrDesc &Desc,
return IgnoreUnkownSlotSets ? 0 : ~0;
}

static SlotBits getConflictSet(const MCInstrDesc &Desc,
const AIEBaseMCFormats &Formats) {
MCSlotKind SlotKind = Formats.getSlotKind(Desc.getOpcode());
if (SlotKind != MCSlotKind())
return Formats.getSlotInfo(SlotKind)->getConflictSet();

return 0;
}

namespace {
auto toHazardType(bool Conflict) {
return Conflict ? ScheduleHazardRecognizer::NoopHazard
Expand Down Expand Up @@ -525,8 +538,9 @@ ScheduleHazardRecognizer::HazardType AIEHazardRecognizer::getHazardType(
return toHazardType(checkConflict(
TheScoreboard, ItinData, SchedClass,
getSlotSet(Desc, *TII->getFormatInterface(), IgnoreUnknownSlotSets),
MemoryBanks, MemObjectsBits, TII->getMemoryCycles(SchedClass),
DeltaCycles, FUDepthLimit));
getConflictSet(Desc, *TII->getFormatInterface()), MemoryBanks,
MemObjectsBits, TII->getMemoryCycles(SchedClass), DeltaCycles,
FUDepthLimit));
}

bool AIEHazardRecognizer::checkConflict(
Expand All @@ -540,25 +554,28 @@ bool AIEHazardRecognizer::checkConflict(
return checkConflict(
Scoreboard, ItinData, SchedClass,
getSlotSet(Desc, *TII->getFormatInterface(), IgnoreUnknownSlotSets),
MemoryBanks, MemObjectsBits, TII->getMemoryCycles(SchedClass),
DeltaCycles, std::nullopt);
getConflictSet(Desc, *TII->getFormatInterface()), MemoryBanks,
MemObjectsBits, TII->getMemoryCycles(SchedClass), DeltaCycles,
std::nullopt);
}

bool AIEHazardRecognizer::checkConflict(
const ResourceScoreboard<FuncUnitWrapper> &Scoreboard,
const InstrItineraryData *ItinData, unsigned SchedClass, SlotBits SlotSet,
MemoryBankBits MemoryBanks, MemoryObjectsBits MemObjectsBits,
SmallVector<int, 2> MemoryAccessCycles, int DeltaCycles,
std::optional<int> FUDepthLimit) {
SlotBits ConflictSet, MemoryBankBits MemoryBanks,
MemoryObjectsBits MemObjectsBits, SmallVector<int, 2> MemoryAccessCycles,
int DeltaCycles, std::optional<int> FUDepthLimit) {

// Verify format hazards
FuncUnitWrapper EmissionCycle(SlotSet);
FuncUnitWrapper EmissionCycle(SlotSet, ConflictSet);
if (EmissionCycle.conflict(Scoreboard[DeltaCycles]))
return true;

// Verify memory bank and shared object hazards
if (!MemoryAccessCycles.empty()) {
FuncUnitWrapper MemoryAccessCycle(/*SlotSet=*/0, MemoryBanks,
const SlotBits Slots = 0;
const SlotBits Conflicts = 0;
FuncUnitWrapper MemoryAccessCycle(Slots, Conflicts, MemoryBanks,
MemObjectsBits);

for (auto Cycles : MemoryAccessCycles) {
Expand Down Expand Up @@ -644,13 +661,16 @@ void AIEHazardRecognizer::enterResources(
std::optional<int> FUDepthLimit) {

// Append slot usage
FuncUnitWrapper EmissionCycle(SlotSet);
const SlotBits Conflicts = 0;
FuncUnitWrapper EmissionCycle(SlotSet, Conflicts);
Scoreboard[DeltaCycles] |= EmissionCycle;

// Append memory bank usage
if (!MemoryAccessCycles.empty()) {
const SlotBits Slots = 0;
const SlotBits Conflicts = 0;
FuncUnitWrapper MemoryBankAndObjectsAccessCycle(
/*SlotSet=*/0, MemoryBanks, MemObjectsBits);
Slots, Conflicts, MemoryBanks, MemObjectsBits);
for (auto Cycles : MemoryAccessCycles) {
assert(Scoreboard.isInRange(DeltaCycles + Cycles - 1));
Scoreboard[DeltaCycles + Cycles - 1] |= MemoryBankAndObjectsAccessCycle;
Expand Down
17 changes: 11 additions & 6 deletions llvm/lib/Target/AIE/AIEHazardRecognizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class FuncUnitWrapper {

/// The occupied slots. This is currently redundant with Bundle
SlotBits Slots = 0;
/// Conflicts are just for speeding up conflict detection. They may be present
/// in cycles to be merged, but they will not be merged into the scoreboard.
SlotBits Conflicts = 0;

/// The occupied bank
MemoryBankBits MemoryBanks = 0;
Expand Down Expand Up @@ -105,10 +108,11 @@ class FuncUnitWrapper {
/// Make this conflict with any non-empty cycle
void blockResources();
FuncUnitWrapper() = default;
FuncUnitWrapper(SlotBits Slots, MemoryBankBits MemoryBanks = 0,
FuncUnitWrapper(SlotBits Slots, SlotBits Conflicts,
MemoryBankBits MemoryBanks = 0,
MemoryObjectsBits MemObjectsBits = 0)
: Slots(Slots), MemoryBanks(MemoryBanks), MemObjectsBits(MemObjectsBits) {
}
: Slots(Slots), Conflicts(Conflicts), MemoryBanks(MemoryBanks),
MemObjectsBits(MemObjectsBits) {}

/// Compare two FuncUnitWrappers for equality. This is only used for
/// dumping purposes, quite literally saying "this looks the same"
Expand Down Expand Up @@ -268,9 +272,10 @@ class AIEHazardRecognizer : public ScheduleHazardRecognizer {
static bool
checkConflict(const ResourceScoreboard<FuncUnitWrapper> &Scoreboard,
const InstrItineraryData *ItinData, unsigned SchedClass,
SlotBits SlotSet, MemoryBankBits MemoryBanks,
uint64_t MemObjectsBits, SmallVector<int, 2> MemoryAccessCycles,
int DeltaCycles, std::optional<int> FUDepthLimit);
SlotBits SlotSet, SlotBits Conflicts,
MemoryBankBits MemoryBanks, uint64_t MemObjectsBits,
SmallVector<int, 2> MemoryAccessCycles, int DeltaCycles,
std::optional<int> FUDepthLimit);

static void enterResources(ResourceScoreboard<FuncUnitWrapper> &Scoreboard,
const InstrItineraryData *ItinData,
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AIE/MCTargetDesc/AIE2MCFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const MCFormatDesc *AIE2MCFormats::getMCFormats() const {

const PacketFormats &AIE2MCFormats::getPacketFormats() const { return Formats; }

ArrayRef<bool> AIE2MCFormats::getIsFormatAvailable() const {
return FormatAvailable;
}

SmallVector<MCSlotKind, 2> AIE2MCFormats::getLoadSlotKinds() const {
return {MCSlotKind::AIE2_SLOT_LDB, MCSlotKind::AIE2_SLOT_LDA};
}
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/AIE/MCTargetDesc/AIEBaseMCFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ bool AIEBaseMCFormats::isSupportedInstruction(unsigned int Opcode) const {
return getFormatDescIndex(Opcode) ? true : false;
}

bool AIEBaseMCFormats::isFormatAvailable(uint64_t SlotSet) const {
ArrayRef<bool> IsFormatsAvailable = getIsFormatAvailable();
return SlotSet < IsFormatsAvailable.size() && IsFormatsAvailable[SlotSet];
}

const MCSlotKind AIEBaseMCFormats::getSlotKind(unsigned int Opcode) const {
// First, we check that the instruction has a format defined.
// Some KILLs instructions are still in the pipeline for example...
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AIE/MCTargetDesc/AIEMCFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,8 @@ const MCFormatDesc *AIEMCFormats::getMCFormats() const { return AIE::Formats; }

const PacketFormats &AIEMCFormats::getPacketFormats() const { return Formats; }

ArrayRef<bool> AIEMCFormats::getIsFormatAvailable() const {
return FormatAvailable;
}

} // end namespace llvm
22 changes: 17 additions & 5 deletions llvm/lib/Target/AIE/MCTargetDesc/AIEMCFormats.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// (c) Copyright 2023-2024 Advanced Micro Devices, Inc. or its affiliates
// (c) Copyright 2023-2025 Advanced Micro Devices, Inc. or its affiliates
//
//===----------------------------------------------------------------------===//
// Utility classes to interface the generated Formats from CodeGenFormat with
Expand Down Expand Up @@ -93,18 +93,23 @@ class MCSlotInfo {
const char *SlotName;
/// Size of the slot (in bits)
const unsigned Size;
/// Bitset representing the occupancy of the slot
/// Bitset representing the occupancy of the slots
const SlotBits SlotOccupancy;
/// Opcode of the NOP inst. attached to the slot
/// The closure of SlotOccupancy with the computed exclusions,
/// e.g. XM implies X and M
const SlotBits ConflictBits;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a new SlotBits?
Could we not have a unified SlotOccupancy where XM is properly mapped to X and M occupation?

/// Opcode of the NOP instruction attached to the slot
const unsigned NopOpc;

public:
constexpr MCSlotInfo(const char *SlotName, unsigned Size, SlotBits Bits,
unsigned NopOpc)
: SlotName(SlotName), Size(Size), SlotOccupancy(Bits), NopOpc(NopOpc) {}
SlotBits ConflictBits, unsigned NopOpc)
: SlotName(SlotName), Size(Size), SlotOccupancy(Bits),
ConflictBits(ConflictBits), NopOpc(NopOpc) {}

const char *getName() const { return SlotName; }
SlotBits getSlotSet() const { return SlotOccupancy; }
SlotBits getConflictSet() const { return ConflictBits; }
unsigned getNOPOpcode() const { return NopOpc; }
unsigned getSize() const { return Size; }
};
Expand Down Expand Up @@ -384,11 +389,15 @@ class AIEBaseMCFormats {

virtual const PacketFormats &getPacketFormats() const = 0;

virtual ArrayRef<bool> getIsFormatAvailable() const = 0;

// \return all Slots that correspond to the load instructions
virtual SmallVector<MCSlotKind, 2> getLoadSlotKinds() const {
llvm_unreachable("Target didn't implement getLoadSlotKinds()");
}

bool isFormatAvailable(uint64_t SlotSet) const;

protected:
/// Check if the Instruction is indeed into the Tables.
void checkInstructionIsSupported(unsigned int Opcode) const;
Expand All @@ -402,6 +411,7 @@ class AIEMCFormats : public AIEBaseMCFormats {
getFormatDescIndex(unsigned int Opcode) const override;
const MCSlotInfo *getSlotInfo(const MCSlotKind Kind) const override;
const MCFormatDesc *getMCFormats() const override;
ArrayRef<bool> getIsFormatAvailable() const override;
const PacketFormats &getPacketFormats() const override;
};

Expand All @@ -414,6 +424,7 @@ class AIE2MCFormats : public AIEBaseMCFormats {
const MCSlotInfo *getSlotInfo(const MCSlotKind Kind) const override;
const MCFormatDesc *getMCFormats() const override;
const PacketFormats &getPacketFormats() const override;
ArrayRef<bool> getIsFormatAvailable() const override;
SmallVector<MCSlotKind, 2> getLoadSlotKinds() const override;
};

Expand All @@ -426,6 +437,7 @@ class AIE2PMCFormats : public AIEBaseMCFormats {
const MCSlotInfo *getSlotInfo(const MCSlotKind Kind) const override;
const MCFormatDesc *getMCFormats() const override;
const PacketFormats &getPacketFormats() const override;
ArrayRef<bool> getIsFormatAvailable() const override;
SmallVector<MCSlotKind, 2> getLoadSlotKinds() const override;
};

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AIE/MCTargetDesc/aie2p/AIE2PMCFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const PacketFormats &AIE2PMCFormats::getPacketFormats() const {
return Formats;
}

ArrayRef<bool> AIE2PMCFormats::getIsFormatAvailable() const {
return FormatAvailable;
}

SmallVector<MCSlotKind, 2> AIE2PMCFormats::getLoadSlotKinds() const {
return {MCSlotKind::AIE2P_SLOT_LDB, MCSlotKind::AIE2P_SLOT_LDA};
}
Expand Down
Loading