1 //===-- MemoryOpRemark.cpp - Auto-init remark analysis---------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace llvm::ore
;
23 MemoryOpRemark::~MemoryOpRemark() = default;
25 bool MemoryOpRemark::canHandle(const Instruction
*I
, const TargetLibraryInfo
&TLI
) {
26 if (isa
<StoreInst
>(I
))
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
:
44 if (auto *CI
= dyn_cast
<CallInst
>(I
)) {
45 auto *CF
= CI
->getCalledFunction();
53 bool KnownLibCall
= TLI
.getLibFunc(*CF
, LF
) && TLI
.has(LF
);
58 case LibFunc_memcpy_chk
:
59 case LibFunc_mempcpy_chk
:
60 case LibFunc_memset_chk
:
61 case LibFunc_memmove_chk
:
77 void MemoryOpRemark::visit(const Instruction
*I
) {
78 // For some of them, we can provide more information:
82 // * volatile / atomic
83 if (auto *SI
= dyn_cast
<StoreInst
>(I
)) {
89 // * user-friendly name
91 if (auto *II
= dyn_cast
<IntrinsicInst
>(I
)) {
92 visitIntrinsicCall(*II
);
97 // * known/unknown function (e.g. the compiler knows bzero, but it doesn't
99 // * memory operation size
100 if (auto *CI
= dyn_cast
<CallInst
>(I
)) {
108 std::string
MemoryOpRemark::explainSource(StringRef Type
) const {
109 return (Type
+ ".").str();
112 StringRef
MemoryOpRemark::remarkName(RemarkKind RK
) const {
115 return "MemoryOpStore";
117 return "MemoryOpUnknown";
118 case RK_IntrinsicCall
:
119 return "MemoryOpIntrinsicCall";
121 return "MemoryOpCall";
123 llvm_unreachable("missing RemarkKind case");
126 static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline
, bool Volatile
,
128 DiagnosticInfoIROptimization
&R
) {
129 if (Inline
&& *Inline
)
130 R
<< " Inlined: " << NV("StoreInlined", true) << ".";
132 R
<< " Volatile: " << NV("StoreVolatile", true) << ".";
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
)
139 if (Inline
&& !*Inline
)
140 R
<< " Inlined: " << NV("StoreInlined", false) << ".";
142 R
<< " Volatile: " << NV("StoreVolatile", false) << ".";
144 R
<< " Atomic: " << NV("StoreAtomic", false) << ".";
147 static Optional
<uint64_t> getSizeInBytes(Optional
<uint64_t> SizeInBits
) {
148 if (!SizeInBits
|| *SizeInBits
% 8 != 0)
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
...);
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
)
174 visitPtr(SI
.getOperand(1), /*IsRead=*/false, *R
);
175 inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile
, Atomic
, *R
);
179 void MemoryOpRemark::visitUnknown(const Instruction
&I
) {
180 auto R
= makeRemark(RemarkPass
.data(), remarkName(RK_Unknown
), &I
);
181 *R
<< explainSource("Initialization");
185 void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst
&II
) {
186 SmallString
<32> CallTo
;
189 switch (II
.getIntrinsicID()) {
190 case Intrinsic::memcpy_inline
:
194 case Intrinsic::memcpy
:
197 case Intrinsic::memmove
:
200 case Intrinsic::memset
:
203 case Intrinsic::memcpy_element_unordered_atomic
:
207 case Intrinsic::memmove_element_unordered_atomic
:
211 case Intrinsic::memset_element_unordered_atomic
:
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
);
234 case Intrinsic::memset
:
235 case Intrinsic::memset_element_unordered_atomic
:
236 visitPtr(II
.getOperand(0), /*IsRead=*/false, *R
);
239 inlineVolatileOrAtomicWithExtraArgs(&Inline
, Volatile
, Atomic
, *R
);
243 void MemoryOpRemark::visitCall(const CallInst
&CI
) {
244 Function
*F
= CI
.getCalledFunction();
246 return visitUnknown(CI
);
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
);
256 template <typename FTy
>
257 void MemoryOpRemark::visitCallee(FTy F
, bool KnownLibCall
,
258 DiagnosticInfoIROptimization
&R
) {
261 R
<< NV("UnknownLibCall", "unknown") << " function ";
262 R
<< NV("Callee", F
) << explainSource("");
265 void MemoryOpRemark::visitKnownLibCall(const CallInst
&CI
, LibFunc LF
,
266 DiagnosticInfoIROptimization
&R
) {
270 case LibFunc_memset_chk
:
272 visitSizeOperand(CI
.getOperand(2), R
);
273 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
276 visitSizeOperand(CI
.getOperand(1), R
);
277 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
279 case LibFunc_memcpy_chk
:
280 case LibFunc_mempcpy_chk
:
281 case LibFunc_memmove_chk
:
283 case LibFunc_mempcpy
:
284 case LibFunc_memmove
:
286 visitSizeOperand(CI
.getOperand(2), R
);
287 visitPtr(CI
.getOperand(1), /*IsRead=*/true, R
);
288 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
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
) {
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
};
313 Result
.push_back(std::move(Var
));
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
));
333 assert(!Result
.empty());
337 const auto *AI
= dyn_cast
<AllocaInst
>(V
);
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
};
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
);
361 uint64_t Size
= Ptr
->getPointerDereferenceableBytes(DL
, CanBeNull
, CanBeFreed
);
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.");
374 R
<< NV(IsRead
? "RVarName" : "WVarName", *VI
.Name
);
376 R
<< NV(IsRead
? "RVarName" : "WVarName", "<unknown>");
378 R
<< " (" << NV(IsRead
? "RVarSize" : "WVarSize", *VI
.Size
) << " bytes)";
383 bool AutoInitRemark::canHandle(const Instruction
*I
) {
384 if (!I
->hasMetadata(LLVMContext::MD_annotation
))
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 {
399 return "AutoInitStore";
401 return "AutoInitUnknownInstruction";
402 case RK_IntrinsicCall
:
403 return "AutoInitIntrinsicCall";
405 return "AutoInitCall";
407 llvm_unreachable("missing RemarkKind case");