| 1 | //===- StackSafetyAnalysis.cpp - Stack memory safety analysis -------------===// |
| 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 | //===----------------------------------------------------------------------===// |
| 10 | |
| 11 | #include "llvm/Analysis/StackSafetyAnalysis.h" |
| 12 | #include "llvm/ADT/APInt.h" |
| 13 | #include "llvm/ADT/SmallPtrSet.h" |
| 14 | #include "llvm/ADT/SmallVector.h" |
| 15 | #include "llvm/ADT/Statistic.h" |
| 16 | #include "llvm/Analysis/ModuleSummaryAnalysis.h" |
| 17 | #include "llvm/Analysis/ScalarEvolution.h" |
| 18 | #include "llvm/Analysis/StackLifetime.h" |
| 19 | #include "llvm/IR/ConstantRange.h" |
| 20 | #include "llvm/IR/DerivedTypes.h" |
| 21 | #include "llvm/IR/GlobalValue.h" |
| 22 | #include "llvm/IR/InstIterator.h" |
| 23 | #include "llvm/IR/Instruction.h" |
| 24 | #include "llvm/IR/Instructions.h" |
| 25 | #include "llvm/IR/IntrinsicInst.h" |
| 26 | #include "llvm/IR/ModuleSummaryIndex.h" |
| 27 | #include "llvm/InitializePasses.h" |
| 28 | #include "llvm/Support/Casting.h" |
| 29 | #include "llvm/Support/CommandLine.h" |
| 30 | #include "llvm/Support/FormatVariadic.h" |
| 31 | #include "llvm/Support/raw_ostream.h" |
| 32 | #include <algorithm> |
| 33 | #include <memory> |
| 34 | #include <tuple> |
| 35 | |
| 36 | using namespace llvm; |
| 37 | |
| 38 | #define DEBUG_TYPE "stack-safety" |
| 39 | |
| 40 | STATISTIC(NumAllocaStackSafe, "Number of safe allocas" ); |
| 41 | STATISTIC(NumAllocaTotal, "Number of total allocas" ); |
| 42 | |
| 43 | STATISTIC(NumCombinedCalleeLookupTotal, |
| 44 | "Number of total callee lookups on combined index." ); |
| 45 | STATISTIC(NumCombinedCalleeLookupFailed, |
| 46 | "Number of failed callee lookups on combined index." ); |
| 47 | STATISTIC(NumModuleCalleeLookupTotal, |
| 48 | "Number of total callee lookups on module index." ); |
| 49 | STATISTIC(NumModuleCalleeLookupFailed, |
| 50 | "Number of failed callee lookups on module index." ); |
| 51 | STATISTIC(NumCombinedParamAccessesBefore, |
| 52 | "Number of total param accesses before generateParamAccessSummary." ); |
| 53 | STATISTIC(NumCombinedParamAccessesAfter, |
| 54 | "Number of total param accesses after generateParamAccessSummary." ); |
| 55 | STATISTIC(NumCombinedDataFlowNodes, |
| 56 | "Number of total nodes in combined index for dataflow processing." ); |
| 57 | STATISTIC(NumIndexCalleeUnhandled, "Number of index callee which are unhandled." ); |
| 58 | STATISTIC(NumIndexCalleeMultipleWeak, "Number of index callee non-unique weak." ); |
| 59 | STATISTIC(NumIndexCalleeMultipleExternal, "Number of index callee non-unique external." ); |
| 60 | |
| 61 | |
| 62 | static cl::opt<int> StackSafetyMaxIterations("stack-safety-max-iterations" , |
| 63 | cl::init(Val: 20), cl::Hidden); |
| 64 | |
| 65 | static cl::opt<bool> StackSafetyPrint("stack-safety-print" , cl::init(Val: false), |
| 66 | cl::Hidden); |
| 67 | |
| 68 | static cl::opt<bool> StackSafetyRun("stack-safety-run" , cl::init(Val: false), |
| 69 | cl::Hidden); |
| 70 | |
| 71 | namespace { |
| 72 | |
| 73 | // Check if we should bailout for such ranges. |
| 74 | bool isUnsafe(const ConstantRange &R) { |
| 75 | return R.isEmptySet() || R.isFullSet() || R.isUpperSignWrapped(); |
| 76 | } |
| 77 | |
| 78 | ConstantRange addOverflowNever(const ConstantRange &L, const ConstantRange &R) { |
| 79 | assert(!L.isSignWrappedSet()); |
| 80 | assert(!R.isSignWrappedSet()); |
| 81 | if (L.signedAddMayOverflow(Other: R) != |
| 82 | ConstantRange::OverflowResult::NeverOverflows) |
| 83 | return ConstantRange::getFull(BitWidth: L.getBitWidth()); |
| 84 | ConstantRange Result = L.add(Other: R); |
| 85 | assert(!Result.isSignWrappedSet()); |
| 86 | return Result; |
| 87 | } |
| 88 | |
| 89 | ConstantRange unionNoWrap(const ConstantRange &L, const ConstantRange &R) { |
| 90 | assert(!L.isSignWrappedSet()); |
| 91 | assert(!R.isSignWrappedSet()); |
| 92 | auto Result = L.unionWith(CR: R); |
| 93 | // Two non-wrapped sets can produce wrapped. |
| 94 | if (Result.isSignWrappedSet()) |
| 95 | Result = ConstantRange::getFull(BitWidth: Result.getBitWidth()); |
| 96 | return Result; |
| 97 | } |
| 98 | |
| 99 | /// Describes use of address in as a function call argument. |
| 100 | template <typename CalleeTy> struct CallInfo { |
| 101 | /// Function being called. |
| 102 | const CalleeTy *Callee = nullptr; |
| 103 | /// Index of argument which pass address. |
| 104 | size_t ParamNo = 0; |
| 105 | |
| 106 | CallInfo(const CalleeTy *Callee, size_t ParamNo) |
| 107 | : Callee(Callee), ParamNo(ParamNo) {} |
| 108 | |
| 109 | struct Less { |
| 110 | bool operator()(const CallInfo &L, const CallInfo &R) const { |
| 111 | return std::tie(L.ParamNo, L.Callee) < std::tie(R.ParamNo, R.Callee); |
| 112 | } |
| 113 | }; |
| 114 | }; |
| 115 | |
| 116 | /// Describe uses of address (alloca or parameter) inside of the function. |
| 117 | template <typename CalleeTy> struct UseInfo { |
| 118 | // Access range if the address (alloca or parameters). |
| 119 | // It is allowed to be empty-set when there are no known accesses. |
| 120 | ConstantRange Range; |
| 121 | std::set<const Instruction *> UnsafeAccesses; |
| 122 | |
| 123 | // List of calls which pass address as an argument. |
| 124 | // Value is offset range of address from base address (alloca or calling |
| 125 | // function argument). Range should never set to empty-set, that is an invalid |
| 126 | // access range that can cause empty-set to be propagated with |
| 127 | // ConstantRange::add |
| 128 | using CallsTy = std::map<CallInfo<CalleeTy>, ConstantRange, |
| 129 | typename CallInfo<CalleeTy>::Less>; |
| 130 | CallsTy Calls; |
| 131 | |
| 132 | UseInfo(unsigned PointerSize) : Range{PointerSize, false} {} |
| 133 | |
| 134 | void updateRange(const ConstantRange &R) { Range = unionNoWrap(L: Range, R); } |
| 135 | void addRange(const Instruction *I, const ConstantRange &R, bool IsSafe) { |
| 136 | if (!IsSafe) |
| 137 | UnsafeAccesses.insert(x: I); |
| 138 | updateRange(R); |
| 139 | } |
| 140 | }; |
| 141 | |
| 142 | template <typename CalleeTy> |
| 143 | raw_ostream &operator<<(raw_ostream &OS, const UseInfo<CalleeTy> &U) { |
| 144 | OS << U.Range; |
| 145 | for (auto &Call : U.Calls) |
| 146 | OS << ", " |
| 147 | << "@" << Call.first.Callee->getName() << "(arg" << Call.first.ParamNo |
| 148 | << ", " << Call.second << ")" ; |
| 149 | return OS; |
| 150 | } |
| 151 | |
| 152 | /// Calculate the allocation size of a given alloca. Returns empty range |
| 153 | // in case of confution. |
| 154 | ConstantRange getStaticAllocaSizeRange(const AllocaInst &AI) { |
| 155 | const DataLayout &DL = AI.getDataLayout(); |
| 156 | TypeSize TS = DL.getTypeAllocSize(Ty: AI.getAllocatedType()); |
| 157 | unsigned PointerSize = DL.getPointerTypeSizeInBits(AI.getType()); |
| 158 | // Fallback to empty range for alloca size. |
| 159 | ConstantRange R = ConstantRange::getEmpty(BitWidth: PointerSize); |
| 160 | if (TS.isScalable()) |
| 161 | return R; |
| 162 | APInt APSize(PointerSize, TS.getFixedValue(), true); |
| 163 | if (APSize.isNonPositive()) |
| 164 | return R; |
| 165 | if (AI.isArrayAllocation()) { |
| 166 | const auto *C = dyn_cast<ConstantInt>(Val: AI.getArraySize()); |
| 167 | if (!C) |
| 168 | return R; |
| 169 | bool Overflow = false; |
| 170 | APInt Mul = C->getValue(); |
| 171 | if (Mul.isNonPositive()) |
| 172 | return R; |
| 173 | Mul = Mul.sextOrTrunc(width: PointerSize); |
| 174 | APSize = APSize.smul_ov(RHS: Mul, Overflow); |
| 175 | if (Overflow) |
| 176 | return R; |
| 177 | } |
| 178 | R = ConstantRange(APInt::getZero(numBits: PointerSize), APSize); |
| 179 | assert(!isUnsafe(R)); |
| 180 | return R; |
| 181 | } |
| 182 | |
| 183 | template <typename CalleeTy> struct FunctionInfo { |
| 184 | std::map<const AllocaInst *, UseInfo<CalleeTy>> Allocas; |
| 185 | std::map<uint32_t, UseInfo<CalleeTy>> Params; |
| 186 | // TODO: describe return value as depending on one or more of its arguments. |
| 187 | |
| 188 | // StackSafetyDataFlowAnalysis counter stored here for faster access. |
| 189 | int UpdateCount = 0; |
| 190 | |
| 191 | void print(raw_ostream &O, StringRef Name, const Function *F) const { |
| 192 | // TODO: Consider different printout format after |
| 193 | // StackSafetyDataFlowAnalysis. Calls and parameters are irrelevant then. |
| 194 | O << " @" << Name << ((F && F->isDSOLocal()) ? "" : " dso_preemptable" ) |
| 195 | << ((F && F->isInterposable()) ? " interposable" : "" ) << "\n" ; |
| 196 | |
| 197 | O << " args uses:\n" ; |
| 198 | for (auto &KV : Params) { |
| 199 | O << " " ; |
| 200 | if (F) |
| 201 | O << F->getArg(i: KV.first)->getName(); |
| 202 | else |
| 203 | O << formatv("arg{0}" , KV.first); |
| 204 | O << "[]: " << KV.second << "\n" ; |
| 205 | } |
| 206 | |
| 207 | O << " allocas uses:\n" ; |
| 208 | if (F) { |
| 209 | for (const auto &I : instructions(F)) { |
| 210 | if (const AllocaInst *AI = dyn_cast<AllocaInst>(Val: &I)) { |
| 211 | auto &AS = Allocas.find(AI)->second; |
| 212 | O << " " << AI->getName() << "[" |
| 213 | << getStaticAllocaSizeRange(AI: *AI).getUpper() << "]: " << AS << "\n" ; |
| 214 | } |
| 215 | } |
| 216 | } else { |
| 217 | assert(Allocas.empty()); |
| 218 | } |
| 219 | } |
| 220 | }; |
| 221 | |
| 222 | using GVToSSI = std::map<const GlobalValue *, FunctionInfo<GlobalValue>>; |
| 223 | |
| 224 | } // namespace |
| 225 | |
| 226 | struct StackSafetyInfo::InfoTy { |
| 227 | FunctionInfo<GlobalValue> Info; |
| 228 | }; |
| 229 | |
| 230 | struct StackSafetyGlobalInfo::InfoTy { |
| 231 | GVToSSI Info; |
| 232 | SmallPtrSet<const AllocaInst *, 8> SafeAllocas; |
| 233 | std::set<const Instruction *> UnsafeAccesses; |
| 234 | }; |
| 235 | |
| 236 | namespace { |
| 237 | |
| 238 | class StackSafetyLocalAnalysis { |
| 239 | Function &F; |
| 240 | const DataLayout &DL; |
| 241 | ScalarEvolution &SE; |
| 242 | unsigned PointerSize = 0; |
| 243 | |
| 244 | const ConstantRange UnknownRange; |
| 245 | |
| 246 | /// FIXME: This function is a bandaid, it's only needed |
| 247 | /// because this pass doesn't handle address spaces of different pointer |
| 248 | /// sizes. |
| 249 | /// |
| 250 | /// \returns \p Val's SCEV as a pointer of AS zero, or nullptr if it can't be |
| 251 | /// converted to AS 0. |
| 252 | const SCEV *getSCEVAsPointer(Value *Val); |
| 253 | |
| 254 | ConstantRange offsetFrom(Value *Addr, Value *Base); |
| 255 | ConstantRange getAccessRange(Value *Addr, Value *Base, |
| 256 | const ConstantRange &SizeRange); |
| 257 | ConstantRange getAccessRange(Value *Addr, Value *Base, TypeSize Size); |
| 258 | ConstantRange getMemIntrinsicAccessRange(const MemIntrinsic *MI, const Use &U, |
| 259 | Value *Base); |
| 260 | |
| 261 | void analyzeAllUses(Value *Ptr, UseInfo<GlobalValue> &AS, |
| 262 | const StackLifetime &SL); |
| 263 | |
| 264 | |
| 265 | bool isSafeAccess(const Use &U, AllocaInst *AI, const SCEV *AccessSize); |
| 266 | bool isSafeAccess(const Use &U, AllocaInst *AI, Value *V); |
| 267 | bool isSafeAccess(const Use &U, AllocaInst *AI, TypeSize AccessSize); |
| 268 | |
| 269 | public: |
| 270 | StackSafetyLocalAnalysis(Function &F, ScalarEvolution &SE) |
| 271 | : F(F), DL(F.getDataLayout()), SE(SE), |
| 272 | PointerSize(DL.getPointerSizeInBits()), |
| 273 | UnknownRange(PointerSize, true) {} |
| 274 | |
| 275 | // Run the transformation on the associated function. |
| 276 | FunctionInfo<GlobalValue> run(); |
| 277 | }; |
| 278 | |
| 279 | const SCEV *StackSafetyLocalAnalysis::getSCEVAsPointer(Value *Val) { |
| 280 | Type *ValTy = Val->getType(); |
| 281 | |
| 282 | // We don't handle targets with multiple address spaces. |
| 283 | if (!ValTy->isPointerTy()) { |
| 284 | auto *PtrTy = PointerType::getUnqual(C&: SE.getContext()); |
| 285 | return SE.getTruncateOrZeroExtend(V: SE.getSCEV(V: Val), Ty: PtrTy); |
| 286 | } |
| 287 | |
| 288 | if (ValTy->getPointerAddressSpace() != 0) |
| 289 | return nullptr; |
| 290 | return SE.getSCEV(V: Val); |
| 291 | } |
| 292 | |
| 293 | ConstantRange StackSafetyLocalAnalysis::offsetFrom(Value *Addr, Value *Base) { |
| 294 | if (!SE.isSCEVable(Ty: Addr->getType()) || !SE.isSCEVable(Ty: Base->getType())) |
| 295 | return UnknownRange; |
| 296 | |
| 297 | const SCEV *AddrExp = getSCEVAsPointer(Val: Addr); |
| 298 | const SCEV *BaseExp = getSCEVAsPointer(Val: Base); |
| 299 | if (!AddrExp || !BaseExp) |
| 300 | return UnknownRange; |
| 301 | |
| 302 | const SCEV *Diff = SE.getMinusSCEV(LHS: AddrExp, RHS: BaseExp); |
| 303 | if (isa<SCEVCouldNotCompute>(Val: Diff)) |
| 304 | return UnknownRange; |
| 305 | |
| 306 | ConstantRange Offset = SE.getSignedRange(S: Diff); |
| 307 | if (isUnsafe(R: Offset)) |
| 308 | return UnknownRange; |
| 309 | return Offset.sextOrTrunc(BitWidth: PointerSize); |
| 310 | } |
| 311 | |
| 312 | ConstantRange |
| 313 | StackSafetyLocalAnalysis::getAccessRange(Value *Addr, Value *Base, |
| 314 | const ConstantRange &SizeRange) { |
| 315 | // Zero-size loads and stores do not access memory. |
| 316 | if (SizeRange.isEmptySet()) |
| 317 | return ConstantRange::getEmpty(BitWidth: PointerSize); |
| 318 | assert(!isUnsafe(SizeRange)); |
| 319 | |
| 320 | ConstantRange Offsets = offsetFrom(Addr, Base); |
| 321 | if (isUnsafe(R: Offsets)) |
| 322 | return UnknownRange; |
| 323 | |
| 324 | Offsets = addOverflowNever(L: Offsets, R: SizeRange); |
| 325 | if (isUnsafe(R: Offsets)) |
| 326 | return UnknownRange; |
| 327 | return Offsets; |
| 328 | } |
| 329 | |
| 330 | ConstantRange StackSafetyLocalAnalysis::getAccessRange(Value *Addr, Value *Base, |
| 331 | TypeSize Size) { |
| 332 | if (Size.isScalable()) |
| 333 | return UnknownRange; |
| 334 | APInt APSize(PointerSize, Size.getFixedValue(), true); |
| 335 | if (APSize.isNegative()) |
| 336 | return UnknownRange; |
| 337 | return getAccessRange(Addr, Base, |
| 338 | SizeRange: ConstantRange(APInt::getZero(numBits: PointerSize), APSize)); |
| 339 | } |
| 340 | |
| 341 | ConstantRange StackSafetyLocalAnalysis::getMemIntrinsicAccessRange( |
| 342 | const MemIntrinsic *MI, const Use &U, Value *Base) { |
| 343 | if (const auto *MTI = dyn_cast<MemTransferInst>(Val: MI)) { |
| 344 | if (MTI->getRawSource() != U && MTI->getRawDest() != U) |
| 345 | return ConstantRange::getEmpty(BitWidth: PointerSize); |
| 346 | } else { |
| 347 | if (MI->getRawDest() != U) |
| 348 | return ConstantRange::getEmpty(BitWidth: PointerSize); |
| 349 | } |
| 350 | |
| 351 | auto *CalculationTy = IntegerType::getIntNTy(C&: SE.getContext(), N: PointerSize); |
| 352 | if (!SE.isSCEVable(Ty: MI->getLength()->getType())) |
| 353 | return UnknownRange; |
| 354 | |
| 355 | const SCEV *Expr = |
| 356 | SE.getTruncateOrZeroExtend(V: SE.getSCEV(V: MI->getLength()), Ty: CalculationTy); |
| 357 | ConstantRange Sizes = SE.getSignedRange(S: Expr); |
| 358 | if (!Sizes.getUpper().isStrictlyPositive() || isUnsafe(R: Sizes)) |
| 359 | return UnknownRange; |
| 360 | Sizes = Sizes.sextOrTrunc(BitWidth: PointerSize); |
| 361 | ConstantRange SizeRange(APInt::getZero(numBits: PointerSize), Sizes.getUpper() - 1); |
| 362 | return getAccessRange(Addr: U, Base, SizeRange); |
| 363 | } |
| 364 | |
| 365 | bool StackSafetyLocalAnalysis::isSafeAccess(const Use &U, AllocaInst *AI, |
| 366 | Value *V) { |
| 367 | return isSafeAccess(U, AI, AccessSize: SE.getSCEV(V)); |
| 368 | } |
| 369 | |
| 370 | bool StackSafetyLocalAnalysis::isSafeAccess(const Use &U, AllocaInst *AI, |
| 371 | TypeSize TS) { |
| 372 | if (TS.isScalable()) |
| 373 | return false; |
| 374 | auto *CalculationTy = IntegerType::getIntNTy(C&: SE.getContext(), N: PointerSize); |
| 375 | const SCEV *SV = SE.getConstant(Ty: CalculationTy, V: TS.getFixedValue()); |
| 376 | return isSafeAccess(U, AI, AccessSize: SV); |
| 377 | } |
| 378 | |
| 379 | bool StackSafetyLocalAnalysis::isSafeAccess(const Use &U, AllocaInst *AI, |
| 380 | const SCEV *AccessSize) { |
| 381 | |
| 382 | if (!AI) |
| 383 | return true; // This only judges whether it is a safe *stack* access. |
| 384 | if (isa<SCEVCouldNotCompute>(Val: AccessSize)) |
| 385 | return false; |
| 386 | |
| 387 | const auto *I = cast<Instruction>(Val: U.getUser()); |
| 388 | |
| 389 | const SCEV *AddrExp = getSCEVAsPointer(Val: U.get()); |
| 390 | const SCEV *BaseExp = getSCEVAsPointer(Val: AI); |
| 391 | if (!AddrExp || !BaseExp) |
| 392 | return false; |
| 393 | |
| 394 | const SCEV *Diff = SE.getMinusSCEV(LHS: AddrExp, RHS: BaseExp); |
| 395 | if (isa<SCEVCouldNotCompute>(Val: Diff)) |
| 396 | return false; |
| 397 | |
| 398 | auto Size = getStaticAllocaSizeRange(AI: *AI); |
| 399 | |
| 400 | auto *CalculationTy = IntegerType::getIntNTy(C&: SE.getContext(), N: PointerSize); |
| 401 | auto ToDiffTy = [&](const SCEV *V) { |
| 402 | return SE.getTruncateOrZeroExtend(V, Ty: CalculationTy); |
| 403 | }; |
| 404 | const SCEV *Min = ToDiffTy(SE.getConstant(Val: Size.getLower())); |
| 405 | const SCEV *Max = SE.getMinusSCEV(LHS: ToDiffTy(SE.getConstant(Val: Size.getUpper())), |
| 406 | RHS: ToDiffTy(AccessSize)); |
| 407 | return SE.evaluatePredicateAt(Pred: ICmpInst::Predicate::ICMP_SGE, LHS: Diff, RHS: Min, CtxI: I) |
| 408 | .value_or(u: false) && |
| 409 | SE.evaluatePredicateAt(Pred: ICmpInst::Predicate::ICMP_SLE, LHS: Diff, RHS: Max, CtxI: I) |
| 410 | .value_or(u: false); |
| 411 | } |
| 412 | |
| 413 | /// The function analyzes all local uses of Ptr (alloca or argument) and |
| 414 | /// calculates local access range and all function calls where it was used. |
| 415 | void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr, |
| 416 | UseInfo<GlobalValue> &US, |
| 417 | const StackLifetime &SL) { |
| 418 | SmallPtrSet<const Value *, 16> Visited; |
| 419 | SmallVector<const Value *, 8> WorkList; |
| 420 | WorkList.push_back(Elt: Ptr); |
| 421 | AllocaInst *AI = dyn_cast<AllocaInst>(Val: Ptr); |
| 422 | |
| 423 | // A DFS search through all uses of the alloca in bitcasts/PHI/GEPs/etc. |
| 424 | while (!WorkList.empty()) { |
| 425 | const Value *V = WorkList.pop_back_val(); |
| 426 | for (const Use &UI : V->uses()) { |
| 427 | const auto *I = cast<Instruction>(Val: UI.getUser()); |
| 428 | if (!SL.isReachable(I)) |
| 429 | continue; |
| 430 | |
| 431 | assert(V == UI.get()); |
| 432 | |
| 433 | auto RecordStore = [&](const Value* StoredVal) { |
| 434 | if (V == StoredVal) { |
| 435 | // Stored the pointer - conservatively assume it may be unsafe. |
| 436 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 437 | return; |
| 438 | } |
| 439 | if (AI && !SL.isAliveAfter(AI, I)) { |
| 440 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 441 | return; |
| 442 | } |
| 443 | auto TypeSize = DL.getTypeStoreSize(Ty: StoredVal->getType()); |
| 444 | auto AccessRange = getAccessRange(Addr: UI, Base: Ptr, Size: TypeSize); |
| 445 | bool Safe = isSafeAccess(U: UI, AI, TS: TypeSize); |
| 446 | US.addRange(I, R: AccessRange, IsSafe: Safe); |
| 447 | return; |
| 448 | }; |
| 449 | |
| 450 | switch (I->getOpcode()) { |
| 451 | case Instruction::Load: { |
| 452 | if (AI && !SL.isAliveAfter(AI, I)) { |
| 453 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 454 | break; |
| 455 | } |
| 456 | auto TypeSize = DL.getTypeStoreSize(Ty: I->getType()); |
| 457 | auto AccessRange = getAccessRange(Addr: UI, Base: Ptr, Size: TypeSize); |
| 458 | bool Safe = isSafeAccess(U: UI, AI, TS: TypeSize); |
| 459 | US.addRange(I, R: AccessRange, IsSafe: Safe); |
| 460 | break; |
| 461 | } |
| 462 | |
| 463 | case Instruction::VAArg: |
| 464 | // "va-arg" from a pointer is safe. |
| 465 | break; |
| 466 | case Instruction::Store: |
| 467 | RecordStore(cast<StoreInst>(Val: I)->getValueOperand()); |
| 468 | break; |
| 469 | case Instruction::AtomicCmpXchg: |
| 470 | RecordStore(cast<AtomicCmpXchgInst>(Val: I)->getNewValOperand()); |
| 471 | break; |
| 472 | case Instruction::AtomicRMW: |
| 473 | RecordStore(cast<AtomicRMWInst>(Val: I)->getValOperand()); |
| 474 | break; |
| 475 | |
| 476 | case Instruction::Ret: |
| 477 | // Information leak. |
| 478 | // FIXME: Process parameters correctly. This is a leak only if we return |
| 479 | // alloca. |
| 480 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 481 | break; |
| 482 | |
| 483 | case Instruction::Call: |
| 484 | case Instruction::Invoke: { |
| 485 | if (I->isLifetimeStartOrEnd()) |
| 486 | break; |
| 487 | |
| 488 | if (AI && !SL.isAliveAfter(AI, I)) { |
| 489 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 490 | break; |
| 491 | } |
| 492 | if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(Val: I)) { |
| 493 | auto AccessRange = getMemIntrinsicAccessRange(MI, U: UI, Base: Ptr); |
| 494 | bool Safe = false; |
| 495 | if (const auto *MTI = dyn_cast<MemTransferInst>(Val: MI)) { |
| 496 | if (MTI->getRawSource() != UI && MTI->getRawDest() != UI) |
| 497 | Safe = true; |
| 498 | } else if (MI->getRawDest() != UI) { |
| 499 | Safe = true; |
| 500 | } |
| 501 | Safe = Safe || isSafeAccess(U: UI, AI, V: MI->getLength()); |
| 502 | US.addRange(I, R: AccessRange, IsSafe: Safe); |
| 503 | break; |
| 504 | } |
| 505 | |
| 506 | const auto &CB = cast<CallBase>(Val: *I); |
| 507 | if (CB.getReturnedArgOperand() == V) { |
| 508 | if (Visited.insert(Ptr: I).second) |
| 509 | WorkList.push_back(Elt: cast<const Instruction>(Val: I)); |
| 510 | } |
| 511 | |
| 512 | if (!CB.isArgOperand(U: &UI)) { |
| 513 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 514 | break; |
| 515 | } |
| 516 | |
| 517 | unsigned ArgNo = CB.getArgOperandNo(U: &UI); |
| 518 | if (CB.isByValArgument(ArgNo)) { |
| 519 | auto TypeSize = DL.getTypeStoreSize(Ty: CB.getParamByValType(ArgNo)); |
| 520 | auto AccessRange = getAccessRange(Addr: UI, Base: Ptr, Size: TypeSize); |
| 521 | bool Safe = isSafeAccess(U: UI, AI, TS: TypeSize); |
| 522 | US.addRange(I, R: AccessRange, IsSafe: Safe); |
| 523 | break; |
| 524 | } |
| 525 | |
| 526 | // FIXME: consult devirt? |
| 527 | // Do not follow aliases, otherwise we could inadvertently follow |
| 528 | // dso_preemptable aliases or aliases with interposable linkage. |
| 529 | const GlobalValue *Callee = |
| 530 | dyn_cast<GlobalValue>(Val: CB.getCalledOperand()->stripPointerCasts()); |
| 531 | if (!Callee || isa<GlobalIFunc>(Val: Callee)) { |
| 532 | US.addRange(I, R: UnknownRange, /*IsSafe=*/false); |
| 533 | break; |
| 534 | } |
| 535 | |
| 536 | assert(isa<Function>(Callee) || isa<GlobalAlias>(Callee)); |
| 537 | ConstantRange Offsets = offsetFrom(Addr: UI, Base: Ptr); |
| 538 | auto Insert = |
| 539 | US.Calls.emplace(args: CallInfo<GlobalValue>(Callee, ArgNo), args&: Offsets); |
| 540 | if (!Insert.second) |
| 541 | Insert.first->second = Insert.first->second.unionWith(CR: Offsets); |
| 542 | break; |
| 543 | } |
| 544 | |
| 545 | default: |
| 546 | if (Visited.insert(Ptr: I).second) |
| 547 | WorkList.push_back(Elt: cast<const Instruction>(Val: I)); |
| 548 | } |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | FunctionInfo<GlobalValue> StackSafetyLocalAnalysis::run() { |
| 554 | FunctionInfo<GlobalValue> Info; |
| 555 | assert(!F.isDeclaration() && |
| 556 | "Can't run StackSafety on a function declaration" ); |
| 557 | |
| 558 | LLVM_DEBUG(dbgs() << "[StackSafety] " << F.getName() << "\n" ); |
| 559 | |
| 560 | SmallVector<AllocaInst *, 64> Allocas; |
| 561 | for (auto &I : instructions(F)) |
| 562 | if (auto *AI = dyn_cast<AllocaInst>(Val: &I)) |
| 563 | Allocas.push_back(Elt: AI); |
| 564 | StackLifetime SL(F, Allocas, StackLifetime::LivenessType::Must); |
| 565 | SL.run(); |
| 566 | |
| 567 | for (auto *AI : Allocas) { |
| 568 | auto &UI = Info.Allocas.emplace(args&: AI, args&: PointerSize).first->second; |
| 569 | analyzeAllUses(Ptr: AI, US&: UI, SL); |
| 570 | } |
| 571 | |
| 572 | for (Argument &A : F.args()) { |
| 573 | // Non pointers and bypass arguments are not going to be used in any global |
| 574 | // processing. |
| 575 | if (A.getType()->isPointerTy() && !A.hasByValAttr()) { |
| 576 | auto &UI = Info.Params.emplace(args: A.getArgNo(), args&: PointerSize).first->second; |
| 577 | analyzeAllUses(Ptr: &A, US&: UI, SL); |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | LLVM_DEBUG(Info.print(dbgs(), F.getName(), &F)); |
| 582 | LLVM_DEBUG(dbgs() << "\n[StackSafety] done\n" ); |
| 583 | return Info; |
| 584 | } |
| 585 | |
| 586 | template <typename CalleeTy> class StackSafetyDataFlowAnalysis { |
| 587 | using FunctionMap = std::map<const CalleeTy *, FunctionInfo<CalleeTy>>; |
| 588 | |
| 589 | FunctionMap Functions; |
| 590 | const ConstantRange UnknownRange; |
| 591 | |
| 592 | // Callee-to-Caller multimap. |
| 593 | DenseMap<const CalleeTy *, SmallVector<const CalleeTy *, 4>> Callers; |
| 594 | SetVector<const CalleeTy *> WorkList; |
| 595 | |
| 596 | bool updateOneUse(UseInfo<CalleeTy> &US, bool UpdateToFullSet); |
| 597 | void updateOneNode(const CalleeTy *Callee, FunctionInfo<CalleeTy> &FS); |
| 598 | void updateOneNode(const CalleeTy *Callee) { |
| 599 | updateOneNode(Callee, Functions.find(Callee)->second); |
| 600 | } |
| 601 | void updateAllNodes() { |
| 602 | for (auto &F : Functions) |
| 603 | updateOneNode(F.first, F.second); |
| 604 | } |
| 605 | void runDataFlow(); |
| 606 | #ifndef NDEBUG |
| 607 | void verifyFixedPoint(); |
| 608 | #endif |
| 609 | |
| 610 | public: |
| 611 | StackSafetyDataFlowAnalysis(uint32_t PointerBitWidth, FunctionMap Functions) |
| 612 | : Functions(std::move(Functions)), |
| 613 | UnknownRange(ConstantRange::getFull(PointerBitWidth)) {} |
| 614 | |
| 615 | const FunctionMap &run(); |
| 616 | |
| 617 | ConstantRange getArgumentAccessRange(const CalleeTy *Callee, unsigned ParamNo, |
| 618 | const ConstantRange &Offsets) const; |
| 619 | }; |
| 620 | |
| 621 | template <typename CalleeTy> |
| 622 | ConstantRange StackSafetyDataFlowAnalysis<CalleeTy>::getArgumentAccessRange( |
| 623 | const CalleeTy *Callee, unsigned ParamNo, |
| 624 | const ConstantRange &Offsets) const { |
| 625 | auto FnIt = Functions.find(Callee); |
| 626 | // Unknown callee (outside of LTO domain or an indirect call). |
| 627 | if (FnIt == Functions.end()) |
| 628 | return UnknownRange; |
| 629 | auto &FS = FnIt->second; |
| 630 | auto ParamIt = FS.Params.find(ParamNo); |
| 631 | if (ParamIt == FS.Params.end()) |
| 632 | return UnknownRange; |
| 633 | auto &Access = ParamIt->second.Range; |
| 634 | if (Access.isEmptySet()) |
| 635 | return Access; |
| 636 | if (Access.isFullSet()) |
| 637 | return UnknownRange; |
| 638 | return addOverflowNever(Access, Offsets); |
| 639 | } |
| 640 | |
| 641 | template <typename CalleeTy> |
| 642 | bool StackSafetyDataFlowAnalysis<CalleeTy>::updateOneUse(UseInfo<CalleeTy> &US, |
| 643 | bool UpdateToFullSet) { |
| 644 | bool Changed = false; |
| 645 | for (auto &KV : US.Calls) { |
| 646 | assert(!KV.second.isEmptySet() && |
| 647 | "Param range can't be empty-set, invalid offset range" ); |
| 648 | |
| 649 | ConstantRange CalleeRange = |
| 650 | getArgumentAccessRange(Callee: KV.first.Callee, ParamNo: KV.first.ParamNo, Offsets: KV.second); |
| 651 | if (!US.Range.contains(CalleeRange)) { |
| 652 | Changed = true; |
| 653 | if (UpdateToFullSet) |
| 654 | US.Range = UnknownRange; |
| 655 | else |
| 656 | US.updateRange(CalleeRange); |
| 657 | } |
| 658 | } |
| 659 | return Changed; |
| 660 | } |
| 661 | |
| 662 | template <typename CalleeTy> |
| 663 | void StackSafetyDataFlowAnalysis<CalleeTy>::updateOneNode( |
| 664 | const CalleeTy *Callee, FunctionInfo<CalleeTy> &FS) { |
| 665 | bool UpdateToFullSet = FS.UpdateCount > StackSafetyMaxIterations; |
| 666 | bool Changed = false; |
| 667 | for (auto &KV : FS.Params) |
| 668 | Changed |= updateOneUse(US&: KV.second, UpdateToFullSet); |
| 669 | |
| 670 | if (Changed) { |
| 671 | LLVM_DEBUG(dbgs() << "=== update [" << FS.UpdateCount |
| 672 | << (UpdateToFullSet ? ", full-set" : "" ) << "] " << &FS |
| 673 | << "\n" ); |
| 674 | // Callers of this function may need updating. |
| 675 | WorkList.insert_range(Callers[Callee]); |
| 676 | |
| 677 | ++FS.UpdateCount; |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | template <typename CalleeTy> |
| 682 | void StackSafetyDataFlowAnalysis<CalleeTy>::runDataFlow() { |
| 683 | SmallVector<const CalleeTy *, 16> Callees; |
| 684 | for (auto &F : Functions) { |
| 685 | Callees.clear(); |
| 686 | auto &FS = F.second; |
| 687 | for (auto &KV : FS.Params) |
| 688 | for (auto &CS : KV.second.Calls) |
| 689 | Callees.push_back(CS.first.Callee); |
| 690 | |
| 691 | llvm::sort(Callees); |
| 692 | Callees.erase(llvm::unique(Callees), Callees.end()); |
| 693 | |
| 694 | for (auto &Callee : Callees) |
| 695 | Callers[Callee].push_back(F.first); |
| 696 | } |
| 697 | |
| 698 | updateAllNodes(); |
| 699 | |
| 700 | while (!WorkList.empty()) { |
| 701 | const CalleeTy *Callee = WorkList.pop_back_val(); |
| 702 | updateOneNode(Callee); |
| 703 | } |
| 704 | } |
| 705 | |
| 706 | #ifndef NDEBUG |
| 707 | template <typename CalleeTy> |
| 708 | void StackSafetyDataFlowAnalysis<CalleeTy>::verifyFixedPoint() { |
| 709 | WorkList.clear(); |
| 710 | updateAllNodes(); |
| 711 | assert(WorkList.empty()); |
| 712 | } |
| 713 | #endif |
| 714 | |
| 715 | template <typename CalleeTy> |
| 716 | const typename StackSafetyDataFlowAnalysis<CalleeTy>::FunctionMap & |
| 717 | StackSafetyDataFlowAnalysis<CalleeTy>::run() { |
| 718 | runDataFlow(); |
| 719 | LLVM_DEBUG(verifyFixedPoint()); |
| 720 | return Functions; |
| 721 | } |
| 722 | |
| 723 | FunctionSummary *findCalleeFunctionSummary(ValueInfo VI, StringRef ModuleId) { |
| 724 | if (!VI) |
| 725 | return nullptr; |
| 726 | auto SummaryList = VI.getSummaryList(); |
| 727 | GlobalValueSummary* S = nullptr; |
| 728 | for (const auto& GVS : SummaryList) { |
| 729 | if (!GVS->isLive()) |
| 730 | continue; |
| 731 | if (const AliasSummary *AS = dyn_cast<AliasSummary>(Val: GVS.get())) |
| 732 | if (!AS->hasAliasee()) |
| 733 | continue; |
| 734 | if (!isa<FunctionSummary>(Val: GVS->getBaseObject())) |
| 735 | continue; |
| 736 | if (GlobalValue::isLocalLinkage(Linkage: GVS->linkage())) { |
| 737 | if (GVS->modulePath() == ModuleId) { |
| 738 | S = GVS.get(); |
| 739 | break; |
| 740 | } |
| 741 | } else if (GlobalValue::isExternalLinkage(Linkage: GVS->linkage())) { |
| 742 | if (S) { |
| 743 | ++NumIndexCalleeMultipleExternal; |
| 744 | return nullptr; |
| 745 | } |
| 746 | S = GVS.get(); |
| 747 | } else if (GlobalValue::isWeakLinkage(Linkage: GVS->linkage())) { |
| 748 | if (S) { |
| 749 | ++NumIndexCalleeMultipleWeak; |
| 750 | return nullptr; |
| 751 | } |
| 752 | S = GVS.get(); |
| 753 | } else if (GlobalValue::isAvailableExternallyLinkage(Linkage: GVS->linkage()) || |
| 754 | GlobalValue::isLinkOnceLinkage(Linkage: GVS->linkage())) { |
| 755 | if (SummaryList.size() == 1) |
| 756 | S = GVS.get(); |
| 757 | // According thinLTOResolvePrevailingGUID these are unlikely prevailing. |
| 758 | } else { |
| 759 | ++NumIndexCalleeUnhandled; |
| 760 | } |
| 761 | }; |
| 762 | while (S) { |
| 763 | if (!S->isLive() || !S->isDSOLocal()) |
| 764 | return nullptr; |
| 765 | if (FunctionSummary *FS = dyn_cast<FunctionSummary>(Val: S)) |
| 766 | return FS; |
| 767 | AliasSummary *AS = dyn_cast<AliasSummary>(Val: S); |
| 768 | if (!AS || !AS->hasAliasee()) |
| 769 | return nullptr; |
| 770 | S = AS->getBaseObject(); |
| 771 | if (S == AS) |
| 772 | return nullptr; |
| 773 | } |
| 774 | return nullptr; |
| 775 | } |
| 776 | |
| 777 | const Function *findCalleeInModule(const GlobalValue *GV) { |
| 778 | while (GV) { |
| 779 | if (GV->isDeclaration() || GV->isInterposable() || !GV->isDSOLocal()) |
| 780 | return nullptr; |
| 781 | if (const Function *F = dyn_cast<Function>(Val: GV)) |
| 782 | return F; |
| 783 | const GlobalAlias *A = dyn_cast<GlobalAlias>(Val: GV); |
| 784 | if (!A) |
| 785 | return nullptr; |
| 786 | GV = A->getAliaseeObject(); |
| 787 | if (GV == A) |
| 788 | return nullptr; |
| 789 | } |
| 790 | return nullptr; |
| 791 | } |
| 792 | |
| 793 | const ConstantRange *findParamAccess(const FunctionSummary &FS, |
| 794 | uint32_t ParamNo) { |
| 795 | assert(FS.isLive()); |
| 796 | assert(FS.isDSOLocal()); |
| 797 | for (const auto &PS : FS.paramAccesses()) |
| 798 | if (ParamNo == PS.ParamNo) |
| 799 | return &PS.Use; |
| 800 | return nullptr; |
| 801 | } |
| 802 | |
| 803 | void resolveAllCalls(UseInfo<GlobalValue> &Use, |
| 804 | const ModuleSummaryIndex *Index) { |
| 805 | ConstantRange FullSet(Use.Range.getBitWidth(), true); |
| 806 | // Move Use.Calls to a temp storage and repopulate - don't use std::move as it |
| 807 | // leaves Use.Calls in an undefined state. |
| 808 | UseInfo<GlobalValue>::CallsTy TmpCalls; |
| 809 | std::swap(x&: TmpCalls, y&: Use.Calls); |
| 810 | for (const auto &C : TmpCalls) { |
| 811 | const Function *F = findCalleeInModule(GV: C.first.Callee); |
| 812 | if (F) { |
| 813 | Use.Calls.emplace(args: CallInfo<GlobalValue>(F, C.first.ParamNo), args: C.second); |
| 814 | continue; |
| 815 | } |
| 816 | |
| 817 | if (!Index) |
| 818 | return Use.updateRange(R: FullSet); |
| 819 | FunctionSummary *FS = |
| 820 | findCalleeFunctionSummary(VI: Index->getValueInfo(GUID: C.first.Callee->getGUID()), |
| 821 | ModuleId: C.first.Callee->getParent()->getModuleIdentifier()); |
| 822 | ++NumModuleCalleeLookupTotal; |
| 823 | if (!FS) { |
| 824 | ++NumModuleCalleeLookupFailed; |
| 825 | return Use.updateRange(R: FullSet); |
| 826 | } |
| 827 | const ConstantRange *Found = findParamAccess(FS: *FS, ParamNo: C.first.ParamNo); |
| 828 | if (!Found || Found->isFullSet()) |
| 829 | return Use.updateRange(R: FullSet); |
| 830 | ConstantRange Access = Found->sextOrTrunc(BitWidth: Use.Range.getBitWidth()); |
| 831 | if (!Access.isEmptySet()) |
| 832 | Use.updateRange(R: addOverflowNever(L: Access, R: C.second)); |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | GVToSSI createGlobalStackSafetyInfo( |
| 837 | std::map<const GlobalValue *, FunctionInfo<GlobalValue>> Functions, |
| 838 | const ModuleSummaryIndex *Index) { |
| 839 | GVToSSI SSI; |
| 840 | if (Functions.empty()) |
| 841 | return SSI; |
| 842 | |
| 843 | // FIXME: Simplify printing and remove copying here. |
| 844 | auto Copy = Functions; |
| 845 | |
| 846 | for (auto &FnKV : Copy) |
| 847 | for (auto &KV : FnKV.second.Params) { |
| 848 | resolveAllCalls(Use&: KV.second, Index); |
| 849 | if (KV.second.Range.isFullSet()) |
| 850 | KV.second.Calls.clear(); |
| 851 | } |
| 852 | |
| 853 | uint32_t PointerSize = |
| 854 | Copy.begin()->first->getDataLayout().getPointerSizeInBits(); |
| 855 | StackSafetyDataFlowAnalysis<GlobalValue> SSDFA(PointerSize, std::move(Copy)); |
| 856 | |
| 857 | for (const auto &F : SSDFA.run()) { |
| 858 | auto FI = F.second; |
| 859 | auto &SrcF = Functions[F.first]; |
| 860 | for (auto &KV : FI.Allocas) { |
| 861 | auto &A = KV.second; |
| 862 | resolveAllCalls(Use&: A, Index); |
| 863 | for (auto &C : A.Calls) { |
| 864 | A.updateRange(R: SSDFA.getArgumentAccessRange(Callee: C.first.Callee, |
| 865 | ParamNo: C.first.ParamNo, Offsets: C.second)); |
| 866 | } |
| 867 | // FIXME: This is needed only to preserve calls in print() results. |
| 868 | A.Calls = SrcF.Allocas.find(x: KV.first)->second.Calls; |
| 869 | } |
| 870 | for (auto &KV : FI.Params) { |
| 871 | auto &P = KV.second; |
| 872 | P.Calls = SrcF.Params.find(x: KV.first)->second.Calls; |
| 873 | } |
| 874 | SSI[F.first] = std::move(FI); |
| 875 | } |
| 876 | |
| 877 | return SSI; |
| 878 | } |
| 879 | |
| 880 | } // end anonymous namespace |
| 881 | |
| 882 | StackSafetyInfo::StackSafetyInfo() = default; |
| 883 | |
| 884 | StackSafetyInfo::StackSafetyInfo(Function *F, |
| 885 | std::function<ScalarEvolution &()> GetSE) |
| 886 | : F(F), GetSE(GetSE) {} |
| 887 | |
| 888 | StackSafetyInfo::StackSafetyInfo(StackSafetyInfo &&) = default; |
| 889 | |
| 890 | StackSafetyInfo &StackSafetyInfo::operator=(StackSafetyInfo &&) = default; |
| 891 | |
| 892 | StackSafetyInfo::~StackSafetyInfo() = default; |
| 893 | |
| 894 | const StackSafetyInfo::InfoTy &StackSafetyInfo::getInfo() const { |
| 895 | if (!Info) { |
| 896 | StackSafetyLocalAnalysis SSLA(*F, GetSE()); |
| 897 | Info.reset(p: new InfoTy{.Info: SSLA.run()}); |
| 898 | } |
| 899 | return *Info; |
| 900 | } |
| 901 | |
| 902 | void StackSafetyInfo::print(raw_ostream &O) const { |
| 903 | getInfo().Info.print(O, Name: F->getName(), F: dyn_cast<Function>(Val: F)); |
| 904 | O << "\n" ; |
| 905 | } |
| 906 | |
| 907 | const StackSafetyGlobalInfo::InfoTy &StackSafetyGlobalInfo::getInfo() const { |
| 908 | if (!Info) { |
| 909 | std::map<const GlobalValue *, FunctionInfo<GlobalValue>> Functions; |
| 910 | for (auto &F : M->functions()) { |
| 911 | if (!F.isDeclaration()) { |
| 912 | auto FI = GetSSI(F).getInfo().Info; |
| 913 | Functions.emplace(args: &F, args: std::move(FI)); |
| 914 | } |
| 915 | } |
| 916 | Info.reset(p: new InfoTy{ |
| 917 | .Info: createGlobalStackSafetyInfo(Functions: std::move(Functions), Index), .SafeAllocas: {}, .UnsafeAccesses: {}}); |
| 918 | |
| 919 | for (auto &FnKV : Info->Info) { |
| 920 | for (auto &KV : FnKV.second.Allocas) { |
| 921 | ++NumAllocaTotal; |
| 922 | const AllocaInst *AI = KV.first; |
| 923 | auto AIRange = getStaticAllocaSizeRange(AI: *AI); |
| 924 | if (AIRange.contains(CR: KV.second.Range)) { |
| 925 | Info->SafeAllocas.insert(Ptr: AI); |
| 926 | ++NumAllocaStackSafe; |
| 927 | } |
| 928 | Info->UnsafeAccesses.insert(first: KV.second.UnsafeAccesses.begin(), |
| 929 | last: KV.second.UnsafeAccesses.end()); |
| 930 | } |
| 931 | } |
| 932 | |
| 933 | if (StackSafetyPrint) |
| 934 | print(O&: errs()); |
| 935 | } |
| 936 | return *Info; |
| 937 | } |
| 938 | |
| 939 | std::vector<FunctionSummary::ParamAccess> |
| 940 | StackSafetyInfo::getParamAccesses(ModuleSummaryIndex &Index) const { |
| 941 | // Implementation transforms internal representation of parameter information |
| 942 | // into FunctionSummary format. |
| 943 | std::vector<FunctionSummary::ParamAccess> ParamAccesses; |
| 944 | for (const auto &KV : getInfo().Info.Params) { |
| 945 | auto &PS = KV.second; |
| 946 | // Parameter accessed by any or unknown offset, represented as FullSet by |
| 947 | // StackSafety, is handled as the parameter for which we have no |
| 948 | // StackSafety info at all. So drop it to reduce summary size. |
| 949 | if (PS.Range.isFullSet()) |
| 950 | continue; |
| 951 | |
| 952 | ParamAccesses.emplace_back(args: KV.first, args: PS.Range); |
| 953 | FunctionSummary::ParamAccess &Param = ParamAccesses.back(); |
| 954 | |
| 955 | Param.Calls.reserve(n: PS.Calls.size()); |
| 956 | for (const auto &C : PS.Calls) { |
| 957 | // Parameter forwarded into another function by any or unknown offset |
| 958 | // will make ParamAccess::Range as FullSet anyway. So we can drop the |
| 959 | // entire parameter like we did above. |
| 960 | // TODO(vitalybuka): Return already filtered parameters from getInfo(). |
| 961 | if (C.second.isFullSet()) { |
| 962 | ParamAccesses.pop_back(); |
| 963 | break; |
| 964 | } |
| 965 | Param.Calls.emplace_back(args: C.first.ParamNo, |
| 966 | args: Index.getOrInsertValueInfo(GV: C.first.Callee), |
| 967 | args: C.second); |
| 968 | } |
| 969 | } |
| 970 | for (FunctionSummary::ParamAccess &Param : ParamAccesses) { |
| 971 | sort(C&: Param.Calls, Comp: [](const FunctionSummary::ParamAccess::Call &L, |
| 972 | const FunctionSummary::ParamAccess::Call &R) { |
| 973 | return std::tie(args: L.ParamNo, args: L.Callee) < std::tie(args: R.ParamNo, args: R.Callee); |
| 974 | }); |
| 975 | } |
| 976 | return ParamAccesses; |
| 977 | } |
| 978 | |
| 979 | StackSafetyGlobalInfo::StackSafetyGlobalInfo() = default; |
| 980 | |
| 981 | StackSafetyGlobalInfo::StackSafetyGlobalInfo( |
| 982 | Module *M, std::function<const StackSafetyInfo &(Function &F)> GetSSI, |
| 983 | const ModuleSummaryIndex *Index) |
| 984 | : M(M), GetSSI(GetSSI), Index(Index) { |
| 985 | if (StackSafetyRun) |
| 986 | getInfo(); |
| 987 | } |
| 988 | |
| 989 | StackSafetyGlobalInfo::StackSafetyGlobalInfo(StackSafetyGlobalInfo &&) = |
| 990 | default; |
| 991 | |
| 992 | StackSafetyGlobalInfo & |
| 993 | StackSafetyGlobalInfo::operator=(StackSafetyGlobalInfo &&) = default; |
| 994 | |
| 995 | StackSafetyGlobalInfo::~StackSafetyGlobalInfo() = default; |
| 996 | |
| 997 | bool StackSafetyGlobalInfo::isSafe(const AllocaInst &AI) const { |
| 998 | const auto &Info = getInfo(); |
| 999 | return Info.SafeAllocas.count(Ptr: &AI); |
| 1000 | } |
| 1001 | |
| 1002 | bool StackSafetyGlobalInfo::stackAccessIsSafe(const Instruction &I) const { |
| 1003 | const auto &Info = getInfo(); |
| 1004 | return Info.UnsafeAccesses.find(x: &I) == Info.UnsafeAccesses.end(); |
| 1005 | } |
| 1006 | |
| 1007 | void StackSafetyGlobalInfo::print(raw_ostream &O) const { |
| 1008 | auto &SSI = getInfo().Info; |
| 1009 | if (SSI.empty()) |
| 1010 | return; |
| 1011 | const Module &M = *SSI.begin()->first->getParent(); |
| 1012 | for (const auto &F : M.functions()) { |
| 1013 | if (!F.isDeclaration()) { |
| 1014 | SSI.find(x: &F)->second.print(O, Name: F.getName(), F: &F); |
| 1015 | O << " safe accesses:" |
| 1016 | << "\n" ; |
| 1017 | for (const auto &I : instructions(F)) { |
| 1018 | const CallInst *Call = dyn_cast<CallInst>(Val: &I); |
| 1019 | if ((isa<StoreInst>(Val: I) || isa<LoadInst>(Val: I) || isa<MemIntrinsic>(Val: I) || |
| 1020 | isa<AtomicCmpXchgInst>(Val: I) || isa<AtomicRMWInst>(Val: I) || |
| 1021 | (Call && Call->hasByValArgument())) && |
| 1022 | stackAccessIsSafe(I)) { |
| 1023 | O << " " << I << "\n" ; |
| 1024 | } |
| 1025 | } |
| 1026 | O << "\n" ; |
| 1027 | } |
| 1028 | } |
| 1029 | } |
| 1030 | |
| 1031 | LLVM_DUMP_METHOD void StackSafetyGlobalInfo::dump() const { print(O&: dbgs()); } |
| 1032 | |
| 1033 | AnalysisKey StackSafetyAnalysis::Key; |
| 1034 | |
| 1035 | StackSafetyInfo StackSafetyAnalysis::run(Function &F, |
| 1036 | FunctionAnalysisManager &AM) { |
| 1037 | return StackSafetyInfo(&F, [&AM, &F]() -> ScalarEvolution & { |
| 1038 | return AM.getResult<ScalarEvolutionAnalysis>(IR&: F); |
| 1039 | }); |
| 1040 | } |
| 1041 | |
| 1042 | PreservedAnalyses StackSafetyPrinterPass::run(Function &F, |
| 1043 | FunctionAnalysisManager &AM) { |
| 1044 | OS << "'Stack Safety Local Analysis' for function '" << F.getName() << "'\n" ; |
| 1045 | AM.getResult<StackSafetyAnalysis>(IR&: F).print(O&: OS); |
| 1046 | return PreservedAnalyses::all(); |
| 1047 | } |
| 1048 | |
| 1049 | char StackSafetyInfoWrapperPass::ID = 0; |
| 1050 | |
| 1051 | StackSafetyInfoWrapperPass::StackSafetyInfoWrapperPass() : FunctionPass(ID) {} |
| 1052 | |
| 1053 | void StackSafetyInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
| 1054 | AU.addRequiredTransitive<ScalarEvolutionWrapperPass>(); |
| 1055 | AU.setPreservesAll(); |
| 1056 | } |
| 1057 | |
| 1058 | void StackSafetyInfoWrapperPass::print(raw_ostream &O, const Module *M) const { |
| 1059 | SSI.print(O); |
| 1060 | } |
| 1061 | |
| 1062 | bool StackSafetyInfoWrapperPass::runOnFunction(Function &F) { |
| 1063 | auto *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE(); |
| 1064 | SSI = {&F, [SE]() -> ScalarEvolution & { return *SE; }}; |
| 1065 | return false; |
| 1066 | } |
| 1067 | |
| 1068 | AnalysisKey StackSafetyGlobalAnalysis::Key; |
| 1069 | |
| 1070 | StackSafetyGlobalInfo |
| 1071 | StackSafetyGlobalAnalysis::run(Module &M, ModuleAnalysisManager &AM) { |
| 1072 | // FIXME: Lookup Module Summary. |
| 1073 | FunctionAnalysisManager &FAM = |
| 1074 | AM.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager(); |
| 1075 | return {&M, |
| 1076 | [&FAM](Function &F) -> const StackSafetyInfo & { |
| 1077 | return FAM.getResult<StackSafetyAnalysis>(IR&: F); |
| 1078 | }, |
| 1079 | nullptr}; |
| 1080 | } |
| 1081 | |
| 1082 | PreservedAnalyses StackSafetyGlobalPrinterPass::run(Module &M, |
| 1083 | ModuleAnalysisManager &AM) { |
| 1084 | OS << "'Stack Safety Analysis' for module '" << M.getName() << "'\n" ; |
| 1085 | AM.getResult<StackSafetyGlobalAnalysis>(IR&: M).print(O&: OS); |
| 1086 | return PreservedAnalyses::all(); |
| 1087 | } |
| 1088 | |
| 1089 | char StackSafetyGlobalInfoWrapperPass::ID = 0; |
| 1090 | |
| 1091 | StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass() |
| 1092 | : ModulePass(ID) {} |
| 1093 | |
| 1094 | StackSafetyGlobalInfoWrapperPass::~StackSafetyGlobalInfoWrapperPass() = default; |
| 1095 | |
| 1096 | void StackSafetyGlobalInfoWrapperPass::print(raw_ostream &O, |
| 1097 | const Module *M) const { |
| 1098 | SSGI.print(O); |
| 1099 | } |
| 1100 | |
| 1101 | void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage( |
| 1102 | AnalysisUsage &AU) const { |
| 1103 | AU.setPreservesAll(); |
| 1104 | AU.addRequired<StackSafetyInfoWrapperPass>(); |
| 1105 | } |
| 1106 | |
| 1107 | bool StackSafetyGlobalInfoWrapperPass::runOnModule(Module &M) { |
| 1108 | const ModuleSummaryIndex *ImportSummary = nullptr; |
| 1109 | if (auto *IndexWrapperPass = |
| 1110 | getAnalysisIfAvailable<ImmutableModuleSummaryIndexWrapperPass>()) |
| 1111 | ImportSummary = IndexWrapperPass->getIndex(); |
| 1112 | |
| 1113 | SSGI = {&M, |
| 1114 | [this](Function &F) -> const StackSafetyInfo & { |
| 1115 | return getAnalysis<StackSafetyInfoWrapperPass>(F).getResult(); |
| 1116 | }, |
| 1117 | ImportSummary}; |
| 1118 | return false; |
| 1119 | } |
| 1120 | |
| 1121 | bool llvm::needsParamAccessSummary(const Module &M) { |
| 1122 | if (StackSafetyRun) |
| 1123 | return true; |
| 1124 | for (const auto &F : M.functions()) |
| 1125 | if (F.hasFnAttribute(Kind: Attribute::SanitizeMemTag)) |
| 1126 | return true; |
| 1127 | return false; |
| 1128 | } |
| 1129 | |
| 1130 | void llvm::generateParamAccessSummary(ModuleSummaryIndex &Index) { |
| 1131 | if (!Index.hasParamAccess()) |
| 1132 | return; |
| 1133 | const ConstantRange FullSet(FunctionSummary::ParamAccess::RangeWidth, true); |
| 1134 | |
| 1135 | auto CountParamAccesses = [&](auto &Stat) { |
| 1136 | if (!AreStatisticsEnabled()) |
| 1137 | return; |
| 1138 | for (auto &GVS : Index) |
| 1139 | for (auto &GV : GVS.second.SummaryList) |
| 1140 | if (FunctionSummary *FS = dyn_cast<FunctionSummary>(Val: GV.get())) |
| 1141 | Stat += FS->paramAccesses().size(); |
| 1142 | }; |
| 1143 | |
| 1144 | CountParamAccesses(NumCombinedParamAccessesBefore); |
| 1145 | |
| 1146 | std::map<const FunctionSummary *, FunctionInfo<FunctionSummary>> Functions; |
| 1147 | |
| 1148 | // Convert the ModuleSummaryIndex to a FunctionMap |
| 1149 | for (auto &GVS : Index) { |
| 1150 | for (auto &GV : GVS.second.SummaryList) { |
| 1151 | FunctionSummary *FS = dyn_cast<FunctionSummary>(Val: GV.get()); |
| 1152 | if (!FS || FS->paramAccesses().empty()) |
| 1153 | continue; |
| 1154 | if (FS->isLive() && FS->isDSOLocal()) { |
| 1155 | FunctionInfo<FunctionSummary> FI; |
| 1156 | for (const auto &PS : FS->paramAccesses()) { |
| 1157 | auto &US = |
| 1158 | FI.Params |
| 1159 | .emplace(args: PS.ParamNo, args: FunctionSummary::ParamAccess::RangeWidth) |
| 1160 | .first->second; |
| 1161 | US.Range = PS.Use; |
| 1162 | for (const auto &Call : PS.Calls) { |
| 1163 | assert(!Call.Offsets.isFullSet()); |
| 1164 | FunctionSummary *S = |
| 1165 | findCalleeFunctionSummary(VI: Call.Callee, ModuleId: FS->modulePath()); |
| 1166 | ++NumCombinedCalleeLookupTotal; |
| 1167 | if (!S) { |
| 1168 | ++NumCombinedCalleeLookupFailed; |
| 1169 | US.Range = FullSet; |
| 1170 | US.Calls.clear(); |
| 1171 | break; |
| 1172 | } |
| 1173 | US.Calls.emplace(args: CallInfo<FunctionSummary>(S, Call.ParamNo), |
| 1174 | args: Call.Offsets); |
| 1175 | } |
| 1176 | } |
| 1177 | Functions.emplace(args&: FS, args: std::move(FI)); |
| 1178 | } |
| 1179 | // Reset data for all summaries. Alive and DSO local will be set back from |
| 1180 | // of data flow results below. Anything else will not be accessed |
| 1181 | // by ThinLTO backend, so we can save on bitcode size. |
| 1182 | FS->setParamAccesses({}); |
| 1183 | } |
| 1184 | } |
| 1185 | NumCombinedDataFlowNodes += Functions.size(); |
| 1186 | StackSafetyDataFlowAnalysis<FunctionSummary> SSDFA( |
| 1187 | FunctionSummary::ParamAccess::RangeWidth, std::move(Functions)); |
| 1188 | for (const auto &KV : SSDFA.run()) { |
| 1189 | std::vector<FunctionSummary::ParamAccess> NewParams; |
| 1190 | NewParams.reserve(n: KV.second.Params.size()); |
| 1191 | for (const auto &Param : KV.second.Params) { |
| 1192 | // It's not needed as FullSet is processed the same as a missing value. |
| 1193 | if (Param.second.Range.isFullSet()) |
| 1194 | continue; |
| 1195 | NewParams.emplace_back(); |
| 1196 | FunctionSummary::ParamAccess &New = NewParams.back(); |
| 1197 | New.ParamNo = Param.first; |
| 1198 | New.Use = Param.second.Range; // Only range is needed. |
| 1199 | } |
| 1200 | const_cast<FunctionSummary *>(KV.first)->setParamAccesses( |
| 1201 | std::move(NewParams)); |
| 1202 | } |
| 1203 | |
| 1204 | CountParamAccesses(NumCombinedParamAccessesAfter); |
| 1205 | } |
| 1206 | |
| 1207 | static const char LocalPassArg[] = "stack-safety-local" ; |
| 1208 | static const char LocalPassName[] = "Stack Safety Local Analysis" ; |
| 1209 | INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName, |
| 1210 | false, true) |
| 1211 | INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) |
| 1212 | INITIALIZE_PASS_END(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName, |
| 1213 | false, true) |
| 1214 | |
| 1215 | static const char GlobalPassName[] = "Stack Safety Analysis" ; |
| 1216 | INITIALIZE_PASS_BEGIN(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE, |
| 1217 | GlobalPassName, false, true) |
| 1218 | INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass) |
| 1219 | INITIALIZE_PASS_DEPENDENCY(ImmutableModuleSummaryIndexWrapperPass) |
| 1220 | INITIALIZE_PASS_END(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE, |
| 1221 | GlobalPassName, false, true) |
| 1222 | |