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 llvm_unreachable("missing RemarkKind case"); 124 } 125 126 static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile, 127 bool Atomic, 128 OptimizationRemarkMissed &R) { 129 if (Inline && *Inline) 130 R << " Inlined: " << NV("StoreInlined", true) << "."; 131 if (Volatile) 132 R << " Volatile: " << NV("StoreVolatile", true) << "."; 133 if (Atomic) 134 R << " Atomic: " << NV("StoreAtomic", true) << "."; 135 // Emit the false cases under ExtraArgs. This won't show them in the remark 136 // message but will end up in the serialized remarks. 137 if ((Inline && !*Inline) || !Volatile || !Atomic) 138 R << setExtraArgs(); 139 if (Inline && !*Inline) 140 R << " Inlined: " << NV("StoreInlined", false) << "."; 141 if (!Volatile) 142 R << " Volatile: " << NV("StoreVolatile", false) << "."; 143 if (!Atomic) 144 R << " Atomic: " << NV("StoreAtomic", false) << "."; 145 } 146 147 static Optional<uint64_t> getSizeInBytes(Optional<uint64_t> SizeInBits) { 148 if (!SizeInBits || *SizeInBits % 8 != 0) 149 return None; 150 return *SizeInBits / 8; 151 } 152 153 void MemoryOpRemark::visitStore(const StoreInst &SI) { 154 bool Volatile = SI.isVolatile(); 155 bool Atomic = SI.isAtomic(); 156 int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType()); 157 158 OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_Store), &SI); 159 R << explainSource("Store") << "\nStore size: " << NV("StoreSize", Size) 160 << " bytes."; 161 visitPtr(SI.getOperand(1), /*IsRead=*/false, R); 162 inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile, Atomic, R); 163 ORE.emit(R); 164 } 165 166 void MemoryOpRemark::visitUnknown(const Instruction &I) { 167 OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_Unknown), &I); 168 R << explainSource("Initialization"); 169 ORE.emit(R); 170 } 171 172 void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst &II) { 173 SmallString<32> CallTo; 174 bool Atomic = false; 175 bool Inline = false; 176 switch (II.getIntrinsicID()) { 177 case Intrinsic::memcpy_inline: 178 CallTo = "memcpy"; 179 Inline = true; 180 break; 181 case Intrinsic::memcpy: 182 CallTo = "memcpy"; 183 break; 184 case Intrinsic::memmove: 185 CallTo = "memmove"; 186 break; 187 case Intrinsic::memset: 188 CallTo = "memset"; 189 break; 190 case Intrinsic::memcpy_element_unordered_atomic: 191 CallTo = "memcpy"; 192 Atomic = true; 193 break; 194 case Intrinsic::memmove_element_unordered_atomic: 195 CallTo = "memmove"; 196 Atomic = true; 197 break; 198 case Intrinsic::memset_element_unordered_atomic: 199 CallTo = "memset"; 200 Atomic = true; 201 break; 202 default: 203 return visitUnknown(II); 204 } 205 206 OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_IntrinsicCall), 207 &II); 208 visitCallee(StringRef(CallTo), /*KnownLibCall=*/true, R); 209 visitSizeOperand(II.getOperand(2), R); 210 211 auto *CIVolatile = dyn_cast<ConstantInt>(II.getOperand(3)); 212 // No such thing as a memory intrinsic that is both atomic and volatile. 213 bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue(); 214 switch (II.getIntrinsicID()) { 215 case Intrinsic::memcpy_inline: 216 case Intrinsic::memcpy: 217 case Intrinsic::memmove: 218 case Intrinsic::memcpy_element_unordered_atomic: 219 visitPtr(II.getOperand(1), /*IsRead=*/true, R); 220 visitPtr(II.getOperand(0), /*IsRead=*/false, R); 221 break; 222 case Intrinsic::memset: 223 case Intrinsic::memset_element_unordered_atomic: 224 visitPtr(II.getOperand(0), /*IsRead=*/false, R); 225 break; 226 } 227 inlineVolatileOrAtomicWithExtraArgs(&Inline, Volatile, Atomic, R); 228 ORE.emit(R); 229 } 230 231 void MemoryOpRemark::visitCall(const CallInst &CI) { 232 Function *F = CI.getCalledFunction(); 233 if (!F) 234 return visitUnknown(CI); 235 236 LibFunc LF; 237 bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF); 238 OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_Call), &CI); 239 visitCallee(F, KnownLibCall, R); 240 visitKnownLibCall(CI, LF, R); 241 ORE.emit(R); 242 } 243 244 template <typename FTy> 245 void MemoryOpRemark::visitCallee(FTy F, bool KnownLibCall, 246 OptimizationRemarkMissed &R) { 247 R << "Call to "; 248 if (!KnownLibCall) 249 R << NV("UnknownLibCall", "unknown") << " function "; 250 R << NV("Callee", F) << explainSource(""); 251 } 252 253 void MemoryOpRemark::visitKnownLibCall(const CallInst &CI, LibFunc LF, 254 OptimizationRemarkMissed &R) { 255 switch (LF) { 256 default: 257 return; 258 case LibFunc_memset_chk: 259 case LibFunc_memset: 260 visitSizeOperand(CI.getOperand(2), R); 261 visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 262 break; 263 case LibFunc_bzero: 264 visitSizeOperand(CI.getOperand(1), R); 265 visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 266 break; 267 case LibFunc_memcpy_chk: 268 case LibFunc_mempcpy_chk: 269 case LibFunc_memmove_chk: 270 case LibFunc_memcpy: 271 case LibFunc_mempcpy: 272 case LibFunc_memmove: 273 case LibFunc_bcopy: 274 visitSizeOperand(CI.getOperand(2), R); 275 visitPtr(CI.getOperand(1), /*IsRead=*/true, R); 276 visitPtr(CI.getOperand(0), /*IsRead=*/false, R); 277 break; 278 } 279 } 280 281 void MemoryOpRemark::visitSizeOperand(Value *V, OptimizationRemarkMissed &R) { 282 if (auto *Len = dyn_cast<ConstantInt>(V)) { 283 uint64_t Size = Len->getZExtValue(); 284 R << " Memory operation size: " << NV("StoreSize", Size) << " bytes."; 285 } 286 } 287 288 static Optional<StringRef> nameOrNone(const Value *V) { 289 if (V->hasName()) 290 return V->getName(); 291 return None; 292 } 293 294 void MemoryOpRemark::visitVariable(const Value *V, 295 SmallVectorImpl<VariableInfo> &Result) { 296 if (auto *GV = dyn_cast<GlobalVariable>(V)) { 297 auto *Ty = cast<PointerType>(GV->getType())->getElementType(); 298 uint64_t Size = DL.getTypeSizeInBits(Ty).getFixedSize(); 299 VariableInfo Var{nameOrNone(GV), Size}; 300 if (!Var.isEmpty()) 301 Result.push_back(std::move(Var)); 302 return; 303 } 304 305 // If we find some information in the debug info, take that. 306 bool FoundDI = false; 307 // Try to get an llvm.dbg.declare, which has a DILocalVariable giving us the 308 // real debug info name and size of the variable. 309 for (const DbgVariableIntrinsic *DVI : 310 FindDbgAddrUses(const_cast<Value *>(V))) { 311 if (DILocalVariable *DILV = DVI->getVariable()) { 312 Optional<uint64_t> DISize = getSizeInBytes(DILV->getSizeInBits()); 313 VariableInfo Var{DILV->getName(), DISize}; 314 if (!Var.isEmpty()) { 315 Result.push_back(std::move(Var)); 316 FoundDI = true; 317 } 318 } 319 } 320 if (FoundDI) { 321 assert(!Result.empty()); 322 return; 323 } 324 325 const auto *AI = dyn_cast<AllocaInst>(V); 326 if (!AI) 327 return; 328 329 // If not, get it from the alloca. 330 Optional<TypeSize> TySize = AI->getAllocationSizeInBits(DL); 331 Optional<uint64_t> Size = 332 TySize ? getSizeInBytes(TySize->getFixedSize()) : None; 333 VariableInfo Var{nameOrNone(AI), Size}; 334 if (!Var.isEmpty()) 335 Result.push_back(std::move(Var)); 336 } 337 338 void MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, OptimizationRemarkMissed &R) { 339 // Find if Ptr is a known variable we can give more information on. 340 SmallVector<Value *, 2> Objects; 341 getUnderlyingObjectsForCodeGen(Ptr, Objects); 342 SmallVector<VariableInfo, 2> VIs; 343 for (const Value *V : Objects) 344 visitVariable(V, VIs); 345 346 if (VIs.empty()) { 347 bool CanBeNull; 348 bool CanBeFreed; 349 uint64_t Size = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed); 350 if (!Size) 351 return; 352 VIs.push_back({None, Size}); 353 } 354 355 R << (IsRead ? "\n Read Variables: " : "\n Written Variables: "); 356 for (unsigned i = 0; i < VIs.size(); ++i) { 357 const VariableInfo &VI = VIs[i]; 358 assert(!VI.isEmpty() && "No extra content to display."); 359 if (i != 0) 360 R << ", "; 361 if (VI.Name) 362 R << NV(IsRead ? "RVarName" : "WVarName", *VI.Name); 363 else 364 R << NV(IsRead ? "RVarName" : "WVarName", "<unknown>"); 365 if (VI.Size) 366 R << " (" << NV(IsRead ? "RVarSize" : "WVarSize", *VI.Size) << " bytes)"; 367 } 368 R << "."; 369 } 370 371 bool AutoInitRemark::canHandle(const Instruction *I) { 372 if (!I->hasMetadata(LLVMContext::MD_annotation)) 373 return false; 374 return any_of(I->getMetadata(LLVMContext::MD_annotation)->operands(), 375 [](const MDOperand &Op) { 376 return cast<MDString>(Op.get())->getString() == "auto-init"; 377 }); 378 } 379 380 std::string AutoInitRemark::explainSource(StringRef Type) { 381 return (Type + " inserted by -ftrivial-auto-var-init.").str(); 382 } 383 384 StringRef AutoInitRemark::remarkName(RemarkKind RK) { 385 switch (RK) { 386 case RK_Store: 387 return "AutoInitStore"; 388 case RK_Unknown: 389 return "AutoInitUnknownInstruction"; 390 case RK_IntrinsicCall: 391 return "AutoInitIntrinsicCall"; 392 case RK_Call: 393 return "AutoInitCall"; 394 } 395 llvm_unreachable("missing RemarkKind case"); 396 } 397