1fe6060f1SDimitry Andric //===-- MemoryOpRemark.cpp - Auto-init remark analysis---------------------===// 2fe6060f1SDimitry Andric // 3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fe6060f1SDimitry Andric // 7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8fe6060f1SDimitry Andric // 9fe6060f1SDimitry Andric // Implementation of the analysis for the "auto-init" remark. 10fe6060f1SDimitry Andric // 11fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 12fe6060f1SDimitry Andric 13fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/MemoryOpRemark.h" 1406c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h" 15fe6060f1SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 16fe6060f1SDimitry Andric #include "llvm/Analysis/ValueTracking.h" 17fe6060f1SDimitry Andric #include "llvm/IR/DebugInfo.h" 18fe6060f1SDimitry Andric #include "llvm/IR/Instructions.h" 19fe6060f1SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 20bdd1243dSDimitry Andric #include <optional> 21fe6060f1SDimitry Andric 22fe6060f1SDimitry Andric using namespace llvm; 23fe6060f1SDimitry Andric using namespace llvm::ore; 24fe6060f1SDimitry Andric 25fe6060f1SDimitry Andric MemoryOpRemark::~MemoryOpRemark() = default; 26fe6060f1SDimitry Andric 27fe6060f1SDimitry Andric bool MemoryOpRemark::canHandle(const Instruction *I, const TargetLibraryInfo &TLI) { 28fe6060f1SDimitry Andric if (isa<StoreInst>(I)) 29fe6060f1SDimitry Andric return true; 30fe6060f1SDimitry Andric 31fe6060f1SDimitry Andric if (auto *II = dyn_cast<IntrinsicInst>(I)) { 32fe6060f1SDimitry Andric switch (II->getIntrinsicID()) { 33fe6060f1SDimitry Andric case Intrinsic::memcpy_inline: 34fe6060f1SDimitry Andric case Intrinsic::memcpy: 35fe6060f1SDimitry Andric case Intrinsic::memmove: 36fe6060f1SDimitry Andric case Intrinsic::memset: 37fe6060f1SDimitry Andric case Intrinsic::memcpy_element_unordered_atomic: 38fe6060f1SDimitry Andric case Intrinsic::memmove_element_unordered_atomic: 39fe6060f1SDimitry Andric case Intrinsic::memset_element_unordered_atomic: 40fe6060f1SDimitry Andric return true; 41fe6060f1SDimitry Andric default: 42fe6060f1SDimitry Andric return false; 43fe6060f1SDimitry Andric } 44fe6060f1SDimitry Andric } 45fe6060f1SDimitry Andric 46fe6060f1SDimitry Andric if (auto *CI = dyn_cast<CallInst>(I)) { 47fe6060f1SDimitry Andric auto *CF = CI->getCalledFunction(); 48fe6060f1SDimitry Andric if (!CF) 49fe6060f1SDimitry Andric return false; 50fe6060f1SDimitry Andric 51fe6060f1SDimitry Andric if (!CF->hasName()) 52fe6060f1SDimitry Andric return false; 53fe6060f1SDimitry Andric 54fe6060f1SDimitry Andric LibFunc LF; 55fe6060f1SDimitry Andric bool KnownLibCall = TLI.getLibFunc(*CF, LF) && TLI.has(LF); 56fe6060f1SDimitry Andric if (!KnownLibCall) 57fe6060f1SDimitry Andric return false; 58fe6060f1SDimitry Andric 59fe6060f1SDimitry Andric switch (LF) { 60fe6060f1SDimitry Andric case LibFunc_memcpy_chk: 61fe6060f1SDimitry Andric case LibFunc_mempcpy_chk: 62fe6060f1SDimitry Andric case LibFunc_memset_chk: 63fe6060f1SDimitry Andric case LibFunc_memmove_chk: 64fe6060f1SDimitry Andric case LibFunc_memcpy: 65fe6060f1SDimitry Andric case LibFunc_mempcpy: 66fe6060f1SDimitry Andric case LibFunc_memset: 67fe6060f1SDimitry Andric case LibFunc_memmove: 68fe6060f1SDimitry Andric case LibFunc_bzero: 69fe6060f1SDimitry Andric case LibFunc_bcopy: 70fe6060f1SDimitry Andric return true; 71fe6060f1SDimitry Andric default: 72fe6060f1SDimitry Andric return false; 73fe6060f1SDimitry Andric } 74fe6060f1SDimitry Andric } 75fe6060f1SDimitry Andric 76fe6060f1SDimitry Andric return false; 77fe6060f1SDimitry Andric } 78fe6060f1SDimitry Andric 79fe6060f1SDimitry Andric void MemoryOpRemark::visit(const Instruction *I) { 80fe6060f1SDimitry Andric // For some of them, we can provide more information: 81fe6060f1SDimitry Andric 82fe6060f1SDimitry Andric // For stores: 83fe6060f1SDimitry Andric // * size 84fe6060f1SDimitry Andric // * volatile / atomic 85fe6060f1SDimitry Andric if (auto *SI = dyn_cast<StoreInst>(I)) { 86fe6060f1SDimitry Andric visitStore(*SI); 87fe6060f1SDimitry Andric return; 88fe6060f1SDimitry Andric } 89fe6060f1SDimitry Andric 90fe6060f1SDimitry Andric // For intrinsics: 91fe6060f1SDimitry Andric // * user-friendly name 92fe6060f1SDimitry Andric // * size 93fe6060f1SDimitry Andric if (auto *II = dyn_cast<IntrinsicInst>(I)) { 94fe6060f1SDimitry Andric visitIntrinsicCall(*II); 95fe6060f1SDimitry Andric return; 96fe6060f1SDimitry Andric } 97fe6060f1SDimitry Andric 98fe6060f1SDimitry Andric // For calls: 99fe6060f1SDimitry Andric // * known/unknown function (e.g. the compiler knows bzero, but it doesn't 100fe6060f1SDimitry Andric // know my_bzero) 101fe6060f1SDimitry Andric // * memory operation size 102fe6060f1SDimitry Andric if (auto *CI = dyn_cast<CallInst>(I)) { 103fe6060f1SDimitry Andric visitCall(*CI); 104fe6060f1SDimitry Andric return; 105fe6060f1SDimitry Andric } 106fe6060f1SDimitry Andric 107fe6060f1SDimitry Andric visitUnknown(*I); 108fe6060f1SDimitry Andric } 109fe6060f1SDimitry Andric 110fe6060f1SDimitry Andric std::string MemoryOpRemark::explainSource(StringRef Type) const { 111fe6060f1SDimitry Andric return (Type + ".").str(); 112fe6060f1SDimitry Andric } 113fe6060f1SDimitry Andric 114fe6060f1SDimitry Andric StringRef MemoryOpRemark::remarkName(RemarkKind RK) const { 115fe6060f1SDimitry Andric switch (RK) { 116fe6060f1SDimitry Andric case RK_Store: 117fe6060f1SDimitry Andric return "MemoryOpStore"; 118fe6060f1SDimitry Andric case RK_Unknown: 119fe6060f1SDimitry Andric return "MemoryOpUnknown"; 120fe6060f1SDimitry Andric case RK_IntrinsicCall: 121fe6060f1SDimitry Andric return "MemoryOpIntrinsicCall"; 122fe6060f1SDimitry Andric case RK_Call: 123fe6060f1SDimitry Andric return "MemoryOpCall"; 124fe6060f1SDimitry Andric } 125fe6060f1SDimitry Andric llvm_unreachable("missing RemarkKind case"); 126fe6060f1SDimitry Andric } 127fe6060f1SDimitry Andric 128fe6060f1SDimitry Andric static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile, 129fe6060f1SDimitry Andric bool Atomic, 130fe6060f1SDimitry Andric DiagnosticInfoIROptimization &R) { 131fe6060f1SDimitry Andric if (Inline && *Inline) 132fe6060f1SDimitry Andric R << " Inlined: " << NV("StoreInlined", true) << "."; 133fe6060f1SDimitry Andric if (Volatile) 134fe6060f1SDimitry Andric R << " Volatile: " << NV("StoreVolatile", true) << "."; 135fe6060f1SDimitry Andric if (Atomic) 136fe6060f1SDimitry Andric R << " Atomic: " << NV("StoreAtomic", true) << "."; 137fe6060f1SDimitry Andric // Emit the false cases under ExtraArgs. This won't show them in the remark 138fe6060f1SDimitry Andric // message but will end up in the serialized remarks. 139fe6060f1SDimitry Andric if ((Inline && !*Inline) || !Volatile || !Atomic) 140fe6060f1SDimitry Andric R << setExtraArgs(); 141fe6060f1SDimitry Andric if (Inline && !*Inline) 142fe6060f1SDimitry Andric R << " Inlined: " << NV("StoreInlined", false) << "."; 143fe6060f1SDimitry Andric if (!Volatile) 144fe6060f1SDimitry Andric R << " Volatile: " << NV("StoreVolatile", false) << "."; 145fe6060f1SDimitry Andric if (!Atomic) 146fe6060f1SDimitry Andric R << " Atomic: " << NV("StoreAtomic", false) << "."; 147fe6060f1SDimitry Andric } 148fe6060f1SDimitry Andric 149bdd1243dSDimitry Andric static std::optional<uint64_t> 150bdd1243dSDimitry Andric getSizeInBytes(std::optional<uint64_t> SizeInBits) { 151fe6060f1SDimitry Andric if (!SizeInBits || *SizeInBits % 8 != 0) 152bdd1243dSDimitry Andric return std::nullopt; 153fe6060f1SDimitry Andric return *SizeInBits / 8; 154fe6060f1SDimitry Andric } 155fe6060f1SDimitry Andric 156fe6060f1SDimitry Andric template<typename ...Ts> 157fe6060f1SDimitry Andric std::unique_ptr<DiagnosticInfoIROptimization> 158fe6060f1SDimitry Andric MemoryOpRemark::makeRemark(Ts... Args) { 159fe6060f1SDimitry Andric switch (diagnosticKind()) { 160fe6060f1SDimitry Andric case DK_OptimizationRemarkAnalysis: 161fe6060f1SDimitry Andric return std::make_unique<OptimizationRemarkAnalysis>(Args...); 162fe6060f1SDimitry Andric case DK_OptimizationRemarkMissed: 163fe6060f1SDimitry Andric return std::make_unique<OptimizationRemarkMissed>(Args...); 164fe6060f1SDimitry Andric default: 165fe6060f1SDimitry Andric llvm_unreachable("unexpected DiagnosticKind"); 166fe6060f1SDimitry Andric } 167fe6060f1SDimitry Andric } 168fe6060f1SDimitry Andric 169fe6060f1SDimitry Andric void MemoryOpRemark::visitStore(const StoreInst &SI) { 170fe6060f1SDimitry Andric bool Volatile = SI.isVolatile(); 171fe6060f1SDimitry Andric bool Atomic = SI.isAtomic(); 172fe6060f1SDimitry Andric int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType()); 173fe6060f1SDimitry Andric 174fe6060f1SDimitry Andric auto R = makeRemark(RemarkPass.data(), remarkName(RK_Store), &SI); 175fe6060f1SDimitry Andric *R << explainSource("Store") << "\nStore size: " << NV("StoreSize", Size) 176fe6060f1SDimitry Andric << " bytes."; 177fe6060f1SDimitry Andric visitPtr(SI.getOperand(1), /*IsRead=*/false, *R); 178fe6060f1SDimitry Andric inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile, Atomic, *R); 179fe6060f1SDimitry Andric ORE.emit(*R); 180fe6060f1SDimitry Andric } 181fe6060f1SDimitry Andric 182fe6060f1SDimitry Andric void MemoryOpRemark::visitUnknown(const Instruction &I) { 183fe6060f1SDimitry Andric auto R = makeRemark(RemarkPass.data(), remarkName(RK_Unknown), &I); 184fe6060f1SDimitry Andric *R << explainSource("Initialization"); 185fe6060f1SDimitry Andric ORE.emit(*R); 186fe6060f1SDimitry Andric } 187fe6060f1SDimitry Andric 188fe6060f1SDimitry Andric void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst &II) { 189fe6060f1SDimitry Andric SmallString<32> CallTo; 190fe6060f1SDimitry Andric bool Atomic = false; 191fe6060f1SDimitry Andric bool Inline = false; 192fe6060f1SDimitry Andric switch (II.getIntrinsicID()) { 193fe6060f1SDimitry Andric case Intrinsic::memcpy_inline: 194fe6060f1SDimitry Andric CallTo = "memcpy"; 195fe6060f1SDimitry Andric Inline = true; 196fe6060f1SDimitry Andric break; 197fe6060f1SDimitry Andric case Intrinsic::memcpy: 198fe6060f1SDimitry Andric CallTo = "memcpy"; 199fe6060f1SDimitry Andric break; 200fe6060f1SDimitry Andric case Intrinsic::memmove: 201fe6060f1SDimitry Andric CallTo = "memmove"; 202fe6060f1SDimitry Andric break; 203fe6060f1SDimitry Andric case Intrinsic::memset: 204fe6060f1SDimitry Andric CallTo = "memset"; 205fe6060f1SDimitry Andric break; 206fe6060f1SDimitry Andric case Intrinsic::memcpy_element_unordered_atomic: 207fe6060f1SDimitry Andric CallTo = "memcpy"; 208fe6060f1SDimitry Andric Atomic = true; 209fe6060f1SDimitry Andric break; 210fe6060f1SDimitry Andric case Intrinsic::memmove_element_unordered_atomic: 211fe6060f1SDimitry Andric CallTo = "memmove"; 212fe6060f1SDimitry Andric Atomic = true; 213fe6060f1SDimitry Andric break; 214fe6060f1SDimitry Andric case Intrinsic::memset_element_unordered_atomic: 215fe6060f1SDimitry Andric CallTo = "memset"; 216fe6060f1SDimitry Andric Atomic = true; 217fe6060f1SDimitry Andric break; 218fe6060f1SDimitry Andric default: 219fe6060f1SDimitry Andric return visitUnknown(II); 220fe6060f1SDimitry Andric } 221fe6060f1SDimitry Andric 222fe6060f1SDimitry Andric auto R = makeRemark(RemarkPass.data(), remarkName(RK_IntrinsicCall), &II); 223fe6060f1SDimitry Andric visitCallee(CallTo.str(), /*KnownLibCall=*/true, *R); 224fe6060f1SDimitry Andric visitSizeOperand(II.getOperand(2), *R); 225fe6060f1SDimitry Andric 226fe6060f1SDimitry Andric auto *CIVolatile = dyn_cast<ConstantInt>(II.getOperand(3)); 227fe6060f1SDimitry Andric // No such thing as a memory intrinsic that is both atomic and volatile. 228fe6060f1SDimitry Andric bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue(); 229fe6060f1SDimitry Andric switch (II.getIntrinsicID()) { 230fe6060f1SDimitry Andric case Intrinsic::memcpy_inline: 231fe6060f1SDimitry Andric case Intrinsic::memcpy: 232fe6060f1SDimitry Andric case Intrinsic::memmove: 233fe6060f1SDimitry Andric case Intrinsic::memcpy_element_unordered_atomic: 234fe6060f1SDimitry Andric visitPtr(II.getOperand(1), /*IsRead=*/true, *R); 235fe6060f1SDimitry Andric visitPtr(II.getOperand(0), /*IsRead=*/false, *R); 236fe6060f1SDimitry Andric break; 237fe6060f1SDimitry Andric case Intrinsic::memset: 238fe6060f1SDimitry Andric case Intrinsic::memset_element_unordered_atomic: 239fe6060f1SDimitry Andric visitPtr(II.getOperand(0), /*IsRead=*/false, *R); 240fe6060f1SDimitry Andric break; 241fe6060f1SDimitry Andric } 242fe6060f1SDimitry Andric inlineVolatileOrAtomicWithExtraArgs(&Inline, Volatile, Atomic, *R); 243fe6060f1SDimitry Andric ORE.emit(*R); 244fe6060f1SDimitry Andric } 245fe6060f1SDimitry Andric 246fe6060f1SDimitry Andric void MemoryOpRemark::visitCall(const CallInst &CI) { 247fe6060f1SDimitry Andric Function *F = CI.getCalledFunction(); 248fe6060f1SDimitry Andric if (!F) 249fe6060f1SDimitry Andric return visitUnknown(CI); 250fe6060f1SDimitry Andric 251fe6060f1SDimitry Andric LibFunc LF; 252fe6060f1SDimitry Andric bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF); 253fe6060f1SDimitry Andric auto R = makeRemark(RemarkPass.data(), remarkName(RK_Call), &CI); 254fe6060f1SDimitry Andric visitCallee(F, KnownLibCall, *R); 255fe6060f1SDimitry Andric visitKnownLibCall(CI, LF, *R); 256fe6060f1SDimitry Andric ORE.emit(*R); 257fe6060f1SDimitry Andric } 258fe6060f1SDimitry Andric 259fe6060f1SDimitry Andric template <typename FTy> 260fe6060f1SDimitry Andric void MemoryOpRemark::visitCallee(FTy F, bool KnownLibCall, 261fe6060f1SDimitry Andric DiagnosticInfoIROptimization &R) { 262fe6060f1SDimitry Andric R << "Call to "; 263fe6060f1SDimitry Andric if (!KnownLibCall) 264fe6060f1SDimitry Andric R << NV("UnknownLibCall", "unknown") << " function "; 265fe6060f1SDimitry Andric R << NV("Callee", F) << explainSource(""); 266fe6060f1SDimitry Andric } 267fe6060f1SDimitry Andric 268fe6060f1SDimitry Andric void MemoryOpRemark::visitKnownLibCall(const CallInst &CI, LibFunc LF, 269fe6060f1SDimitry Andric DiagnosticInfoIROptimization &R) { 270fe6060f1SDimitry Andric switch (LF) { 271fe6060f1SDimitry Andric default: 272fe6060f1SDimitry Andric return; 273fe6060f1SDimitry Andric case LibFunc_memset_chk: 274fe6060f1SDimitry Andric case LibFunc_memset: 275fe6060f1SDimitry Andric visitSizeOperand(CI.getOperand(2), R); 276fe6060f1SDimitry Andric visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 277fe6060f1SDimitry Andric break; 278fe6060f1SDimitry Andric case LibFunc_bzero: 279fe6060f1SDimitry Andric visitSizeOperand(CI.getOperand(1), R); 280fe6060f1SDimitry Andric visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 281fe6060f1SDimitry Andric break; 282fe6060f1SDimitry Andric case LibFunc_memcpy_chk: 283fe6060f1SDimitry Andric case LibFunc_mempcpy_chk: 284fe6060f1SDimitry Andric case LibFunc_memmove_chk: 285fe6060f1SDimitry Andric case LibFunc_memcpy: 286fe6060f1SDimitry Andric case LibFunc_mempcpy: 287fe6060f1SDimitry Andric case LibFunc_memmove: 288fe6060f1SDimitry Andric case LibFunc_bcopy: 289fe6060f1SDimitry Andric visitSizeOperand(CI.getOperand(2), R); 290fe6060f1SDimitry Andric visitPtr(CI.getOperand(1), /*IsRead=*/true, R); 291fe6060f1SDimitry Andric visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 292fe6060f1SDimitry Andric break; 293fe6060f1SDimitry Andric } 294fe6060f1SDimitry Andric } 295fe6060f1SDimitry Andric 296fe6060f1SDimitry Andric void MemoryOpRemark::visitSizeOperand(Value *V, DiagnosticInfoIROptimization &R) { 297fe6060f1SDimitry Andric if (auto *Len = dyn_cast<ConstantInt>(V)) { 298fe6060f1SDimitry Andric uint64_t Size = Len->getZExtValue(); 299fe6060f1SDimitry Andric R << " Memory operation size: " << NV("StoreSize", Size) << " bytes."; 300fe6060f1SDimitry Andric } 301fe6060f1SDimitry Andric } 302fe6060f1SDimitry Andric 303bdd1243dSDimitry Andric static std::optional<StringRef> nameOrNone(const Value *V) { 304fe6060f1SDimitry Andric if (V->hasName()) 305fe6060f1SDimitry Andric return V->getName(); 306bdd1243dSDimitry Andric return std::nullopt; 307fe6060f1SDimitry Andric } 308fe6060f1SDimitry Andric 309fe6060f1SDimitry Andric void MemoryOpRemark::visitVariable(const Value *V, 310fe6060f1SDimitry Andric SmallVectorImpl<VariableInfo> &Result) { 311fe6060f1SDimitry Andric if (auto *GV = dyn_cast<GlobalVariable>(V)) { 312fe6060f1SDimitry Andric auto *Ty = GV->getValueType(); 313bdd1243dSDimitry Andric uint64_t Size = DL.getTypeSizeInBits(Ty).getFixedValue(); 314fe6060f1SDimitry Andric VariableInfo Var{nameOrNone(GV), Size}; 315fe6060f1SDimitry Andric if (!Var.isEmpty()) 316fe6060f1SDimitry Andric Result.push_back(std::move(Var)); 317fe6060f1SDimitry Andric return; 318fe6060f1SDimitry Andric } 319fe6060f1SDimitry Andric 320fe6060f1SDimitry Andric // If we find some information in the debug info, take that. 321fe6060f1SDimitry Andric bool FoundDI = false; 322fe6060f1SDimitry Andric // Try to get an llvm.dbg.declare, which has a DILocalVariable giving us the 323fe6060f1SDimitry Andric // real debug info name and size of the variable. 3245f757f3fSDimitry Andric auto FindDI = [&](const auto *DVI) { 325fe6060f1SDimitry Andric if (DILocalVariable *DILV = DVI->getVariable()) { 326bdd1243dSDimitry Andric std::optional<uint64_t> DISize = getSizeInBytes(DILV->getSizeInBits()); 327fe6060f1SDimitry Andric VariableInfo Var{DILV->getName(), DISize}; 328fe6060f1SDimitry Andric if (!Var.isEmpty()) { 329fe6060f1SDimitry Andric Result.push_back(std::move(Var)); 330fe6060f1SDimitry Andric FoundDI = true; 331fe6060f1SDimitry Andric } 332fe6060f1SDimitry Andric } 3335f757f3fSDimitry Andric }; 3347a6dacacSDimitry Andric for_each(findDbgDeclares(const_cast<Value *>(V)), FindDI); 335*0fca6ea1SDimitry Andric for_each(findDVRDeclares(const_cast<Value *>(V)), FindDI); 3365f757f3fSDimitry Andric 337fe6060f1SDimitry Andric if (FoundDI) { 338fe6060f1SDimitry Andric assert(!Result.empty()); 339fe6060f1SDimitry Andric return; 340fe6060f1SDimitry Andric } 341fe6060f1SDimitry Andric 342fe6060f1SDimitry Andric const auto *AI = dyn_cast<AllocaInst>(V); 343fe6060f1SDimitry Andric if (!AI) 344fe6060f1SDimitry Andric return; 345fe6060f1SDimitry Andric 346fe6060f1SDimitry Andric // If not, get it from the alloca. 347bdd1243dSDimitry Andric std::optional<TypeSize> TySize = AI->getAllocationSize(DL); 348bdd1243dSDimitry Andric std::optional<uint64_t> Size = 349bdd1243dSDimitry Andric TySize ? std::optional(TySize->getFixedValue()) : std::nullopt; 350fe6060f1SDimitry Andric VariableInfo Var{nameOrNone(AI), Size}; 351fe6060f1SDimitry Andric if (!Var.isEmpty()) 352fe6060f1SDimitry Andric Result.push_back(std::move(Var)); 353fe6060f1SDimitry Andric } 354fe6060f1SDimitry Andric 355fe6060f1SDimitry Andric void MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, DiagnosticInfoIROptimization &R) { 356fe6060f1SDimitry Andric // Find if Ptr is a known variable we can give more information on. 357fe6060f1SDimitry Andric SmallVector<Value *, 2> Objects; 358fe6060f1SDimitry Andric getUnderlyingObjectsForCodeGen(Ptr, Objects); 359fe6060f1SDimitry Andric SmallVector<VariableInfo, 2> VIs; 360fe6060f1SDimitry Andric for (const Value *V : Objects) 361fe6060f1SDimitry Andric visitVariable(V, VIs); 362fe6060f1SDimitry Andric 363fe6060f1SDimitry Andric if (VIs.empty()) { 364fe6060f1SDimitry Andric bool CanBeNull; 365fe6060f1SDimitry Andric bool CanBeFreed; 366fe6060f1SDimitry Andric uint64_t Size = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed); 367fe6060f1SDimitry Andric if (!Size) 368fe6060f1SDimitry Andric return; 369bdd1243dSDimitry Andric VIs.push_back({std::nullopt, Size}); 370fe6060f1SDimitry Andric } 371fe6060f1SDimitry Andric 372fe6060f1SDimitry Andric R << (IsRead ? "\n Read Variables: " : "\n Written Variables: "); 373fe6060f1SDimitry Andric for (unsigned i = 0; i < VIs.size(); ++i) { 374fe6060f1SDimitry Andric const VariableInfo &VI = VIs[i]; 375fe6060f1SDimitry Andric assert(!VI.isEmpty() && "No extra content to display."); 376fe6060f1SDimitry Andric if (i != 0) 377fe6060f1SDimitry Andric R << ", "; 378fe6060f1SDimitry Andric if (VI.Name) 379fe6060f1SDimitry Andric R << NV(IsRead ? "RVarName" : "WVarName", *VI.Name); 380fe6060f1SDimitry Andric else 381fe6060f1SDimitry Andric R << NV(IsRead ? "RVarName" : "WVarName", "<unknown>"); 382fe6060f1SDimitry Andric if (VI.Size) 383fe6060f1SDimitry Andric R << " (" << NV(IsRead ? "RVarSize" : "WVarSize", *VI.Size) << " bytes)"; 384fe6060f1SDimitry Andric } 385fe6060f1SDimitry Andric R << "."; 386fe6060f1SDimitry Andric } 387fe6060f1SDimitry Andric 388fe6060f1SDimitry Andric bool AutoInitRemark::canHandle(const Instruction *I) { 389fe6060f1SDimitry Andric if (!I->hasMetadata(LLVMContext::MD_annotation)) 390fe6060f1SDimitry Andric return false; 391fe6060f1SDimitry Andric return any_of(I->getMetadata(LLVMContext::MD_annotation)->operands(), 392fe6060f1SDimitry Andric [](const MDOperand &Op) { 39306c3fb27SDimitry Andric return isa<MDString>(Op.get()) && 39406c3fb27SDimitry Andric cast<MDString>(Op.get())->getString() == "auto-init"; 395fe6060f1SDimitry Andric }); 396fe6060f1SDimitry Andric } 397fe6060f1SDimitry Andric 398fe6060f1SDimitry Andric std::string AutoInitRemark::explainSource(StringRef Type) const { 399fe6060f1SDimitry Andric return (Type + " inserted by -ftrivial-auto-var-init.").str(); 400fe6060f1SDimitry Andric } 401fe6060f1SDimitry Andric 402fe6060f1SDimitry Andric StringRef AutoInitRemark::remarkName(RemarkKind RK) const { 403fe6060f1SDimitry Andric switch (RK) { 404fe6060f1SDimitry Andric case RK_Store: 405fe6060f1SDimitry Andric return "AutoInitStore"; 406fe6060f1SDimitry Andric case RK_Unknown: 407fe6060f1SDimitry Andric return "AutoInitUnknownInstruction"; 408fe6060f1SDimitry Andric case RK_IntrinsicCall: 409fe6060f1SDimitry Andric return "AutoInitIntrinsicCall"; 410fe6060f1SDimitry Andric case RK_Call: 411fe6060f1SDimitry Andric return "AutoInitCall"; 412fe6060f1SDimitry Andric } 413fe6060f1SDimitry Andric llvm_unreachable("missing RemarkKind case"); 414fe6060f1SDimitry Andric } 415