//===- StackLifetime.cpp - Alloca Lifetime Analysis -----------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "llvm/Analysis/StackLifetime.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormattedStream.h" #include #include using namespace llvm; #define DEBUG_TYPE "stack-lifetime" const StackLifetime::LiveRange & StackLifetime::getLiveRange(const AllocaInst *AI) const { const auto IT = AllocaNumbering.find(AI); assert(IT != AllocaNumbering.end()); return LiveRanges[IT->second]; } bool StackLifetime::isReachable(const Instruction *I) const { return BlockInstRange.contains(I->getParent()); } bool StackLifetime::isAliveAfter(const AllocaInst *AI, const Instruction *I) const { const BasicBlock *BB = I->getParent(); auto ItBB = BlockInstRange.find(BB); assert(ItBB != BlockInstRange.end() && "Unreachable is not expected"); // Search the block for the first instruction following 'I'. auto It = std::upper_bound(Instructions.begin() + ItBB->getSecond().first + 1, Instructions.begin() + ItBB->getSecond().second, I, [](const Instruction *L, const Instruction *R) { return L->comesBefore(R); }); --It; unsigned InstNum = It - Instructions.begin(); return getLiveRange(AI).test(InstNum); } // Returns unique alloca annotated by lifetime marker only if // markers has the same size and points to the alloca start. static const AllocaInst *findMatchingAlloca(const IntrinsicInst &II, const DataLayout &DL) { const AllocaInst *AI = findAllocaForValue(II.getArgOperand(1), true); if (!AI) return nullptr; auto AllocaSize = AI->getAllocationSize(DL); if (!AllocaSize) return nullptr; auto *Size = dyn_cast(II.getArgOperand(0)); if (!Size) return nullptr; int64_t LifetimeSize = Size->getSExtValue(); if (LifetimeSize != -1 && uint64_t(LifetimeSize) != *AllocaSize) return nullptr; return AI; } void StackLifetime::collectMarkers() { InterestingAllocas.resize(NumAllocas); DenseMap> BBMarkerSet; const DataLayout &DL = F.getDataLayout(); // Compute the set of start/end markers per basic block. for (const BasicBlock *BB : depth_first(&F)) { for (const Instruction &I : *BB) { const IntrinsicInst *II = dyn_cast(&I); if (!II || !II->isLifetimeStartOrEnd()) continue; const AllocaInst *AI = findMatchingAlloca(*II, DL); if (!AI) { HasUnknownLifetimeStartOrEnd = true; continue; } auto It = AllocaNumbering.find(AI); if (It == AllocaNumbering.end()) continue; auto AllocaNo = It->second; bool IsStart = II->getIntrinsicID() == Intrinsic::lifetime_start; if (IsStart) InterestingAllocas.set(AllocaNo); BBMarkerSet[BB][II] = {AllocaNo, IsStart}; } } // Compute instruction numbering. Only the following instructions are // considered: // * Basic block entries // * Lifetime markers // For each basic block, compute // * the list of markers in the instruction order // * the sets of allocas whose lifetime starts or ends in this BB LLVM_DEBUG(dbgs() << "Instructions:\n"); for (const BasicBlock *BB : depth_first(&F)) { LLVM_DEBUG(dbgs() << " " << Instructions.size() << ": BB " << BB->getName() << "\n"); auto BBStart = Instructions.size(); Instructions.push_back(nullptr); BlockLifetimeInfo &BlockInfo = BlockLiveness.try_emplace(BB, NumAllocas).first->getSecond(); auto &BlockMarkerSet = BBMarkerSet[BB]; if (BlockMarkerSet.empty()) { BlockInstRange[BB] = std::make_pair(BBStart, Instructions.size()); continue; } auto ProcessMarker = [&](const IntrinsicInst *I, const Marker &M) { LLVM_DEBUG(dbgs() << " " << Instructions.size() << ": " << (M.IsStart ? "start " : "end ") << M.AllocaNo << ", " << *I << "\n"); BBMarkers[BB].push_back({Instructions.size(), M}); Instructions.push_back(I); if (M.IsStart) { BlockInfo.End.reset(M.AllocaNo); BlockInfo.Begin.set(M.AllocaNo); } else { BlockInfo.Begin.reset(M.AllocaNo); BlockInfo.End.set(M.AllocaNo); } }; if (BlockMarkerSet.size() == 1) { ProcessMarker(BlockMarkerSet.begin()->getFirst(), BlockMarkerSet.begin()->getSecond()); } else { // Scan the BB to determine the marker order. for (const Instruction &I : *BB) { const IntrinsicInst *II = dyn_cast(&I); if (!II) continue; auto It = BlockMarkerSet.find(II); if (It == BlockMarkerSet.end()) continue; ProcessMarker(II, It->getSecond()); } } BlockInstRange[BB] = std::make_pair(BBStart, Instructions.size()); } } void StackLifetime::calculateLocalLiveness() { bool Changed = true; // LiveIn, LiveOut and BitsIn have a different meaning deppends on type. // ::Maybe true bits represent "may be alive" allocas, ::Must true bits // represent "may be dead". After the loop we will convert ::Must bits from // "may be dead" to "must be alive". while (Changed) { // TODO: Consider switching to worklist instead of traversing entire graph. Changed = false; for (const BasicBlock *BB : depth_first(&F)) { BlockLifetimeInfo &BlockInfo = BlockLiveness.find(BB)->getSecond(); // Compute BitsIn by unioning together the LiveOut sets of all preds. BitVector BitsIn; for (const auto *PredBB : predecessors(BB)) { LivenessMap::const_iterator I = BlockLiveness.find(PredBB); // If a predecessor is unreachable, ignore it. if (I == BlockLiveness.end()) continue; BitsIn |= I->second.LiveOut; } // Everything is "may be dead" for entry without predecessors. if (Type == LivenessType::Must && BitsIn.empty()) BitsIn.resize(NumAllocas, true); // Update block LiveIn set, noting whether it has changed. if (BitsIn.test(BlockInfo.LiveIn)) { BlockInfo.LiveIn |= BitsIn; } // Compute LiveOut by subtracting out lifetimes that end in this // block, then adding in lifetimes that begin in this block. If // we have both BEGIN and END markers in the same basic block // then we know that the BEGIN marker comes after the END, // because we already handle the case where the BEGIN comes // before the END when collecting the markers (and building the // BEGIN/END vectors). switch (Type) { case LivenessType::May: BitsIn.reset(BlockInfo.End); // "may be alive" is set by lifetime start. BitsIn |= BlockInfo.Begin; break; case LivenessType::Must: BitsIn.reset(BlockInfo.Begin); // "may be dead" is set by lifetime end. BitsIn |= BlockInfo.End; break; } // Update block LiveOut set, noting whether it has changed. if (BitsIn.test(BlockInfo.LiveOut)) { Changed = true; BlockInfo.LiveOut |= BitsIn; } } } // while changed. if (Type == LivenessType::Must) { // Convert from "may be dead" to "must be alive". for (auto &[BB, BlockInfo] : BlockLiveness) { BlockInfo.LiveIn.flip(); BlockInfo.LiveOut.flip(); } } } void StackLifetime::calculateLiveIntervals() { for (auto IT : BlockLiveness) { const BasicBlock *BB = IT.getFirst(); BlockLifetimeInfo &BlockInfo = IT.getSecond(); unsigned BBStart, BBEnd; std::tie(BBStart, BBEnd) = BlockInstRange[BB]; BitVector Started, Ended; Started.resize(NumAllocas); Ended.resize(NumAllocas); SmallVector Start; Start.resize(NumAllocas); // LiveIn ranges start at the first instruction. for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) { if (BlockInfo.LiveIn.test(AllocaNo)) { Started.set(AllocaNo); Start[AllocaNo] = BBStart; } } for (auto &It : BBMarkers[BB]) { unsigned InstNo = It.first; bool IsStart = It.second.IsStart; unsigned AllocaNo = It.second.AllocaNo; if (IsStart) { if (!Started.test(AllocaNo)) { Started.set(AllocaNo); Ended.reset(AllocaNo); Start[AllocaNo] = InstNo; } } else { if (Started.test(AllocaNo)) { LiveRanges[AllocaNo].addRange(Start[AllocaNo], InstNo); Started.reset(AllocaNo); } Ended.set(AllocaNo); } } for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) if (Started.test(AllocaNo)) LiveRanges[AllocaNo].addRange(Start[AllocaNo], BBEnd); } } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void StackLifetime::dumpAllocas() const { dbgs() << "Allocas:\n"; for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) dbgs() << " " << AllocaNo << ": " << *Allocas[AllocaNo] << "\n"; } LLVM_DUMP_METHOD void StackLifetime::dumpBlockLiveness() const { dbgs() << "Block liveness:\n"; for (auto IT : BlockLiveness) { const BasicBlock *BB = IT.getFirst(); const BlockLifetimeInfo &BlockInfo = BlockLiveness.find(BB)->getSecond(); auto BlockRange = BlockInstRange.find(BB)->getSecond(); dbgs() << " BB (" << BB->getName() << ") [" << BlockRange.first << ", " << BlockRange.second << "): begin " << BlockInfo.Begin << ", end " << BlockInfo.End << ", livein " << BlockInfo.LiveIn << ", liveout " << BlockInfo.LiveOut << "\n"; } } LLVM_DUMP_METHOD void StackLifetime::dumpLiveRanges() const { dbgs() << "Alloca liveness:\n"; for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) dbgs() << " " << AllocaNo << ": " << LiveRanges[AllocaNo] << "\n"; } #endif StackLifetime::StackLifetime(const Function &F, ArrayRef Allocas, LivenessType Type) : F(F), Type(Type), Allocas(Allocas), NumAllocas(Allocas.size()) { LLVM_DEBUG(dumpAllocas()); for (unsigned I = 0; I < NumAllocas; ++I) AllocaNumbering[Allocas[I]] = I; collectMarkers(); } void StackLifetime::run() { if (HasUnknownLifetimeStartOrEnd) { // There is marker which we can't assign to a specific alloca, so we // fallback to the most conservative results for the type. switch (Type) { case LivenessType::May: LiveRanges.resize(NumAllocas, getFullLiveRange()); break; case LivenessType::Must: LiveRanges.resize(NumAllocas, LiveRange(Instructions.size())); break; } return; } LiveRanges.resize(NumAllocas, LiveRange(Instructions.size())); for (unsigned I = 0; I < NumAllocas; ++I) if (!InterestingAllocas.test(I)) LiveRanges[I] = getFullLiveRange(); calculateLocalLiveness(); LLVM_DEBUG(dumpBlockLiveness()); calculateLiveIntervals(); LLVM_DEBUG(dumpLiveRanges()); } class StackLifetime::LifetimeAnnotationWriter : public AssemblyAnnotationWriter { const StackLifetime &SL; void printInstrAlive(unsigned InstrNo, formatted_raw_ostream &OS) { SmallVector Names; for (const auto &KV : SL.AllocaNumbering) { if (SL.LiveRanges[KV.getSecond()].test(InstrNo)) Names.push_back(KV.getFirst()->getName()); } llvm::sort(Names); OS << " ; Alive: <" << llvm::join(Names, " ") << ">\n"; } void emitBasicBlockStartAnnot(const BasicBlock *BB, formatted_raw_ostream &OS) override { auto ItBB = SL.BlockInstRange.find(BB); if (ItBB == SL.BlockInstRange.end()) return; // Unreachable. printInstrAlive(ItBB->getSecond().first, OS); } void printInfoComment(const Value &V, formatted_raw_ostream &OS) override { const Instruction *Instr = dyn_cast(&V); if (!Instr || !SL.isReachable(Instr)) return; SmallVector Names; for (const auto &KV : SL.AllocaNumbering) { if (SL.isAliveAfter(KV.getFirst(), Instr)) Names.push_back(KV.getFirst()->getName()); } llvm::sort(Names); OS << "\n ; Alive: <" << llvm::join(Names, " ") << ">\n"; } public: LifetimeAnnotationWriter(const StackLifetime &SL) : SL(SL) {} }; void StackLifetime::print(raw_ostream &OS) { LifetimeAnnotationWriter AAW(*this); F.print(OS, &AAW); } PreservedAnalyses StackLifetimePrinterPass::run(Function &F, FunctionAnalysisManager &AM) { SmallVector Allocas; for (auto &I : instructions(F)) if (const AllocaInst *AI = dyn_cast(&I)) Allocas.push_back(AI); StackLifetime SL(F, Allocas, Type); SL.run(); SL.print(OS); return PreservedAnalyses::all(); } void StackLifetimePrinterPass::printPipeline( raw_ostream &OS, function_ref MapClassName2PassName) { static_cast *>(this)->printPipeline( OS, MapClassName2PassName); OS << '<'; switch (Type) { case StackLifetime::LivenessType::May: OS << "may"; break; case StackLifetime::LivenessType::Must: OS << "must"; break; } OS << '>'; }