1097a140dSpatrick //===- StackLifetime.cpp - Alloca Lifetime Analysis -----------------------===// 2097a140dSpatrick // 3097a140dSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4097a140dSpatrick // See https://llvm.org/LICENSE.txt for license information. 5097a140dSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6097a140dSpatrick // 7097a140dSpatrick //===----------------------------------------------------------------------===// 8097a140dSpatrick 9097a140dSpatrick #include "llvm/Analysis/StackLifetime.h" 10097a140dSpatrick #include "llvm/ADT/DepthFirstIterator.h" 11097a140dSpatrick #include "llvm/ADT/STLExtras.h" 12097a140dSpatrick #include "llvm/ADT/SmallVector.h" 13097a140dSpatrick #include "llvm/ADT/StringExtras.h" 14*73471bf0Spatrick #include "llvm/Analysis/ValueTracking.h" 15097a140dSpatrick #include "llvm/Config/llvm-config.h" 16097a140dSpatrick #include "llvm/IR/AssemblyAnnotationWriter.h" 17097a140dSpatrick #include "llvm/IR/BasicBlock.h" 18097a140dSpatrick #include "llvm/IR/CFG.h" 19097a140dSpatrick #include "llvm/IR/InstIterator.h" 20097a140dSpatrick #include "llvm/IR/Instructions.h" 21097a140dSpatrick #include "llvm/IR/IntrinsicInst.h" 22097a140dSpatrick #include "llvm/IR/Intrinsics.h" 23097a140dSpatrick #include "llvm/IR/User.h" 24097a140dSpatrick #include "llvm/IR/Value.h" 25097a140dSpatrick #include "llvm/Pass.h" 26097a140dSpatrick #include "llvm/Support/Casting.h" 27097a140dSpatrick #include "llvm/Support/CommandLine.h" 28097a140dSpatrick #include "llvm/Support/Compiler.h" 29097a140dSpatrick #include "llvm/Support/Debug.h" 30097a140dSpatrick #include "llvm/Support/FormattedStream.h" 31097a140dSpatrick #include <algorithm> 32097a140dSpatrick #include <memory> 33097a140dSpatrick #include <tuple> 34097a140dSpatrick 35097a140dSpatrick using namespace llvm; 36097a140dSpatrick 37097a140dSpatrick #define DEBUG_TYPE "stack-lifetime" 38097a140dSpatrick 39097a140dSpatrick const StackLifetime::LiveRange & 40097a140dSpatrick StackLifetime::getLiveRange(const AllocaInst *AI) const { 41097a140dSpatrick const auto IT = AllocaNumbering.find(AI); 42097a140dSpatrick assert(IT != AllocaNumbering.end()); 43097a140dSpatrick return LiveRanges[IT->second]; 44097a140dSpatrick } 45097a140dSpatrick 46097a140dSpatrick bool StackLifetime::isReachable(const Instruction *I) const { 47097a140dSpatrick return BlockInstRange.find(I->getParent()) != BlockInstRange.end(); 48097a140dSpatrick } 49097a140dSpatrick 50097a140dSpatrick bool StackLifetime::isAliveAfter(const AllocaInst *AI, 51097a140dSpatrick const Instruction *I) const { 52097a140dSpatrick const BasicBlock *BB = I->getParent(); 53097a140dSpatrick auto ItBB = BlockInstRange.find(BB); 54097a140dSpatrick assert(ItBB != BlockInstRange.end() && "Unreachable is not expected"); 55097a140dSpatrick 56097a140dSpatrick // Search the block for the first instruction following 'I'. 57097a140dSpatrick auto It = std::upper_bound(Instructions.begin() + ItBB->getSecond().first + 1, 58097a140dSpatrick Instructions.begin() + ItBB->getSecond().second, I, 59097a140dSpatrick [](const Instruction *L, const Instruction *R) { 60097a140dSpatrick return L->comesBefore(R); 61097a140dSpatrick }); 62097a140dSpatrick --It; 63097a140dSpatrick unsigned InstNum = It - Instructions.begin(); 64097a140dSpatrick return getLiveRange(AI).test(InstNum); 65097a140dSpatrick } 66097a140dSpatrick 67*73471bf0Spatrick // Returns unique alloca annotated by lifetime marker only if 68*73471bf0Spatrick // markers has the same size and points to the alloca start. 69*73471bf0Spatrick static const AllocaInst *findMatchingAlloca(const IntrinsicInst &II, 70*73471bf0Spatrick const DataLayout &DL) { 71*73471bf0Spatrick const AllocaInst *AI = findAllocaForValue(II.getArgOperand(1), true); 72*73471bf0Spatrick if (!AI) 73*73471bf0Spatrick return nullptr; 74097a140dSpatrick 75*73471bf0Spatrick auto AllocaSizeInBits = AI->getAllocationSizeInBits(DL); 76*73471bf0Spatrick if (!AllocaSizeInBits) 77*73471bf0Spatrick return nullptr; 78*73471bf0Spatrick int64_t AllocaSize = AllocaSizeInBits.getValue() / 8; 79*73471bf0Spatrick 80*73471bf0Spatrick auto *Size = dyn_cast<ConstantInt>(II.getArgOperand(0)); 81*73471bf0Spatrick if (!Size) 82*73471bf0Spatrick return nullptr; 83*73471bf0Spatrick int64_t LifetimeSize = Size->getSExtValue(); 84*73471bf0Spatrick 85*73471bf0Spatrick if (LifetimeSize != -1 && LifetimeSize != AllocaSize) 86*73471bf0Spatrick return nullptr; 87*73471bf0Spatrick 88*73471bf0Spatrick return AI; 89097a140dSpatrick } 90097a140dSpatrick 91097a140dSpatrick void StackLifetime::collectMarkers() { 92097a140dSpatrick InterestingAllocas.resize(NumAllocas); 93097a140dSpatrick DenseMap<const BasicBlock *, SmallDenseMap<const IntrinsicInst *, Marker>> 94097a140dSpatrick BBMarkerSet; 95097a140dSpatrick 96*73471bf0Spatrick const DataLayout &DL = F.getParent()->getDataLayout(); 97*73471bf0Spatrick 98097a140dSpatrick // Compute the set of start/end markers per basic block. 99*73471bf0Spatrick for (const BasicBlock *BB : depth_first(&F)) { 100*73471bf0Spatrick for (const Instruction &I : *BB) { 101*73471bf0Spatrick const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I); 102*73471bf0Spatrick if (!II || !II->isLifetimeStartOrEnd()) 103*73471bf0Spatrick continue; 104*73471bf0Spatrick const AllocaInst *AI = findMatchingAlloca(*II, DL); 105*73471bf0Spatrick if (!AI) { 106*73471bf0Spatrick HasUnknownLifetimeStartOrEnd = true; 107097a140dSpatrick continue; 108097a140dSpatrick } 109*73471bf0Spatrick auto It = AllocaNumbering.find(AI); 110*73471bf0Spatrick if (It == AllocaNumbering.end()) 111097a140dSpatrick continue; 112*73471bf0Spatrick auto AllocaNo = It->second; 113*73471bf0Spatrick bool IsStart = II->getIntrinsicID() == Intrinsic::lifetime_start; 114097a140dSpatrick if (IsStart) 115097a140dSpatrick InterestingAllocas.set(AllocaNo); 116*73471bf0Spatrick BBMarkerSet[BB][II] = {AllocaNo, IsStart}; 117097a140dSpatrick } 118097a140dSpatrick } 119097a140dSpatrick 120097a140dSpatrick // Compute instruction numbering. Only the following instructions are 121097a140dSpatrick // considered: 122097a140dSpatrick // * Basic block entries 123097a140dSpatrick // * Lifetime markers 124097a140dSpatrick // For each basic block, compute 125097a140dSpatrick // * the list of markers in the instruction order 126097a140dSpatrick // * the sets of allocas whose lifetime starts or ends in this BB 127097a140dSpatrick LLVM_DEBUG(dbgs() << "Instructions:\n"); 128097a140dSpatrick for (const BasicBlock *BB : depth_first(&F)) { 129097a140dSpatrick LLVM_DEBUG(dbgs() << " " << Instructions.size() << ": BB " << BB->getName() 130097a140dSpatrick << "\n"); 131097a140dSpatrick auto BBStart = Instructions.size(); 132097a140dSpatrick Instructions.push_back(nullptr); 133097a140dSpatrick 134097a140dSpatrick BlockLifetimeInfo &BlockInfo = 135097a140dSpatrick BlockLiveness.try_emplace(BB, NumAllocas).first->getSecond(); 136097a140dSpatrick 137097a140dSpatrick auto &BlockMarkerSet = BBMarkerSet[BB]; 138097a140dSpatrick if (BlockMarkerSet.empty()) { 139097a140dSpatrick BlockInstRange[BB] = std::make_pair(BBStart, Instructions.size()); 140097a140dSpatrick continue; 141097a140dSpatrick } 142097a140dSpatrick 143097a140dSpatrick auto ProcessMarker = [&](const IntrinsicInst *I, const Marker &M) { 144097a140dSpatrick LLVM_DEBUG(dbgs() << " " << Instructions.size() << ": " 145097a140dSpatrick << (M.IsStart ? "start " : "end ") << M.AllocaNo 146097a140dSpatrick << ", " << *I << "\n"); 147097a140dSpatrick 148097a140dSpatrick BBMarkers[BB].push_back({Instructions.size(), M}); 149097a140dSpatrick Instructions.push_back(I); 150097a140dSpatrick 151097a140dSpatrick if (M.IsStart) { 152097a140dSpatrick BlockInfo.End.reset(M.AllocaNo); 153097a140dSpatrick BlockInfo.Begin.set(M.AllocaNo); 154097a140dSpatrick } else { 155097a140dSpatrick BlockInfo.Begin.reset(M.AllocaNo); 156097a140dSpatrick BlockInfo.End.set(M.AllocaNo); 157097a140dSpatrick } 158097a140dSpatrick }; 159097a140dSpatrick 160097a140dSpatrick if (BlockMarkerSet.size() == 1) { 161097a140dSpatrick ProcessMarker(BlockMarkerSet.begin()->getFirst(), 162097a140dSpatrick BlockMarkerSet.begin()->getSecond()); 163097a140dSpatrick } else { 164097a140dSpatrick // Scan the BB to determine the marker order. 165097a140dSpatrick for (const Instruction &I : *BB) { 166097a140dSpatrick const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I); 167097a140dSpatrick if (!II) 168097a140dSpatrick continue; 169097a140dSpatrick auto It = BlockMarkerSet.find(II); 170097a140dSpatrick if (It == BlockMarkerSet.end()) 171097a140dSpatrick continue; 172097a140dSpatrick ProcessMarker(II, It->getSecond()); 173097a140dSpatrick } 174097a140dSpatrick } 175097a140dSpatrick 176097a140dSpatrick BlockInstRange[BB] = std::make_pair(BBStart, Instructions.size()); 177097a140dSpatrick } 178097a140dSpatrick } 179097a140dSpatrick 180097a140dSpatrick void StackLifetime::calculateLocalLiveness() { 181097a140dSpatrick bool Changed = true; 182097a140dSpatrick while (Changed) { 183097a140dSpatrick Changed = false; 184097a140dSpatrick 185097a140dSpatrick for (const BasicBlock *BB : depth_first(&F)) { 186097a140dSpatrick BlockLifetimeInfo &BlockInfo = BlockLiveness.find(BB)->getSecond(); 187097a140dSpatrick 188097a140dSpatrick // Compute LiveIn by unioning together the LiveOut sets of all preds. 189097a140dSpatrick BitVector LocalLiveIn; 190097a140dSpatrick for (auto *PredBB : predecessors(BB)) { 191097a140dSpatrick LivenessMap::const_iterator I = BlockLiveness.find(PredBB); 192097a140dSpatrick // If a predecessor is unreachable, ignore it. 193097a140dSpatrick if (I == BlockLiveness.end()) 194097a140dSpatrick continue; 195097a140dSpatrick switch (Type) { 196097a140dSpatrick case LivenessType::May: 197097a140dSpatrick LocalLiveIn |= I->second.LiveOut; 198097a140dSpatrick break; 199097a140dSpatrick case LivenessType::Must: 200097a140dSpatrick if (LocalLiveIn.empty()) 201097a140dSpatrick LocalLiveIn = I->second.LiveOut; 202097a140dSpatrick else 203097a140dSpatrick LocalLiveIn &= I->second.LiveOut; 204097a140dSpatrick break; 205097a140dSpatrick } 206097a140dSpatrick } 207097a140dSpatrick 208097a140dSpatrick // Compute LiveOut by subtracting out lifetimes that end in this 209097a140dSpatrick // block, then adding in lifetimes that begin in this block. If 210097a140dSpatrick // we have both BEGIN and END markers in the same basic block 211097a140dSpatrick // then we know that the BEGIN marker comes after the END, 212097a140dSpatrick // because we already handle the case where the BEGIN comes 213097a140dSpatrick // before the END when collecting the markers (and building the 214097a140dSpatrick // BEGIN/END vectors). 215097a140dSpatrick BitVector LocalLiveOut = LocalLiveIn; 216097a140dSpatrick LocalLiveOut.reset(BlockInfo.End); 217097a140dSpatrick LocalLiveOut |= BlockInfo.Begin; 218097a140dSpatrick 219097a140dSpatrick // Update block LiveIn set, noting whether it has changed. 220097a140dSpatrick if (LocalLiveIn.test(BlockInfo.LiveIn)) { 221097a140dSpatrick BlockInfo.LiveIn |= LocalLiveIn; 222097a140dSpatrick } 223097a140dSpatrick 224097a140dSpatrick // Update block LiveOut set, noting whether it has changed. 225097a140dSpatrick if (LocalLiveOut.test(BlockInfo.LiveOut)) { 226097a140dSpatrick Changed = true; 227097a140dSpatrick BlockInfo.LiveOut |= LocalLiveOut; 228097a140dSpatrick } 229097a140dSpatrick } 230097a140dSpatrick } // while changed. 231097a140dSpatrick } 232097a140dSpatrick 233097a140dSpatrick void StackLifetime::calculateLiveIntervals() { 234097a140dSpatrick for (auto IT : BlockLiveness) { 235097a140dSpatrick const BasicBlock *BB = IT.getFirst(); 236097a140dSpatrick BlockLifetimeInfo &BlockInfo = IT.getSecond(); 237097a140dSpatrick unsigned BBStart, BBEnd; 238097a140dSpatrick std::tie(BBStart, BBEnd) = BlockInstRange[BB]; 239097a140dSpatrick 240097a140dSpatrick BitVector Started, Ended; 241097a140dSpatrick Started.resize(NumAllocas); 242097a140dSpatrick Ended.resize(NumAllocas); 243097a140dSpatrick SmallVector<unsigned, 8> Start; 244097a140dSpatrick Start.resize(NumAllocas); 245097a140dSpatrick 246097a140dSpatrick // LiveIn ranges start at the first instruction. 247097a140dSpatrick for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) { 248097a140dSpatrick if (BlockInfo.LiveIn.test(AllocaNo)) { 249097a140dSpatrick Started.set(AllocaNo); 250097a140dSpatrick Start[AllocaNo] = BBStart; 251097a140dSpatrick } 252097a140dSpatrick } 253097a140dSpatrick 254097a140dSpatrick for (auto &It : BBMarkers[BB]) { 255097a140dSpatrick unsigned InstNo = It.first; 256097a140dSpatrick bool IsStart = It.second.IsStart; 257097a140dSpatrick unsigned AllocaNo = It.second.AllocaNo; 258097a140dSpatrick 259097a140dSpatrick if (IsStart) { 260097a140dSpatrick assert(!Started.test(AllocaNo) || Start[AllocaNo] == BBStart); 261097a140dSpatrick if (!Started.test(AllocaNo)) { 262097a140dSpatrick Started.set(AllocaNo); 263097a140dSpatrick Ended.reset(AllocaNo); 264097a140dSpatrick Start[AllocaNo] = InstNo; 265097a140dSpatrick } 266097a140dSpatrick } else { 267097a140dSpatrick assert(!Ended.test(AllocaNo)); 268097a140dSpatrick if (Started.test(AllocaNo)) { 269097a140dSpatrick LiveRanges[AllocaNo].addRange(Start[AllocaNo], InstNo); 270097a140dSpatrick Started.reset(AllocaNo); 271097a140dSpatrick } 272097a140dSpatrick Ended.set(AllocaNo); 273097a140dSpatrick } 274097a140dSpatrick } 275097a140dSpatrick 276097a140dSpatrick for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) 277097a140dSpatrick if (Started.test(AllocaNo)) 278097a140dSpatrick LiveRanges[AllocaNo].addRange(Start[AllocaNo], BBEnd); 279097a140dSpatrick } 280097a140dSpatrick } 281097a140dSpatrick 282097a140dSpatrick #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 283097a140dSpatrick LLVM_DUMP_METHOD void StackLifetime::dumpAllocas() const { 284097a140dSpatrick dbgs() << "Allocas:\n"; 285097a140dSpatrick for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) 286097a140dSpatrick dbgs() << " " << AllocaNo << ": " << *Allocas[AllocaNo] << "\n"; 287097a140dSpatrick } 288097a140dSpatrick 289097a140dSpatrick LLVM_DUMP_METHOD void StackLifetime::dumpBlockLiveness() const { 290097a140dSpatrick dbgs() << "Block liveness:\n"; 291097a140dSpatrick for (auto IT : BlockLiveness) { 292097a140dSpatrick const BasicBlock *BB = IT.getFirst(); 293097a140dSpatrick const BlockLifetimeInfo &BlockInfo = BlockLiveness.find(BB)->getSecond(); 294097a140dSpatrick auto BlockRange = BlockInstRange.find(BB)->getSecond(); 295*73471bf0Spatrick dbgs() << " BB (" << BB->getName() << ") [" << BlockRange.first << ", " << BlockRange.second 296097a140dSpatrick << "): begin " << BlockInfo.Begin << ", end " << BlockInfo.End 297097a140dSpatrick << ", livein " << BlockInfo.LiveIn << ", liveout " 298097a140dSpatrick << BlockInfo.LiveOut << "\n"; 299097a140dSpatrick } 300097a140dSpatrick } 301097a140dSpatrick 302097a140dSpatrick LLVM_DUMP_METHOD void StackLifetime::dumpLiveRanges() const { 303097a140dSpatrick dbgs() << "Alloca liveness:\n"; 304097a140dSpatrick for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) 305097a140dSpatrick dbgs() << " " << AllocaNo << ": " << LiveRanges[AllocaNo] << "\n"; 306097a140dSpatrick } 307097a140dSpatrick #endif 308097a140dSpatrick 309097a140dSpatrick StackLifetime::StackLifetime(const Function &F, 310097a140dSpatrick ArrayRef<const AllocaInst *> Allocas, 311097a140dSpatrick LivenessType Type) 312097a140dSpatrick : F(F), Type(Type), Allocas(Allocas), NumAllocas(Allocas.size()) { 313097a140dSpatrick LLVM_DEBUG(dumpAllocas()); 314097a140dSpatrick 315097a140dSpatrick for (unsigned I = 0; I < NumAllocas; ++I) 316097a140dSpatrick AllocaNumbering[Allocas[I]] = I; 317097a140dSpatrick 318097a140dSpatrick collectMarkers(); 319097a140dSpatrick } 320097a140dSpatrick 321097a140dSpatrick void StackLifetime::run() { 322*73471bf0Spatrick if (HasUnknownLifetimeStartOrEnd) { 323*73471bf0Spatrick // There is marker which we can't assign to a specific alloca, so we 324*73471bf0Spatrick // fallback to the most conservative results for the type. 325*73471bf0Spatrick switch (Type) { 326*73471bf0Spatrick case LivenessType::May: 327*73471bf0Spatrick LiveRanges.resize(NumAllocas, getFullLiveRange()); 328*73471bf0Spatrick break; 329*73471bf0Spatrick case LivenessType::Must: 330*73471bf0Spatrick LiveRanges.resize(NumAllocas, LiveRange(Instructions.size())); 331*73471bf0Spatrick break; 332*73471bf0Spatrick } 333*73471bf0Spatrick return; 334*73471bf0Spatrick } 335*73471bf0Spatrick 336097a140dSpatrick LiveRanges.resize(NumAllocas, LiveRange(Instructions.size())); 337097a140dSpatrick for (unsigned I = 0; I < NumAllocas; ++I) 338097a140dSpatrick if (!InterestingAllocas.test(I)) 339097a140dSpatrick LiveRanges[I] = getFullLiveRange(); 340097a140dSpatrick 341097a140dSpatrick calculateLocalLiveness(); 342097a140dSpatrick LLVM_DEBUG(dumpBlockLiveness()); 343097a140dSpatrick calculateLiveIntervals(); 344097a140dSpatrick LLVM_DEBUG(dumpLiveRanges()); 345097a140dSpatrick } 346097a140dSpatrick 347097a140dSpatrick class StackLifetime::LifetimeAnnotationWriter 348097a140dSpatrick : public AssemblyAnnotationWriter { 349097a140dSpatrick const StackLifetime &SL; 350097a140dSpatrick 351097a140dSpatrick void printInstrAlive(unsigned InstrNo, formatted_raw_ostream &OS) { 352097a140dSpatrick SmallVector<StringRef, 16> Names; 353097a140dSpatrick for (const auto &KV : SL.AllocaNumbering) { 354097a140dSpatrick if (SL.LiveRanges[KV.getSecond()].test(InstrNo)) 355097a140dSpatrick Names.push_back(KV.getFirst()->getName()); 356097a140dSpatrick } 357097a140dSpatrick llvm::sort(Names); 358097a140dSpatrick OS << " ; Alive: <" << llvm::join(Names, " ") << ">\n"; 359097a140dSpatrick } 360097a140dSpatrick 361097a140dSpatrick void emitBasicBlockStartAnnot(const BasicBlock *BB, 362097a140dSpatrick formatted_raw_ostream &OS) override { 363097a140dSpatrick auto ItBB = SL.BlockInstRange.find(BB); 364097a140dSpatrick if (ItBB == SL.BlockInstRange.end()) 365097a140dSpatrick return; // Unreachable. 366097a140dSpatrick printInstrAlive(ItBB->getSecond().first, OS); 367097a140dSpatrick } 368097a140dSpatrick 369097a140dSpatrick void printInfoComment(const Value &V, formatted_raw_ostream &OS) override { 370097a140dSpatrick const Instruction *Instr = dyn_cast<Instruction>(&V); 371097a140dSpatrick if (!Instr || !SL.isReachable(Instr)) 372097a140dSpatrick return; 373097a140dSpatrick 374097a140dSpatrick SmallVector<StringRef, 16> Names; 375097a140dSpatrick for (const auto &KV : SL.AllocaNumbering) { 376097a140dSpatrick if (SL.isAliveAfter(KV.getFirst(), Instr)) 377097a140dSpatrick Names.push_back(KV.getFirst()->getName()); 378097a140dSpatrick } 379097a140dSpatrick llvm::sort(Names); 380097a140dSpatrick OS << "\n ; Alive: <" << llvm::join(Names, " ") << ">\n"; 381097a140dSpatrick } 382097a140dSpatrick 383097a140dSpatrick public: 384097a140dSpatrick LifetimeAnnotationWriter(const StackLifetime &SL) : SL(SL) {} 385097a140dSpatrick }; 386097a140dSpatrick 387097a140dSpatrick void StackLifetime::print(raw_ostream &OS) { 388097a140dSpatrick LifetimeAnnotationWriter AAW(*this); 389097a140dSpatrick F.print(OS, &AAW); 390097a140dSpatrick } 391097a140dSpatrick 392097a140dSpatrick PreservedAnalyses StackLifetimePrinterPass::run(Function &F, 393097a140dSpatrick FunctionAnalysisManager &AM) { 394097a140dSpatrick SmallVector<const AllocaInst *, 8> Allocas; 395097a140dSpatrick for (auto &I : instructions(F)) 396097a140dSpatrick if (const AllocaInst *AI = dyn_cast<AllocaInst>(&I)) 397097a140dSpatrick Allocas.push_back(AI); 398097a140dSpatrick StackLifetime SL(F, Allocas, Type); 399097a140dSpatrick SL.run(); 400097a140dSpatrick SL.print(OS); 401097a140dSpatrick return PreservedAnalyses::all(); 402097a140dSpatrick } 403