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