[ORC] Add std::tuple support to SimplePackedSerialization.
[llvm-project.git] / llvm / lib / Transforms / Utils / MemoryOpRemark.cpp
blob68d4dd9d576b39a2f37ee50dcac883ddd4cd89f3
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.
11 //===----------------------------------------------------------------------===//
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"
20 using namespace llvm;
21 using namespace llvm::ore;
23 MemoryOpRemark::~MemoryOpRemark() = default;
25 bool MemoryOpRemark::canHandle(const Instruction *I, const TargetLibraryInfo &TLI) {
26 if (isa<StoreInst>(I))
27 return true;
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;
44 if (auto *CI = dyn_cast<CallInst>(I)) {
45 auto *CF = CI->getCalledFunction();
46 if (!CF)
47 return false;
49 if (!CF->hasName())
50 return false;
52 LibFunc LF;
53 bool KnownLibCall = TLI.getLibFunc(*CF, LF) && TLI.has(LF);
54 if (!KnownLibCall)
55 return false;
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;
74 return false;
77 void MemoryOpRemark::visit(const Instruction *I) {
78 // For some of them, we can provide more information:
80 // For stores:
81 // * size
82 // * volatile / atomic
83 if (auto *SI = dyn_cast<StoreInst>(I)) {
84 visitStore(*SI);
85 return;
88 // For intrinsics:
89 // * user-friendly name
90 // * size
91 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
92 visitIntrinsicCall(*II);
93 return;
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;
105 visitUnknown(*I);
108 std::string MemoryOpRemark::explainSource(StringRef Type) const {
109 return (Type + ".").str();
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";
123 llvm_unreachable("missing RemarkKind case");
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) << ".";
147 static Optional<uint64_t> getSizeInBytes(Optional<uint64_t> SizeInBits) {
148 if (!SizeInBits || *SizeInBits % 8 != 0)
149 return None;
150 return *SizeInBits / 8;
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");
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());
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);
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);
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);
219 auto R = makeRemark(RemarkPass.data(), remarkName(RK_IntrinsicCall), &II);
220 visitCallee(CallTo.str(), /*KnownLibCall=*/true, *R);
221 visitSizeOperand(II.getOperand(2), *R);
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;
239 inlineVolatileOrAtomicWithExtraArgs(&Inline, Volatile, Atomic, *R);
240 ORE.emit(*R);
243 void MemoryOpRemark::visitCall(const CallInst &CI) {
244 Function *F = CI.getCalledFunction();
245 if (!F)
246 return visitUnknown(CI);
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);
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("");
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;
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.";
300 static Optional<StringRef> nameOrNone(const Value *V) {
301 if (V->hasName())
302 return V->getName();
303 return None;
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;
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;
332 if (FoundDI) {
333 assert(!Result.empty());
334 return;
337 const auto *AI = dyn_cast<AllocaInst>(V);
338 if (!AI)
339 return;
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));
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);
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});
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)";
380 R << ".";
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";
392 std::string AutoInitRemark::explainSource(StringRef Type) const {
393 return (Type + " inserted by -ftrivial-auto-var-init.").str();
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";
407 llvm_unreachable("missing RemarkKind case");