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