LLVM  14.0.0git
MemoryOpRemark.cpp
Go to the documentation of this file.
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 
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 
24 
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 
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 
109  return (Type + ".").str();
110 }
111 
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,
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 
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()) {
158  return std::make_unique<OptimizationRemarkAnalysis>(Args...);
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,
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,
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 
301  if (V->hasName())
302  return V->getName();
303  return None;
304 }
305 
306 void MemoryOpRemark::visitVariable(const Value *V,
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);
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);
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 
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 
393  return (Type + " inserted by -ftrivial-auto-var-init.").str();
394 }
395 
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 }
llvm::Check::Size
@ Size
Definition: FileCheck.h:73
i
i
Definition: README.txt:29
llvm
---------------------— PointerInfo ------------------------------------—
Definition: AllocatorList.h:23
IntrinsicInst.h
llvm::TypeSize::getFixedSize
ScalarTy getFixedSize() const
Definition: TypeSize.h:426
llvm::Function
Definition: Function.h:61
llvm::IntrinsicInst::getIntrinsicID
Intrinsic::ID getIntrinsicID() const
Return the intrinsic ID of this intrinsic.
Definition: IntrinsicInst.h:52
llvm::SmallVector
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1168
llvm::Value::hasName
bool hasName() const
Definition: Value.h:262
ValueTracking.h
OptimizationRemarkEmitter.h
llvm::MemoryOpRemark::explainSource
virtual std::string explainSource(StringRef Type) const
Definition: MemoryOpRemark.cpp:108
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
llvm::Optional< uint64_t >
llvm::ore::NV
DiagnosticInfoOptimizationBase::Argument NV
Definition: OptimizationRemarkEmitter.h:136
llvm::DiagnosticInfoIROptimization
Common features for diagnostics dealing with optimization remarks that are used by IR passes.
Definition: DiagnosticInfo.h:618
llvm::FindDbgAddrUses
TinyPtrVector< DbgVariableIntrinsic * > FindDbgAddrUses(Value *V)
Finds all intrinsics declaring local variables as living in the memory that 'V' points to.
Definition: DebugInfo.cpp:46
F
#define F(x, y, z)
Definition: MD5.cpp:56
llvm::RISCVFenceField::R
@ R
Definition: RISCVBaseInfo.h:193
llvm::DILocalVariable
Local variable.
Definition: DebugInfoMetadata.h:3097
llvm::LibFunc
LibFunc
Definition: TargetLibraryInfo.h:34
llvm::CallBase::getCalledFunction
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation.
Definition: InstrTypes.h:1393
llvm::MemoryOpRemark::~MemoryOpRemark
virtual ~MemoryOpRemark()
llvm::ms_demangle::QualifierMangleMode::Result
@ Result
llvm::MemoryOpRemark::remarkName
virtual StringRef remarkName(RemarkKind RK) const
Definition: MemoryOpRemark.cpp:112
llvm::TargetLibraryInfo::getLibFunc
bool getLibFunc(StringRef funcName, LibFunc &F) const
Searches for a particular function name.
Definition: TargetLibraryInfo.h:289
llvm::DK_OptimizationRemarkAnalysis
@ DK_OptimizationRemarkAnalysis
Definition: DiagnosticInfo.h:67
llvm::Instruction
Definition: Instruction.h:45
llvm::codeview::ModifierOptions::Volatile
@ Volatile
llvm::DK_OptimizationRemarkMissed
@ DK_OptimizationRemarkMissed
Definition: DiagnosticInfo.h:66
llvm::None
const NoneType None
Definition: None.h:23
llvm::AutoInitRemark::explainSource
virtual std::string explainSource(StringRef Type) const override
Definition: MemoryOpRemark.cpp:392
llvm::SmallString< 32 >
llvm::DbgVariableIntrinsic
This is the common base class for debug info intrinsics for variables.
Definition: IntrinsicInst.h:148
llvm::StoreInst
An instruction for storing to memory.
Definition: Instructions.h:304
VI
@ VI
Definition: SIInstrInfo.cpp:7679
llvm::Value::getPointerDereferenceableBytes
uint64_t getPointerDereferenceableBytes(const DataLayout &DL, bool &CanBeNull, bool &CanBeFreed) const
Returns the number of bytes known to be dereferenceable for the pointer value.
Definition: Value.cpp:830
uint64_t
llvm::AutoInitRemark::canHandle
static bool canHandle(const Instruction *I)
Definition: MemoryOpRemark.cpp:383
inlineVolatileOrAtomicWithExtraArgs
static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile, bool Atomic, DiagnosticInfoIROptimization &R)
Definition: MemoryOpRemark.cpp:126
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
DebugInfo.h
I
#define I(x, y, z)
Definition: MD5.cpp:59
MemoryOpRemark.h
llvm::codeview::CallingConvention::Inline
@ Inline
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
nameOrNone
static Optional< StringRef > nameOrNone(const Value *V)
Definition: MemoryOpRemark.cpp:300
memcpy
<%struct.s * > cast struct s *S to sbyte *< sbyte * > sbyte uint cast struct s *agg result to sbyte *< sbyte * > sbyte uint cast struct s *memtmp to sbyte *< sbyte * > sbyte uint ret void llc ends up issuing two memcpy or custom lower memcpy(of small size) to be ldmia/stmia. I think option 2 is better but the current register allocator cannot allocate a chunk of registers at a time. A feasible temporary solution is to use specific physical registers at the lowering time for small(<
SI
StandardInstrumentations SI(Debug, VerifyEach)
llvm::getUnderlyingObjectsForCodeGen
bool getUnderlyingObjectsForCodeGen(const Value *V, SmallVectorImpl< Value * > &Objects)
This is a wrapper around getUnderlyingObjects and adds support for basic ptrtoint+arithmetic+inttoptr...
Definition: ValueTracking.cpp:4472
llvm::TargetLibraryInfo::has
bool has(LibFunc F) const
Tests whether a library function is available.
Definition: TargetLibraryInfo.h:323
llvm::MemoryOpRemark::visit
void visit(const Instruction *I)
Definition: MemoryOpRemark.cpp:77
llvm::any_of
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1554
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:58
llvm_unreachable
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Definition: ErrorHandling.h:136
llvm::MemoryOpRemark::canHandle
static bool canHandle(const Instruction *I, const TargetLibraryInfo &TLI)
Definition: MemoryOpRemark.cpp:25
DL
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Definition: AArch64SLSHardening.cpp:76
llvm::Value::getName
StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:297
getSizeInBytes
static Optional< uint64_t > getSizeInBytes(Optional< uint64_t > SizeInBits)
Definition: MemoryOpRemark.cpp:147
llvm::MemoryOpRemark::RemarkKind
RemarkKind
Definition: MemoryOpRemark.h:56
llvm::AMDGPU::SendMsg::Op
Op
Definition: SIDefines.h:321
llvm::ore::setExtraArgs
DiagnosticInfoOptimizationBase::setExtraArgs setExtraArgs
Definition: OptimizationRemarkEmitter.h:138
llvm::TargetLibraryInfo
Provides information about what library functions are available for the current target.
Definition: TargetLibraryInfo.h:219
llvm::SmallString::str
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:259
llvm::IntrinsicInst
A wrapper class for inspecting calls to intrinsic functions.
Definition: IntrinsicInst.h:45
Instructions.h
llvm::SmallVectorImpl
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: APFloat.h:43
llvm::CallInst
This class represents a function call, abstracting a target machine's calling convention.
Definition: Instructions.h:1475
llvm::ore
Add a small namespace to avoid name clashes with the classes used in the streaming interface.
Definition: OptimizationRemarkEmitter.h:135
llvm::AMDGPU::HSAMD::Kernel::Key::Args
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
Definition: AMDGPUMetadata.h:389
llvm::User::getOperand
Value * getOperand(unsigned i) const
Definition: User.h:169
llvm::AutoInitRemark::remarkName
virtual StringRef remarkName(RemarkKind RK) const override
Definition: MemoryOpRemark.cpp:396
llvm::Value
LLVM Value Representation.
Definition: Value.h:75
llvm::MDOperand
Tracking metadata reference owned by Metadata.
Definition: Metadata.h:748