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