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