MemoryOpRemark.cpp 12 KB

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