xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/Transforms/Utils/AutoInitRemark.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 //===-- AutoInitRemark.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/AutoInitRemark.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 
volatileOrAtomicWithExtraArgs(bool Volatile,bool Atomic,OptimizationRemarkMissed & R)23 static void volatileOrAtomicWithExtraArgs(bool Volatile, bool Atomic,
24                                           OptimizationRemarkMissed &R) {
25   if (Volatile)
26     R << " Volatile: " << NV("StoreVolatile", true) << ".";
27   if (Atomic)
28     R << " Atomic: " << NV("StoreAtomic", true) << ".";
29   // Emit StoreVolatile: false and StoreAtomic: false under ExtraArgs. This
30   // won't show them in the remark message but will end up in the serialized
31   // remarks.
32   if (!Volatile || !Atomic)
33     R << setExtraArgs();
34   if (!Volatile)
35     R << " Volatile: " << NV("StoreVolatile", false) << ".";
36   if (!Atomic)
37     R << " Atomic: " << NV("StoreAtomic", false) << ".";
38 }
39 
getSizeInBytes(Optional<uint64_t> SizeInBits)40 static Optional<uint64_t> getSizeInBytes(Optional<uint64_t> SizeInBits) {
41   if (!SizeInBits || *SizeInBits % 8 != 0)
42     return None;
43   return *SizeInBits / 8;
44 }
45 
inspectStore(StoreInst & SI)46 void AutoInitRemark::inspectStore(StoreInst &SI) {
47   bool Volatile = SI.isVolatile();
48   bool Atomic = SI.isAtomic();
49   int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType());
50 
51   OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitStore", &SI);
52   R << "Store inserted by -ftrivial-auto-var-init.\nStore size: "
53     << NV("StoreSize", Size) << " bytes.";
54   inspectDst(SI.getOperand(1), R);
55   volatileOrAtomicWithExtraArgs(Volatile, Atomic, R);
56   ORE.emit(R);
57 }
58 
inspectUnknown(Instruction & I)59 void AutoInitRemark::inspectUnknown(Instruction &I) {
60   ORE.emit(OptimizationRemarkMissed(RemarkPass.data(),
61                                     "AutoInitUnknownInstruction", &I)
62            << "Initialization inserted by -ftrivial-auto-var-init.");
63 }
64 
inspectIntrinsicCall(IntrinsicInst & II)65 void AutoInitRemark::inspectIntrinsicCall(IntrinsicInst &II) {
66   SmallString<32> CallTo;
67   bool Atomic = false;
68   switch (II.getIntrinsicID()) {
69   case Intrinsic::memcpy:
70     CallTo = "memcpy";
71     break;
72   case Intrinsic::memmove:
73     CallTo = "memmove";
74     break;
75   case Intrinsic::memset:
76     CallTo = "memset";
77     break;
78   case Intrinsic::memcpy_element_unordered_atomic:
79     CallTo = "memcpy";
80     Atomic = true;
81     break;
82   case Intrinsic::memmove_element_unordered_atomic:
83     CallTo = "memmove";
84     Atomic = true;
85     break;
86   case Intrinsic::memset_element_unordered_atomic:
87     CallTo = "memset";
88     Atomic = true;
89     break;
90   default:
91     return inspectUnknown(II);
92   }
93 
94   OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitIntrinsic", &II);
95   inspectCallee(StringRef(CallTo), /*KnownLibCall=*/true, R);
96   inspectSizeOperand(II.getOperand(2), R);
97 
98   auto *CIVolatile = dyn_cast<ConstantInt>(II.getOperand(3));
99   // No such thing as a memory intrinsic that is both atomic and volatile.
100   bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue();
101   inspectDst(II.getOperand(0), R);
102   volatileOrAtomicWithExtraArgs(Volatile, Atomic, R);
103   ORE.emit(R);
104 }
105 
inspectCall(CallInst & CI)106 void AutoInitRemark::inspectCall(CallInst &CI) {
107   Function *F = CI.getCalledFunction();
108   if (!F)
109     return inspectUnknown(CI);
110 
111   LibFunc LF;
112   bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF);
113   OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitCall", &CI);
114   inspectCallee(F, KnownLibCall, R);
115   inspectKnownLibCall(CI, LF, R);
116   ORE.emit(R);
117 }
118 
119 template <typename FTy>
inspectCallee(FTy F,bool KnownLibCall,OptimizationRemarkMissed & R)120 void AutoInitRemark::inspectCallee(FTy F, bool KnownLibCall,
121                                    OptimizationRemarkMissed &R) {
122   R << "Call to ";
123   if (!KnownLibCall)
124     R << NV("UnknownLibCall", "unknown") << " function ";
125   R << NV("Callee", F) << " inserted by -ftrivial-auto-var-init.";
126 }
127 
inspectKnownLibCall(CallInst & CI,LibFunc LF,OptimizationRemarkMissed & R)128 void AutoInitRemark::inspectKnownLibCall(CallInst &CI, LibFunc LF,
129                                          OptimizationRemarkMissed &R) {
130   switch (LF) {
131   default:
132     return;
133   case LibFunc_bzero:
134     inspectSizeOperand(CI.getOperand(1), R);
135     inspectDst(CI.getOperand(0), R);
136     break;
137   }
138 }
139 
inspectSizeOperand(Value * V,OptimizationRemarkMissed & R)140 void AutoInitRemark::inspectSizeOperand(Value *V, OptimizationRemarkMissed &R) {
141   if (auto *Len = dyn_cast<ConstantInt>(V)) {
142     uint64_t Size = Len->getZExtValue();
143     R << " Memory operation size: " << NV("StoreSize", Size) << " bytes.";
144   }
145 }
146 
inspectVariable(const Value * V,SmallVectorImpl<VariableInfo> & Result)147 void AutoInitRemark::inspectVariable(const Value *V,
148                                      SmallVectorImpl<VariableInfo> &Result) {
149   // If we find some information in the debug info, take that.
150   bool FoundDI = false;
151   // Try to get an llvm.dbg.declare, which has a DILocalVariable giving us the
152   // real debug info name and size of the variable.
153   for (const DbgVariableIntrinsic *DVI :
154        FindDbgAddrUses(const_cast<Value *>(V))) {
155     if (DILocalVariable *DILV = DVI->getVariable()) {
156       Optional<uint64_t> DISize = getSizeInBytes(DILV->getSizeInBits());
157       VariableInfo Var{DILV->getName(), DISize};
158       if (!Var.isEmpty()) {
159         Result.push_back(std::move(Var));
160         FoundDI = true;
161       }
162     }
163   }
164   if (FoundDI) {
165     assert(!Result.empty());
166     return;
167   }
168 
169   const auto *AI = dyn_cast<AllocaInst>(V);
170   if (!AI)
171     return;
172 
173   // If not, get it from the alloca.
174   Optional<StringRef> Name = AI->hasName()
175                                  ? Optional<StringRef>(AI->getName())
176                                  : Optional<StringRef>(None);
177   Optional<TypeSize> TySize = AI->getAllocationSizeInBits(DL);
178   Optional<uint64_t> Size =
179       TySize ? getSizeInBytes(TySize->getFixedSize()) : None;
180   VariableInfo Var{Name, Size};
181   if (!Var.isEmpty())
182     Result.push_back(std::move(Var));
183 }
184 
inspectDst(Value * Dst,OptimizationRemarkMissed & R)185 void AutoInitRemark::inspectDst(Value *Dst, OptimizationRemarkMissed &R) {
186   // Find if Dst is a known variable we can give more information on.
187   SmallVector<const Value *, 2> Objects;
188   getUnderlyingObjects(Dst, Objects);
189   SmallVector<VariableInfo, 2> VIs;
190   for (const Value *V : Objects)
191     inspectVariable(V, VIs);
192 
193   if (VIs.empty())
194     return;
195 
196   R << "\nVariables: ";
197   for (unsigned i = 0; i < VIs.size(); ++i) {
198     const VariableInfo &VI = VIs[i];
199     assert(!VI.isEmpty() && "No extra content to display.");
200     if (i != 0)
201       R << ", ";
202     if (VI.Name)
203       R << NV("VarName", *VI.Name);
204     else
205       R << NV("VarName", "<unknown>");
206     if (VI.Size)
207       R << " (" << NV("VarSize", *VI.Size) << " bytes)";
208   }
209   R << ".";
210 }
211