| 1 | //===- Region.cpp ---------------------------------------------------------===// |
| 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 | |
| 9 | #include "llvm/SandboxIR/Region.h" |
| 10 | #include "llvm/SandboxIR/Function.h" |
| 11 | |
| 12 | namespace llvm::sandboxir { |
| 13 | |
| 14 | InstructionCost ScoreBoard::getCost(Instruction *I) const { |
| 15 | auto *LLVMI = cast<llvm::Instruction>(Val: I->Val); |
| 16 | SmallVector<const llvm::Value *> Operands(LLVMI->operands()); |
| 17 | return TTI.getInstructionCost(U: LLVMI, Operands, CostKind); |
| 18 | } |
| 19 | |
| 20 | void ScoreBoard::remove(Instruction *I) { |
| 21 | auto Cost = getCost(I); |
| 22 | if (Rgn.contains(I)) |
| 23 | // If `I` is one the newly added ones, then we should adjust `AfterCost` |
| 24 | AfterCost -= Cost; |
| 25 | else |
| 26 | // If `I` is one of the original instructions (outside the region) then it |
| 27 | // is part of the original code, so adjust `BeforeCost`. |
| 28 | BeforeCost += Cost; |
| 29 | } |
| 30 | |
| 31 | #ifndef NDEBUG |
| 32 | void ScoreBoard::dump() const { dump(dbgs()); } |
| 33 | #endif |
| 34 | |
| 35 | Region::Region(Context &Ctx, TargetTransformInfo &TTI) |
| 36 | : Ctx(Ctx), Scoreboard(*this, TTI) { |
| 37 | LLVMContext &LLVMCtx = Ctx.LLVMCtx; |
| 38 | auto *RegionStrMD = MDString::get(Context&: LLVMCtx, Str: RegionStr); |
| 39 | RegionMDN = MDNode::getDistinct(Context&: LLVMCtx, MDs: {RegionStrMD}); |
| 40 | |
| 41 | CreateInstCB = Ctx.registerCreateInstrCallback( |
| 42 | CB: [this](Instruction *NewInst) { add(I: NewInst); }); |
| 43 | EraseInstCB = Ctx.registerEraseInstrCallback(CB: [this](Instruction *ErasedInst) { |
| 44 | remove(I: ErasedInst); |
| 45 | removeFromAux(I: ErasedInst); |
| 46 | }); |
| 47 | } |
| 48 | |
| 49 | Region::~Region() { |
| 50 | Ctx.unregisterCreateInstrCallback(ID: CreateInstCB); |
| 51 | Ctx.unregisterEraseInstrCallback(ID: EraseInstCB); |
| 52 | } |
| 53 | |
| 54 | void Region::addImpl(Instruction *I, bool IgnoreCost) { |
| 55 | Insts.insert(X: I); |
| 56 | // TODO: Consider tagging instructions lazily. |
| 57 | cast<llvm::Instruction>(Val: I->Val)->setMetadata(Kind: MDKind, Node: RegionMDN); |
| 58 | if (!IgnoreCost) |
| 59 | // Keep track of the instruction cost. |
| 60 | Scoreboard.add(I); |
| 61 | } |
| 62 | |
| 63 | void Region::setAux(ArrayRef<Instruction *> Aux) { |
| 64 | this->Aux = SmallVector<Instruction *>(Aux); |
| 65 | auto &LLVMCtx = Ctx.LLVMCtx; |
| 66 | for (auto [Idx, I] : enumerate(First&: Aux)) { |
| 67 | llvm::ConstantInt *IdxC = |
| 68 | llvm::ConstantInt::get(Ty: llvm::Type::getInt32Ty(C&: LLVMCtx), V: Idx, IsSigned: false); |
| 69 | assert(cast<llvm::Instruction>(I->Val)->getMetadata(AuxMDKind) == nullptr && |
| 70 | "Instruction already in Aux!" ); |
| 71 | cast<llvm::Instruction>(Val: I->Val)->setMetadata( |
| 72 | Kind: AuxMDKind, Node: MDNode::get(Context&: LLVMCtx, MDs: ConstantAsMetadata::get(C: IdxC))); |
| 73 | // Aux instrs should always be in a region. |
| 74 | addImpl(I, /*DontTrackCost=*/IgnoreCost: true); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | void Region::setAux(unsigned Idx, Instruction *I) { |
| 79 | assert((Idx >= Aux.size() || Aux[Idx] == nullptr) && |
| 80 | "There is already an Instruction at Idx in Aux!" ); |
| 81 | unsigned ExpectedSz = Idx + 1; |
| 82 | if (Aux.size() < ExpectedSz) { |
| 83 | auto SzBefore = Aux.size(); |
| 84 | Aux.resize(N: ExpectedSz); |
| 85 | // Initialize the gap with nullptr. |
| 86 | for (unsigned Idx = SzBefore; Idx + 1 < ExpectedSz; ++Idx) |
| 87 | Aux[Idx] = nullptr; |
| 88 | } |
| 89 | Aux[Idx] = I; |
| 90 | // Aux instrs should always be in a region. |
| 91 | addImpl(I, /*DontTrackCost=*/IgnoreCost: true); |
| 92 | } |
| 93 | |
| 94 | void Region::dropAuxMetadata(Instruction *I) { |
| 95 | auto *LLVMI = cast<llvm::Instruction>(Val: I->Val); |
| 96 | LLVMI->setMetadata(Kind: AuxMDKind, Node: nullptr); |
| 97 | } |
| 98 | |
| 99 | void Region::removeFromAux(Instruction *I) { |
| 100 | auto It = find(Range&: Aux, Val: I); |
| 101 | if (It == Aux.end()) |
| 102 | return; |
| 103 | dropAuxMetadata(I); |
| 104 | Aux.erase(CI: It); |
| 105 | } |
| 106 | |
| 107 | void Region::clearAux() { |
| 108 | for (unsigned Idx : seq<unsigned>(Begin: 0, End: Aux.size())) |
| 109 | dropAuxMetadata(I: Aux[Idx]); |
| 110 | Aux.clear(); |
| 111 | } |
| 112 | |
| 113 | void Region::remove(Instruction *I) { |
| 114 | // Keep track of the instruction cost. This need to be done *before* we remove |
| 115 | // `I` from the region. |
| 116 | Scoreboard.remove(I); |
| 117 | |
| 118 | Insts.remove(X: I); |
| 119 | cast<llvm::Instruction>(Val: I->Val)->setMetadata(Kind: MDKind, Node: nullptr); |
| 120 | } |
| 121 | |
| 122 | #ifndef NDEBUG |
| 123 | bool Region::operator==(const Region &Other) const { |
| 124 | if (Insts.size() != Other.Insts.size()) |
| 125 | return false; |
| 126 | if (!std::is_permutation(Insts.begin(), Insts.end(), Other.Insts.begin())) |
| 127 | return false; |
| 128 | return true; |
| 129 | } |
| 130 | |
| 131 | void Region::dump(raw_ostream &OS) const { |
| 132 | for (auto *I : Insts) |
| 133 | OS << *I << "\n" ; |
| 134 | if (!Aux.empty()) { |
| 135 | OS << "\nAux:\n" ; |
| 136 | for (auto *I : Aux) { |
| 137 | if (I == nullptr) |
| 138 | OS << "NULL\n" ; |
| 139 | else |
| 140 | OS << *I << "\n" ; |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | void Region::dump() const { |
| 146 | dump(dbgs()); |
| 147 | dbgs() << "\n" ; |
| 148 | } |
| 149 | #endif // NDEBUG |
| 150 | |
| 151 | SmallVector<std::unique_ptr<Region>> |
| 152 | Region::createRegionsFromMD(Function &F, TargetTransformInfo &TTI) { |
| 153 | SmallVector<std::unique_ptr<Region>> Regions; |
| 154 | DenseMap<MDNode *, Region *> MDNToRegion; |
| 155 | auto &Ctx = F.getContext(); |
| 156 | for (BasicBlock &BB : F) { |
| 157 | for (Instruction &Inst : BB) { |
| 158 | auto *LLVMI = cast<llvm::Instruction>(Val: Inst.Val); |
| 159 | Region *R = nullptr; |
| 160 | if (auto *MDN = LLVMI->getMetadata(Kind: MDKind)) { |
| 161 | auto [It, Inserted] = MDNToRegion.try_emplace(Key: MDN); |
| 162 | if (Inserted) { |
| 163 | Regions.push_back(Elt: std::make_unique<Region>(args&: Ctx, args&: TTI)); |
| 164 | R = Regions.back().get(); |
| 165 | It->second = R; |
| 166 | } else { |
| 167 | R = It->second; |
| 168 | } |
| 169 | R->addImpl(I: &Inst, /*IgnoreCost=*/true); |
| 170 | } |
| 171 | if (auto *AuxMDN = LLVMI->getMetadata(Kind: AuxMDKind)) { |
| 172 | llvm::Constant *IdxC = |
| 173 | dyn_cast<ConstantAsMetadata>(Val: AuxMDN->getOperand(I: 0))->getValue(); |
| 174 | auto Idx = cast<llvm::ConstantInt>(Val: IdxC)->getSExtValue(); |
| 175 | if (R == nullptr) { |
| 176 | errs() << "No region specified for Aux: '" << *LLVMI << "'\n" ; |
| 177 | exit(status: 1); |
| 178 | } |
| 179 | R->setAux(Idx, I: &Inst); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | #ifndef NDEBUG |
| 184 | // Check that there are no gaps in the Aux vector. |
| 185 | for (auto &RPtr : Regions) |
| 186 | for (auto *I : RPtr->getAux()) |
| 187 | assert(I != nullptr && "Gap in Aux!" ); |
| 188 | #endif |
| 189 | return Regions; |
| 190 | } |
| 191 | |
| 192 | } // namespace llvm::sandboxir |
| 193 | |