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/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"
23 using namespace llvm::ore
;
25 MemoryOpRemark::~MemoryOpRemark() = default;
27 bool MemoryOpRemark::canHandle(const Instruction
*I
, const TargetLibraryInfo
&TLI
) {
28 if (isa
<StoreInst
>(I
))
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
:
46 if (auto *CI
= dyn_cast
<CallInst
>(I
)) {
47 auto *CF
= CI
->getCalledFunction();
55 bool KnownLibCall
= TLI
.getLibFunc(*CF
, LF
) && TLI
.has(LF
);
60 case LibFunc_memcpy_chk
:
61 case LibFunc_mempcpy_chk
:
62 case LibFunc_memset_chk
:
63 case LibFunc_memmove_chk
:
79 void MemoryOpRemark::visit(const Instruction
*I
) {
80 // For some of them, we can provide more information:
84 // * volatile / atomic
85 if (auto *SI
= dyn_cast
<StoreInst
>(I
)) {
91 // * user-friendly name
93 if (auto *II
= dyn_cast
<IntrinsicInst
>(I
)) {
94 visitIntrinsicCall(*II
);
99 // * known/unknown function (e.g. the compiler knows bzero, but it doesn't
101 // * memory operation size
102 if (auto *CI
= dyn_cast
<CallInst
>(I
)) {
110 std::string
MemoryOpRemark::explainSource(StringRef Type
) const {
111 return (Type
+ ".").str();
114 StringRef
MemoryOpRemark::remarkName(RemarkKind RK
) const {
117 return "MemoryOpStore";
119 return "MemoryOpUnknown";
120 case RK_IntrinsicCall
:
121 return "MemoryOpIntrinsicCall";
123 return "MemoryOpCall";
125 llvm_unreachable("missing RemarkKind case");
128 static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline
, bool Volatile
,
130 DiagnosticInfoIROptimization
&R
) {
131 if (Inline
&& *Inline
)
132 R
<< " Inlined: " << NV("StoreInlined", true) << ".";
134 R
<< " Volatile: " << NV("StoreVolatile", true) << ".";
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
)
141 if (Inline
&& !*Inline
)
142 R
<< " Inlined: " << NV("StoreInlined", false) << ".";
144 R
<< " Volatile: " << NV("StoreVolatile", false) << ".";
146 R
<< " Atomic: " << NV("StoreAtomic", false) << ".";
149 static std::optional
<uint64_t>
150 getSizeInBytes(std::optional
<uint64_t> SizeInBits
) {
151 if (!SizeInBits
|| *SizeInBits
% 8 != 0)
153 return *SizeInBits
/ 8;
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
...);
165 llvm_unreachable("unexpected DiagnosticKind");
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());
174 auto R
= makeRemark(RemarkPass
.data(), remarkName(RK_Store
), &SI
);
175 *R
<< explainSource("Store") << "\nStore size: " << NV("StoreSize", Size
)
177 visitPtr(SI
.getOperand(1), /*IsRead=*/false, *R
);
178 inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile
, Atomic
, *R
);
182 void MemoryOpRemark::visitUnknown(const Instruction
&I
) {
183 auto R
= makeRemark(RemarkPass
.data(), remarkName(RK_Unknown
), &I
);
184 *R
<< explainSource("Initialization");
188 void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst
&II
) {
189 SmallString
<32> CallTo
;
192 switch (II
.getIntrinsicID()) {
193 case Intrinsic::memcpy_inline
:
197 case Intrinsic::memcpy
:
200 case Intrinsic::memmove
:
203 case Intrinsic::memset
:
206 case Intrinsic::memcpy_element_unordered_atomic
:
210 case Intrinsic::memmove_element_unordered_atomic
:
214 case Intrinsic::memset_element_unordered_atomic
:
219 return visitUnknown(II
);
222 auto R
= makeRemark(RemarkPass
.data(), remarkName(RK_IntrinsicCall
), &II
);
223 visitCallee(CallTo
.str(), /*KnownLibCall=*/true, *R
);
224 visitSizeOperand(II
.getOperand(2), *R
);
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
);
237 case Intrinsic::memset
:
238 case Intrinsic::memset_element_unordered_atomic
:
239 visitPtr(II
.getOperand(0), /*IsRead=*/false, *R
);
242 inlineVolatileOrAtomicWithExtraArgs(&Inline
, Volatile
, Atomic
, *R
);
246 void MemoryOpRemark::visitCall(const CallInst
&CI
) {
247 Function
*F
= CI
.getCalledFunction();
249 return visitUnknown(CI
);
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
);
259 template <typename FTy
>
260 void MemoryOpRemark::visitCallee(FTy F
, bool KnownLibCall
,
261 DiagnosticInfoIROptimization
&R
) {
264 R
<< NV("UnknownLibCall", "unknown") << " function ";
265 R
<< NV("Callee", F
) << explainSource("");
268 void MemoryOpRemark::visitKnownLibCall(const CallInst
&CI
, LibFunc LF
,
269 DiagnosticInfoIROptimization
&R
) {
273 case LibFunc_memset_chk
:
275 visitSizeOperand(CI
.getOperand(2), R
);
276 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
279 visitSizeOperand(CI
.getOperand(1), R
);
280 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
282 case LibFunc_memcpy_chk
:
283 case LibFunc_mempcpy_chk
:
284 case LibFunc_memmove_chk
:
286 case LibFunc_mempcpy
:
287 case LibFunc_memmove
:
289 visitSizeOperand(CI
.getOperand(2), R
);
290 visitPtr(CI
.getOperand(1), /*IsRead=*/true, R
);
291 visitPtr(CI
.getOperand(0), /*IsRead=*/false, R
);
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.";
303 static std::optional
<StringRef
> nameOrNone(const Value
*V
) {
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
};
316 Result
.push_back(std::move(Var
));
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
));
334 for_each(findDbgDeclares(const_cast<Value
*>(V
)), FindDI
);
335 for_each(findDPVDeclares(const_cast<Value
*>(V
)), FindDI
);
338 assert(!Result
.empty());
342 const auto *AI
= dyn_cast
<AllocaInst
>(V
);
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
};
352 Result
.push_back(std::move(Var
));
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
);
366 uint64_t Size
= Ptr
->getPointerDereferenceableBytes(DL
, CanBeNull
, CanBeFreed
);
369 VIs
.push_back({std::nullopt
, Size
});
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.");
379 R
<< NV(IsRead
? "RVarName" : "WVarName", *VI
.Name
);
381 R
<< NV(IsRead
? "RVarName" : "WVarName", "<unknown>");
383 R
<< " (" << NV(IsRead
? "RVarSize" : "WVarSize", *VI
.Size
) << " bytes)";
388 bool AutoInitRemark::canHandle(const Instruction
*I
) {
389 if (!I
->hasMetadata(LLVMContext::MD_annotation
))
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";
398 std::string
AutoInitRemark::explainSource(StringRef Type
) const {
399 return (Type
+ " inserted by -ftrivial-auto-var-init.").str();
402 StringRef
AutoInitRemark::remarkName(RemarkKind RK
) const {
405 return "AutoInitStore";
407 return "AutoInitUnknownInstruction";
408 case RK_IntrinsicCall
:
409 return "AutoInitIntrinsicCall";
411 return "AutoInitCall";
413 llvm_unreachable("missing RemarkKind case");