| 1 | //=- llvm/CodeGen/DFAPacketizer.cpp - DFA Packetizer for VLIW -*- C++ -*-=====// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // This class implements a deterministic finite automaton (DFA) based |
| 9 | // packetizing mechanism for VLIW architectures. It provides APIs to |
| 10 | // determine whether there exists a legal mapping of instructions to |
| 11 | // functional unit assignments in a packet. The DFA is auto-generated from |
| 12 | // the target's Schedule.td file. |
| 13 | // |
| 14 | // A DFA consists of 3 major elements: states, inputs, and transitions. For |
| 15 | // the packetizing mechanism, the input is the set of instruction classes for |
| 16 | // a target. The state models all possible combinations of functional unit |
| 17 | // consumption for a given set of instructions in a packet. A transition |
| 18 | // models the addition of an instruction to a packet. In the DFA constructed |
| 19 | // by this class, if an instruction can be added to a packet, then a valid |
| 20 | // transition exists from the corresponding state. Invalid transitions |
| 21 | // indicate that the instruction cannot be added to the current packet. |
| 22 | // |
| 23 | //===----------------------------------------------------------------------===// |
| 24 | |
| 25 | #include "llvm/CodeGen/DFAPacketizer.h" |
| 26 | #include "llvm/ADT/StringExtras.h" |
| 27 | #include "llvm/Analysis/AliasAnalysis.h" |
| 28 | #include "llvm/CodeGen/MachineFunction.h" |
| 29 | #include "llvm/CodeGen/MachineInstr.h" |
| 30 | #include "llvm/CodeGen/MachineInstrBundle.h" |
| 31 | #include "llvm/CodeGen/ScheduleDAG.h" |
| 32 | #include "llvm/CodeGen/TargetInstrInfo.h" |
| 33 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
| 34 | #include "llvm/MC/MCInstrDesc.h" |
| 35 | #include "llvm/Support/CommandLine.h" |
| 36 | #include "llvm/Support/Debug.h" |
| 37 | #include "llvm/Support/raw_ostream.h" |
| 38 | #include <algorithm> |
| 39 | #include <cassert> |
| 40 | #include <iterator> |
| 41 | #include <memory> |
| 42 | #include <vector> |
| 43 | |
| 44 | using namespace llvm; |
| 45 | |
| 46 | #define DEBUG_TYPE "packets" |
| 47 | |
| 48 | static cl::opt<unsigned> InstrLimit("dfa-instr-limit" , cl::Hidden, |
| 49 | cl::init(Val: 0), cl::desc("If present, stops packetizing after N instructions" )); |
| 50 | |
| 51 | static unsigned InstrCount = 0; |
| 52 | |
| 53 | // Check if the resources occupied by a MCInstrDesc are available in the |
| 54 | // current state. |
| 55 | bool DFAPacketizer::canReserveResources(const MCInstrDesc *MID) { |
| 56 | unsigned Action = ItinActions[MID->getSchedClass()]; |
| 57 | if (MID->getSchedClass() == 0 || Action == 0) |
| 58 | return false; |
| 59 | return A.canAdd(A: Action); |
| 60 | } |
| 61 | |
| 62 | // Reserve the resources occupied by a MCInstrDesc and change the current |
| 63 | // state to reflect that change. |
| 64 | void DFAPacketizer::reserveResources(const MCInstrDesc *MID) { |
| 65 | unsigned Action = ItinActions[MID->getSchedClass()]; |
| 66 | if (MID->getSchedClass() == 0 || Action == 0) |
| 67 | return; |
| 68 | A.add(A: Action); |
| 69 | } |
| 70 | |
| 71 | // Check if the resources occupied by a machine instruction are available |
| 72 | // in the current state. |
| 73 | bool DFAPacketizer::canReserveResources(MachineInstr &MI) { |
| 74 | const MCInstrDesc &MID = MI.getDesc(); |
| 75 | return canReserveResources(MID: &MID); |
| 76 | } |
| 77 | |
| 78 | // Reserve the resources occupied by a machine instruction and change the |
| 79 | // current state to reflect that change. |
| 80 | void DFAPacketizer::reserveResources(MachineInstr &MI) { |
| 81 | const MCInstrDesc &MID = MI.getDesc(); |
| 82 | reserveResources(MID: &MID); |
| 83 | } |
| 84 | |
| 85 | unsigned DFAPacketizer::getUsedResources(unsigned InstIdx) { |
| 86 | ArrayRef<NfaPath> NfaPaths = A.getNfaPaths(); |
| 87 | assert(!NfaPaths.empty() && "Invalid bundle!" ); |
| 88 | const NfaPath &RS = NfaPaths.front(); |
| 89 | |
| 90 | // RS stores the cumulative resources used up to and including the I'th |
| 91 | // instruction. The 0th instruction is the base case. |
| 92 | if (InstIdx == 0) |
| 93 | return RS[0]; |
| 94 | // Return the difference between the cumulative resources used by InstIdx and |
| 95 | // its predecessor. |
| 96 | return RS[InstIdx] ^ RS[InstIdx - 1]; |
| 97 | } |
| 98 | |
| 99 | DefaultVLIWScheduler::DefaultVLIWScheduler(MachineFunction &MF, |
| 100 | MachineLoopInfo &MLI, |
| 101 | AAResults *AA) |
| 102 | : ScheduleDAGInstrs(MF, &MLI), AA(AA) { |
| 103 | CanHandleTerminators = true; |
| 104 | } |
| 105 | |
| 106 | /// Apply each ScheduleDAGMutation step in order. |
| 107 | void DefaultVLIWScheduler::postProcessDAG() { |
| 108 | for (auto &M : Mutations) |
| 109 | M->apply(DAG: this); |
| 110 | } |
| 111 | |
| 112 | void DefaultVLIWScheduler::schedule() { |
| 113 | // Build the scheduling graph. |
| 114 | buildSchedGraph(AA); |
| 115 | postProcessDAG(); |
| 116 | } |
| 117 | |
| 118 | VLIWPacketizerList::VLIWPacketizerList(MachineFunction &mf, |
| 119 | MachineLoopInfo &mli, AAResults *aa) |
| 120 | : MF(mf), TII(mf.getSubtarget().getInstrInfo()), AA(aa) { |
| 121 | ResourceTracker = TII->CreateTargetScheduleState(MF.getSubtarget()); |
| 122 | ResourceTracker->setTrackResources(true); |
| 123 | VLIWScheduler = new DefaultVLIWScheduler(MF, mli, AA); |
| 124 | } |
| 125 | |
| 126 | VLIWPacketizerList::~VLIWPacketizerList() { |
| 127 | delete VLIWScheduler; |
| 128 | delete ResourceTracker; |
| 129 | } |
| 130 | |
| 131 | // End the current packet, bundle packet instructions and reset DFA state. |
| 132 | void VLIWPacketizerList::endPacket(MachineBasicBlock *MBB, |
| 133 | MachineBasicBlock::iterator MI) { |
| 134 | LLVM_DEBUG({ |
| 135 | if (!CurrentPacketMIs.empty()) { |
| 136 | dbgs() << "Finalizing packet:\n" ; |
| 137 | unsigned Idx = 0; |
| 138 | for (MachineInstr *MI : CurrentPacketMIs) { |
| 139 | unsigned R = ResourceTracker->getUsedResources(Idx++); |
| 140 | dbgs() << " * [res:0x" << utohexstr(R) << "] " << *MI; |
| 141 | } |
| 142 | } |
| 143 | }); |
| 144 | if (CurrentPacketMIs.size() > 1) { |
| 145 | MachineInstr &MIFirst = *CurrentPacketMIs.front(); |
| 146 | finalizeBundle(MBB&: *MBB, FirstMI: MIFirst.getIterator(), LastMI: MI.getInstrIterator()); |
| 147 | } |
| 148 | CurrentPacketMIs.clear(); |
| 149 | ResourceTracker->clearResources(); |
| 150 | LLVM_DEBUG(dbgs() << "End packet\n" ); |
| 151 | } |
| 152 | |
| 153 | // Bundle machine instructions into packets. |
| 154 | void VLIWPacketizerList::PacketizeMIs(MachineBasicBlock *MBB, |
| 155 | MachineBasicBlock::iterator BeginItr, |
| 156 | MachineBasicBlock::iterator EndItr) { |
| 157 | assert(VLIWScheduler && "VLIW Scheduler is not initialized!" ); |
| 158 | VLIWScheduler->startBlock(BB: MBB); |
| 159 | VLIWScheduler->enterRegion(bb: MBB, begin: BeginItr, end: EndItr, |
| 160 | regioninstrs: std::distance(first: BeginItr, last: EndItr)); |
| 161 | VLIWScheduler->schedule(); |
| 162 | |
| 163 | LLVM_DEBUG({ |
| 164 | dbgs() << "Scheduling DAG of the packetize region\n" ; |
| 165 | VLIWScheduler->dump(); |
| 166 | }); |
| 167 | |
| 168 | // Generate MI -> SU map. |
| 169 | MIToSUnit.clear(); |
| 170 | for (SUnit &SU : VLIWScheduler->SUnits) |
| 171 | MIToSUnit[SU.getInstr()] = &SU; |
| 172 | |
| 173 | bool LimitPresent = InstrLimit.getPosition(); |
| 174 | |
| 175 | // The main packetizer loop. |
| 176 | for (; BeginItr != EndItr; ++BeginItr) { |
| 177 | if (LimitPresent) { |
| 178 | if (InstrCount >= InstrLimit) { |
| 179 | EndItr = BeginItr; |
| 180 | break; |
| 181 | } |
| 182 | InstrCount++; |
| 183 | } |
| 184 | MachineInstr &MI = *BeginItr; |
| 185 | initPacketizerState(); |
| 186 | |
| 187 | // End the current packet if needed. |
| 188 | if (isSoloInstruction(MI)) { |
| 189 | endPacket(MBB, MI); |
| 190 | continue; |
| 191 | } |
| 192 | |
| 193 | // Ignore pseudo instructions. |
| 194 | if (ignorePseudoInstruction(I: MI, MBB)) |
| 195 | continue; |
| 196 | |
| 197 | SUnit *SUI = MIToSUnit[&MI]; |
| 198 | assert(SUI && "Missing SUnit Info!" ); |
| 199 | |
| 200 | // Ask DFA if machine resource is available for MI. |
| 201 | LLVM_DEBUG(dbgs() << "Checking resources for adding MI to packet " << MI); |
| 202 | |
| 203 | bool ResourceAvail = ResourceTracker->canReserveResources(MI); |
| 204 | LLVM_DEBUG({ |
| 205 | if (ResourceAvail) |
| 206 | dbgs() << " Resources are available for adding MI to packet\n" ; |
| 207 | else |
| 208 | dbgs() << " Resources NOT available\n" ; |
| 209 | }); |
| 210 | if (ResourceAvail && shouldAddToPacket(MI)) { |
| 211 | // Dependency check for MI with instructions in CurrentPacketMIs. |
| 212 | for (auto *MJ : CurrentPacketMIs) { |
| 213 | SUnit *SUJ = MIToSUnit[MJ]; |
| 214 | assert(SUJ && "Missing SUnit Info!" ); |
| 215 | |
| 216 | LLVM_DEBUG(dbgs() << " Checking against MJ " << *MJ); |
| 217 | // Is it legal to packetize SUI and SUJ together. |
| 218 | if (!isLegalToPacketizeTogether(SUI, SUJ)) { |
| 219 | LLVM_DEBUG(dbgs() << " Not legal to add MI, try to prune\n" ); |
| 220 | // Allow packetization if dependency can be pruned. |
| 221 | if (!isLegalToPruneDependencies(SUI, SUJ)) { |
| 222 | // End the packet if dependency cannot be pruned. |
| 223 | LLVM_DEBUG(dbgs() |
| 224 | << " Could not prune dependencies for adding MI\n" ); |
| 225 | endPacket(MBB, MI); |
| 226 | break; |
| 227 | } |
| 228 | LLVM_DEBUG(dbgs() << " Pruned dependence for adding MI\n" ); |
| 229 | } |
| 230 | } |
| 231 | } else { |
| 232 | LLVM_DEBUG(if (ResourceAvail) dbgs() |
| 233 | << "Resources are available, but instruction should not be " |
| 234 | "added to packet\n " |
| 235 | << MI); |
| 236 | // End the packet if resource is not available, or if the instruction |
| 237 | // should not be added to the current packet. |
| 238 | endPacket(MBB, MI); |
| 239 | } |
| 240 | |
| 241 | // Add MI to the current packet. |
| 242 | LLVM_DEBUG(dbgs() << "* Adding MI to packet " << MI << '\n'); |
| 243 | BeginItr = addToPacket(MI); |
| 244 | } // For all instructions in the packetization range. |
| 245 | |
| 246 | // End any packet left behind. |
| 247 | endPacket(MBB, MI: EndItr); |
| 248 | VLIWScheduler->exitRegion(); |
| 249 | VLIWScheduler->finishBlock(); |
| 250 | } |
| 251 | |
| 252 | bool VLIWPacketizerList::alias(const MachineMemOperand &Op1, |
| 253 | const MachineMemOperand &Op2, |
| 254 | bool UseTBAA) const { |
| 255 | if (!Op1.getValue() || !Op2.getValue() || !Op1.getSize().hasValue() || |
| 256 | !Op2.getSize().hasValue()) |
| 257 | return true; |
| 258 | |
| 259 | int64_t MinOffset = std::min(a: Op1.getOffset(), b: Op2.getOffset()); |
| 260 | int64_t Overlapa = Op1.getSize().getValue() + Op1.getOffset() - MinOffset; |
| 261 | int64_t Overlapb = Op2.getSize().getValue() + Op2.getOffset() - MinOffset; |
| 262 | |
| 263 | AliasResult AAResult = |
| 264 | AA->alias(LocA: MemoryLocation(Op1.getValue(), Overlapa, |
| 265 | UseTBAA ? Op1.getAAInfo() : AAMDNodes()), |
| 266 | LocB: MemoryLocation(Op2.getValue(), Overlapb, |
| 267 | UseTBAA ? Op2.getAAInfo() : AAMDNodes())); |
| 268 | |
| 269 | return AAResult != AliasResult::NoAlias; |
| 270 | } |
| 271 | |
| 272 | bool VLIWPacketizerList::alias(const MachineInstr &MI1, |
| 273 | const MachineInstr &MI2, |
| 274 | bool UseTBAA) const { |
| 275 | if (MI1.memoperands_empty() || MI2.memoperands_empty()) |
| 276 | return true; |
| 277 | |
| 278 | for (const MachineMemOperand *Op1 : MI1.memoperands()) |
| 279 | for (const MachineMemOperand *Op2 : MI2.memoperands()) |
| 280 | if (alias(Op1: *Op1, Op2: *Op2, UseTBAA)) |
| 281 | return true; |
| 282 | return false; |
| 283 | } |
| 284 | |
| 285 | // Add a DAG mutation object to the ordered list. |
| 286 | void VLIWPacketizerList::addMutation( |
| 287 | std::unique_ptr<ScheduleDAGMutation> Mutation) { |
| 288 | VLIWScheduler->addMutation(Mutation: std::move(Mutation)); |
| 289 | } |
| 290 | |