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 void MemoryOpRemark::visitVariable(const Value *V, 289 SmallVectorImpl<VariableInfo> &Result) { 290 // If we find some information in the debug info, take that. 291 bool FoundDI = false; 292 // Try to get an llvm.dbg.declare, which has a DILocalVariable giving us the 293 // real debug info name and size of the variable. 294 for (const DbgVariableIntrinsic *DVI : 295 FindDbgAddrUses(const_cast<Value *>(V))) { 296 if (DILocalVariable *DILV = DVI->getVariable()) { 297 Optional<uint64_t> DISize = getSizeInBytes(DILV->getSizeInBits()); 298 VariableInfo Var{DILV->getName(), DISize}; 299 if (!Var.isEmpty()) { 300 Result.push_back(std::move(Var)); 301 FoundDI = true; 302 } 303 } 304 } 305 if (FoundDI) { 306 assert(!Result.empty()); 307 return; 308 } 309 310 const auto *AI = dyn_cast<AllocaInst>(V); 311 if (!AI) 312 return; 313 314 // If not, get it from the alloca. 315 Optional<StringRef> Name = AI->hasName() ? Optional<StringRef>(AI->getName()) 316 : Optional<StringRef>(None); 317 Optional<TypeSize> TySize = AI->getAllocationSizeInBits(DL); 318 Optional<uint64_t> Size = 319 TySize ? getSizeInBytes(TySize->getFixedSize()) : None; 320 VariableInfo Var{Name, Size}; 321 if (!Var.isEmpty()) 322 Result.push_back(std::move(Var)); 323 } 324 325 void MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, OptimizationRemarkMissed &R) { 326 // Find if Ptr is a known variable we can give more information on. 327 SmallVector<Value *, 2> Objects; 328 getUnderlyingObjectsForCodeGen(Ptr, Objects); 329 SmallVector<VariableInfo, 2> VIs; 330 for (const Value *V : Objects) 331 visitVariable(V, VIs); 332 333 if (VIs.empty()) { 334 bool CanBeNull; 335 bool CanBeFreed; 336 uint64_t Size = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed); 337 if (!Size) 338 return; 339 VIs.push_back({None, Size}); 340 } 341 342 R << (IsRead ? "\n Read Variables: " : "\n Written Variables: "); 343 for (unsigned i = 0; i < VIs.size(); ++i) { 344 const VariableInfo &VI = VIs[i]; 345 assert(!VI.isEmpty() && "No extra content to display."); 346 if (i != 0) 347 R << ", "; 348 if (VI.Name) 349 R << NV(IsRead ? "RVarName" : "WVarName", *VI.Name); 350 else 351 R << NV(IsRead ? "RVarName" : "WVarName", "<unknown>"); 352 if (VI.Size) 353 R << " (" << NV(IsRead ? "RVarSize" : "WVarSize", *VI.Size) << " bytes)"; 354 } 355 R << "."; 356 } 357 358 bool AutoInitRemark::canHandle(const Instruction *I) { 359 if (!I->hasMetadata(LLVMContext::MD_annotation)) 360 return false; 361 return any_of(I->getMetadata(LLVMContext::MD_annotation)->operands(), 362 [](const MDOperand &Op) { 363 return cast<MDString>(Op.get())->getString() == "auto-init"; 364 }); 365 } 366 367 std::string AutoInitRemark::explainSource(StringRef Type) { 368 return (Type + " inserted by -ftrivial-auto-var-init.").str(); 369 } 370 371 StringRef AutoInitRemark::remarkName(RemarkKind RK) { 372 switch (RK) { 373 case RK_Store: 374 return "AutoInitStore"; 375 case RK_Unknown: 376 return "AutoInitUnknownInstruction"; 377 case RK_IntrinsicCall: 378 return "AutoInitIntrinsicCall"; 379 case RK_Call: 380 return "AutoInitCall"; 381 } 382 llvm_unreachable("missing RemarkKind case"); 383 } 384