Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
KernelArgPtrUseVisitor.h
Go to the documentation of this file.
1#ifndef PROTEUS_KERNELARGPTRDEFVISITOR_H
2#define PROTEUS_KERNELARGPTRDEFVISITOR_H
3
4#include "Helpers.h"
8#include <llvm/Analysis/PtrUseVisitor.h>
9#include <llvm/Analysis/ValueTracking.h>
10
11#include <llvm/ADT/DenseMap.h>
12#include <llvm/ADT/DenseSet.h>
13#include <llvm/ADT/Hashing.h>
14#include <llvm/ADT/SmallPtrSet.h>
15#include <llvm/ADT/SmallVector.h>
16#include <llvm/Analysis/AssumptionCache.h>
17#include <llvm/Analysis/BasicAliasAnalysis.h>
18#include <llvm/Analysis/MemoryLocation.h>
19#include <llvm/Analysis/MemorySSA.h>
20#include <llvm/Analysis/TargetLibraryInfo.h>
21#include <llvm/Analysis/ValueTracking.h>
22#include <llvm/IR/Constants.h>
23#include <llvm/IR/DataLayout.h>
24#include <llvm/IR/DebugInfo.h>
25#include <llvm/IR/Dominators.h>
26#include <llvm/IR/Function.h>
27#include <llvm/IR/InstrTypes.h>
28#include <llvm/IR/Instruction.h>
29#include <llvm/IR/Instructions.h>
30#include <llvm/IR/Metadata.h>
31#include <llvm/IR/Module.h>
32#include <llvm/IR/PassManager.h>
33#include <llvm/IR/Type.h>
34#include <llvm/IR/Value.h>
35#include <llvm/TargetParser/Triple.h>
36#include <memory>
37#include <optional>
38
39namespace proteus {
40using namespace llvm;
41
42// Any instruction creating a new ptr needs use analysis
43bool needsDefUseAnalysis(Value *Val) {
44 return isa<AddrSpaceCastInst>(Val) || isa<AllocaInst>(Val) ||
45 isa<BitCastInst>(Val) || isa<IntToPtrInst>(Val);
46}
47
48inline bool offsetCoveredByRange(int64_t TargetOffset, int64_t RangeOffset,
49 uint64_t RangeSize) {
50 DEBUG(Logger::logs("proteus-pass")
51 << " [PTR use analysis]: Target Offset = " << TargetOffset << "\n");
52 DEBUG(Logger::logs("proteus-pass")
53 << " [PTR use analysis]: Range Offset = " << RangeOffset << "\n");
54 DEBUG(Logger::logs("proteus-pass")
55 << " [PTR use analysis]: Range Size = " << RangeSize << "\n");
56 return TargetOffset >= RangeOffset &&
57 static_cast<uint64_t>(TargetOffset - RangeOffset) < RangeSize;
58}
59
60inline std::optional<uint64_t> getTypeStoreSize(const DataLayout &DL,
61 Type *Ty) {
62 if (!Ty || !Ty->isSized())
63 return std::nullopt;
64 return static_cast<uint64_t>(DL.getTypeStoreSize(Ty));
65}
66
68 Value *DominatingWrite = nullptr;
69 int64_t Offset = 0;
70 // Sometimes instructions like ptrtoint --> inttoptr change the layout of
71 // the kernel args.
72 std::optional<RuntimeConstantType> ChangedRCLayout = std::nullopt;
73};
74
77 CallBase *CallerCB;
78 Function *Callee;
79};
80
81// We track use-edges in our analysis.
82struct UseEdge {
83 Value *CurVal;
84 Value *LastVal;
85};
86
87inline std::optional<MemoryLocation>
88getTrackedPointerLocation(const DataLayout &DL, Value *Ptr) {
89 if (!Ptr || !Ptr->getType()->isPointerTy())
90 return std::nullopt;
91
92 Type *PointeeTy = nullptr;
93 if (auto *AI = dyn_cast<AllocaInst>(Ptr))
94 PointeeTy = AI->getAllocatedType();
95
96 if (!PointeeTy || !PointeeTy->isSized())
97 return MemoryLocation::getBeforeOrAfter(Ptr);
98
99 return MemoryLocation(Ptr,
100 LocationSize::precise(DL.getTypeStoreSize(PointeeTy)));
101}
102
103// Find the closest preceding same-block store whose written range covers the
104// tracked byte. This preserves execution order where Value::users() does not:
105// a later overwrite must win over an earlier initializer. Calls that might
106// clobber the storage force the general analysis instead. See the latest-store
107// and capture-overwrite cases in tests/gpu/lambda_store_order.cpp.
108inline std::optional<LambdaPtrUseAnalysis>
109getPreviousCoveringStore(const DataLayout &DL, Value *Ptr, Value *UseBoundary,
110 int64_t TargetOffset) {
111 auto *PtrI = dyn_cast<Instruction>(Ptr);
112 auto *BoundaryI = dyn_cast<Instruction>(UseBoundary);
113 if (!PtrI || !BoundaryI || PtrI->getFunction() != BoundaryI->getFunction() ||
114 PtrI->getParent() != BoundaryI->getParent())
115 return std::nullopt;
116
117 int64_t RootOffset = 0;
118 Value *RootBase = GetPointerBaseWithConstantOffset(Ptr, RootOffset, DL);
119 if (!RootBase)
120 return std::nullopt;
121 int64_t AbsoluteTarget = RootOffset + TargetOffset;
122
123 // A backwards provenance edge commonly ends at the GEP that computes the
124 // lambda field, while a modifying call follows that GEP before the field is
125 // read. Do not select an older initializer across such a call: the regular
126 // def-use visitor must inspect the call (and, for a dynamic memcpy, decline
127 // specialization). For a non-escaping local, a call can only clobber this
128 // storage if one of its pointer arguments aliases the same base.
129 for (Instruction *I = BoundaryI->getNextNode(); I; I = I->getNextNode()) {
130 auto *CB = dyn_cast<CallBase>(I);
131 if (!CB || isa<DbgInfoIntrinsic>(CB) || CB->onlyReadsMemory())
132 continue;
133 for (Value *Arg : CB->args()) {
134 if (!Arg->getType()->isPointerTy())
135 continue;
136 int64_t ArgOffset = 0;
137 Value *ArgBase = GetPointerBaseWithConstantOffset(Arg, ArgOffset, DL);
138 if (ArgBase == RootBase)
139 return std::nullopt;
140 }
141 }
142
143 for (Instruction *I = BoundaryI->getPrevNode(); I; I = I->getPrevNode()) {
144 if (auto *SI = dyn_cast<StoreInst>(I)) {
145 int64_t StoreOffset = 0;
146 Value *StoreBase = GetPointerBaseWithConstantOffset(
147 SI->getPointerOperand(), StoreOffset, DL);
148 auto StoreSize = getTypeStoreSize(DL, SI->getValueOperand()->getType());
149 if (StoreBase == RootBase && StoreSize &&
150 offsetCoveredByRange(AbsoluteTarget, StoreOffset, *StoreSize))
151 return LambdaPtrUseAnalysis{.DominatingWrite = SI->getValueOperand(),
152 .Offset = StoreOffset - RootOffset,
153 .ChangedRCLayout = std::nullopt};
154 continue;
155 }
156
157 // A call that may write memory could clobber Ptr. Leave such cases to the
158 // interprocedural visitor, which understands memcpy and pointer arguments.
159 if (auto *CB = dyn_cast<CallBase>(I)) {
160 if (!isa<DbgInfoIntrinsic>(CB) && !CB->onlyReadsMemory())
161 return std::nullopt;
162 }
163 }
164 return std::nullopt;
165}
166
167// Given a newly allocated ptr encountered in def-use analysis beginning at a
168// Lambda callsite, we need to determine which definition dominates that ptr.
169class LambdaInstUseVisitor : public InstVisitor<LambdaInstUseVisitor> {
170private:
171 DominatorTree DTree;
172 int64_t Offset = 0;
173 // The ValueOffsetMap contains the "live range" of the ptr we're analyzing.
174 // For example, let's say that LambdaInstUseVisitor is handed ptr %0 = alloca
175 // ptr, and LambdaArgVisitor has already identified that the closure starts at
176 // byte
177 // 8. In this case, ValueOffsetMap[%0] = 8. If we encounter a store like
178 // store ptr %2, ptr%0, align 8, we don't care, because its written outside
179 // of the range of the closure.
180 DenseMap<Value *, int64_t> ValueOffsetMap;
181 Value *TrackedBase = nullptr;
183 DataLayout DL;
184 SmallVector<UseEdge> WorkList;
185 // The visitor pattern is always setting LastUse to the back of the
186 // edge at the front of the worklist (the def that brought us to the
187 // current use).
188 Value *Def = nullptr;
189 SmallDenseSet<Value *> Seen;
190 bool AnalysisSuccess = false;
191 bool AnalysisFailed = false;
192
193public:
194 // Constructor used whenever a NeedsDefUseAnalysis Value is encountered. We
195 // need to track where the calling LambdaArgVisitor came in from, so that our
196 // analysis does not
197 LambdaInstUseVisitor(Value *PtrBegin, Value *SeenUse, CallBase *LambdaCB,
198 const DataLayout &Dl, int64_t TargetOff)
199 : TrackedBase(PtrBegin), DL(Dl) {
200 WorkList.push_back({PtrBegin, nullptr});
201 // A pointer-transform on the backwards provenance path may also have an
202 // earlier store as a user. Visit that transform so those writes remain
203 // visible, but stop before re-entering the lambda invocation itself.
204 if (!isa<GetElementPtrInst, BitCastInst, AddrSpaceCastInst>(SeenUse))
205 Seen.insert(SeenUse);
206 if (LambdaCB)
207 Seen.insert(LambdaCB);
208 ValueOffsetMap[PtrBegin] = TargetOff;
209 }
210 auto back() { return WorkList.back(); }
211 auto popBack() {
212 auto Result = WorkList.back();
213 Def = Result.LastVal;
214 WorkList.pop_back();
215 return Result;
216 }
217 auto getLastDef() { return Def; }
218 bool seen(Value *Val) { return Seen.contains(Val); }
219 void markAsSeen(Value *Val) { Seen.insert(Val); }
220 bool empty() { return WorkList.empty(); }
221 bool success() { return AnalysisSuccess; }
222 bool failed() { return AnalysisFailed; }
223
224 auto getAnalysisResult() { return Result; }
225
226 // Keep track of Function frame
227 void pushBack(Value *NextVal, Value *CurVal) {
228 WorkList.push_back(UseEdge{NextVal, CurVal});
229 }
230
231 void offsetValueMapFailure(Value *V) {
232 AnalysisFailed = true;
233 AnalysisSuccess = false;
234 DEBUG(Logger::logs("proteus-pass")
235 << " [PTR use analysis]: Analysis failed due to absence of " << *V
236 << " in offset tracking map, this is an internal compiler bug\n");
237 }
238
239 // WorkList is LIFO. Enqueue possible writers last so they are inspected
240 // before an older store reached through a GEP. Otherwise a memcpy/memmove
241 // call can be skipped merely because the initializer happens to appear
242 // earlier in Value::users().
243 void pushPointerUsers(Value *V) {
244 SmallVector<User *, 4> PossibleWriters;
245 for (User *Usr : V->users()) {
246 if (Seen.contains(Usr))
247 continue;
248 auto *CB = dyn_cast<CallBase>(Usr);
249 if (CB && !isa<DbgInfoIntrinsic>(CB) && !CB->onlyReadsMemory()) {
250 PossibleWriters.push_back(Usr);
251 continue;
252 }
253 pushBack(Usr, V);
254 }
255 for (User *Usr : PossibleWriters)
256 pushBack(Usr, V);
257 }
258
259 void visitStoreInst(StoreInst &SI) {
260 Value *Stored = SI.getValueOperand();
261 Value *StoreBase = SI.getPointerOperand();
262
263 // A pointer argument is commonly spilled in an unoptimized or optnone
264 // callee before it is used. Follow the slot's loads as carrying the same
265 // pointee-relative offset instead of interpreting this as a write to the
266 // tracked pointee.
267 if (Stored == Def && Stored->getType()->isPointerTy()) {
268 if (!ValueOffsetMap.contains(Stored)) {
269 offsetValueMapFailure(Stored);
270 return;
271 }
272 ValueOffsetMap[StoreBase] = ValueOffsetMap[Stored];
273 for (User *Usr : StoreBase->users())
274 if (Usr != &SI && !Seen.contains(Usr))
275 pushBack(Usr, StoreBase);
276 return;
277 }
278
279 auto StoreSize = getTypeStoreSize(DL, SI.getValueOperand()->getType());
280 if (!ValueOffsetMap.contains(StoreBase)) {
281 offsetValueMapFailure(StoreBase);
282 return;
283 }
284
285 if (!StoreSize ||
286 !offsetCoveredByRange(ValueOffsetMap[StoreBase], 0, *StoreSize))
287 return;
288 DEBUG(Logger::logs("proteus-pass")
289 << " Found PTRstore applicable to offset " << ValueOffsetMap[&SI]
290 << " Store size = " << *StoreSize << " ; " << SI << "\n");
291 AnalysisFailed = false;
292 AnalysisSuccess = true;
293 Result = {.DominatingWrite = SI.getValueOperand(),
294 .Offset = Offset,
295 .ChangedRCLayout = std::nullopt};
296 }
297
298 void visitLoadInst(LoadInst &LI) {
299 if (!LI.getType()->isPointerTy()) {
300 DEBUG(Logger::logs("proteus-pass")
301 << " [PTR use analysis]: Expected a pointer load, got " << LI
302 << "\n");
303 AnalysisFailed = true;
304 AnalysisSuccess = false;
305 return;
306 }
307 if (!ValueOffsetMap.contains(LI.getPointerOperand())) {
308 offsetValueMapFailure(LI.getPointerOperand());
309 return;
310 }
311 ValueOffsetMap[&LI] = ValueOffsetMap[LI.getPointerOperand()];
312 pushPointerUsers(&LI);
313 }
314
315 void visitCallBase(CallBase &CB) {
316 // Lifetime markers describe the validity of an allocation, not a write to
317 // its contents. Following their declaration as if it were an ordinary
318 // callee makes an otherwise valid search fail before reaching a store.
319 if (auto *II = dyn_cast<IntrinsicInst>(&CB)) {
320 if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
321 II->getIntrinsicID() == Intrinsic::lifetime_end)
322 return;
323 }
324
325 Function *F = CB.getCalledFunction();
326 if (!F || F->isDeclaration()) {
327 DEBUG(Logger::logs("proteus-pass")
328 << " [PTR use analysis]: Cannot trace indirect or declaration "
329 "call "
330 << CB << "\n");
331 AnalysisFailed = true;
332 AnalysisSuccess = false;
333 return;
334 }
335
336 Value *DefBeforeCB = getLastDef();
337 if (!DefBeforeCB || !ValueOffsetMap.contains(DefBeforeCB)) {
338 offsetValueMapFailure(DefBeforeCB ? DefBeforeCB : TrackedBase);
339 return;
340 }
341
342 bool FoundArg = false;
343 for (size_t ArgI = 0; ArgI < F->arg_size(); ++ArgI) {
344 DEBUG(Logger::logs("proteus-pass") << " ARG " << ArgI << " VAL "
345 << *CB.getArgOperand(ArgI) << "\n");
346 if (CB.getArgOperand(ArgI) != DefBeforeCB)
347 continue;
348
349 FoundArg = true;
350 Argument *ArgToTrack = F->getArg(ArgI);
351 ValueOffsetMap[ArgToTrack] = ValueOffsetMap[DefBeforeCB];
352 DEBUG(Logger::logs("proteus-pass")
353 << " Looking at uses of " << *ArgToTrack << "\n");
354 for (User *Usr : ArgToTrack->users())
355 pushBack(Usr, ArgToTrack);
356 }
357 DEBUG(Logger::logs("proteus-pass")
358 << " Beginning analysis within " << *F << "\n");
359 if (!FoundArg) {
360 DEBUG(Logger::logs("proteus-pass")
361 << " [PTR use analysis]: Call does not pass the tracked "
362 "pointer on any callee argument: "
363 << CB << "\n");
364 AnalysisFailed = true;
365 AnalysisSuccess = false;
366 }
367 }
368
369 void visitGetElementPtrInst(GetElementPtrInst &GEP) {
370 // We don't want to use GetPointerBaseWithConstantOffset here.
371 // We actually don't want the true "pointer base" here. I.E. if we are
372 // analyzing the dominating store to %4 = addrspacecast ptr addrspace(5) %3
373 // to ptr where %3 = alloca %class.anon.1, align 8, addrspace(5)
374 // GetPointerBaseWithConstantOffset gets us 3, which we (a) don't know about
375 // and (b) don't care about, we just care about the SSA to %4.
376 APInt StepOffset(DL.getIndexTypeSizeInBits(GEP.getType()), 0);
377 if (!GEP.accumulateConstantOffset(DL, StepOffset)) {
378 AnalysisFailed = true;
379 AnalysisSuccess = false;
380 return;
381 }
382
383 int64_t GEPOffset = StepOffset.getSExtValue();
384 DEBUG(Logger::logs("proteus-pass")
385 << " " << "Computed GEP offset " << GEPOffset << "\n");
386 Value *GEPBase = GEP.getPointerOperand();
387 if (!GEPBase)
388 return;
389
390 if (!ValueOffsetMap.contains(GEPBase)) {
391 offsetValueMapFailure(GEPBase);
392 return;
393 }
394
395 auto ResultSize = getTypeStoreSize(DL, GEP.getResultElementType());
396 DEBUG(if (ResultSize) Logger::logs("proteus-pass")
397 << " GEP size = " << *ResultSize << "\n";)
398 if (ResultSize &&
399 !offsetCoveredByRange(ValueOffsetMap[GEPBase], GEPOffset, *ResultSize))
400 return;
401 DEBUG(Logger::logs("proteus-pass")
402 << " Found GEP applicable to offset=" << ValueOffsetMap[GEPBase]
403 << " ; " << GEP << "\n");
404 // We found a GEP, now we need to track the GEP itself, so the TargetOffset
405 // is now zero again
406 ValueOffsetMap[&GEP] = ValueOffsetMap[GEPBase] - GEPOffset;
407 Offset += GEPOffset;
408 DEBUG(Logger::logs("proteus-pass")
409 << " " << "Setting map K " << GEP << " : " << ValueOffsetMap[&GEP]
410 << "\n");
411 pushPointerUsers(&GEP);
412 }
413
414 // todo: these three methods may need to be changed to find a dominating store
415 // particularly for the case of mutable lambdas.
416 void visitAllocaInst(AllocaInst &Alloca) {
417 // This analysis should only ever encounter an AllocaInst as the first
418 // instruction We assert this below and log a failure otherwise
419 if (Def) {
420 AnalysisFailed = true;
421 AnalysisSuccess = false;
422 DEBUG(Logger::logs("proteus-pass")
423 << " Dominating use analysis somehow reached AllocaInst from "
424 "non-null def\n");
425 return;
426 }
427 if (!ValueOffsetMap.contains(&Alloca)) {
428 AnalysisFailed = true;
429 AnalysisSuccess = false;
430 DEBUG(Logger::logs("proteus-pass")
431 << " Value offset map not correctly initialized with "
432 "AllocaInst\n");
433 return;
434 }
435
436 pushPointerUsers(&Alloca);
437 }
438
439 // TODO(bowen) come up with a unit test for an analysis starting with
440 // a BC
441 void visitBitCastInst(BitCastInst &BC) {
442 // If the last Def is nullptr, we have just begun the use analysis.
443 // In this case, respect the constructor's offset for the pointer operand.
444 if (!Def)
445 ValueOffsetMap[BC.getOperand(0)] = Offset;
446 // AddrSpaceCast does not change the offset we track.
447 ValueOffsetMap[&BC] = ValueOffsetMap[BC.getOperand(0)];
448 pushPointerUsers(&BC);
449 }
450
451 void visitAddrSpaceCastInst(AddrSpaceCastInst &ASC) {
452 // The constructor automatically populates the map with ASC's offset
453 // if its not present we need to rely on the pointer operand's offset
454 if (!ValueOffsetMap.contains(&ASC)) {
455 if (!ValueOffsetMap.contains(ASC.getPointerOperand())) {
456 offsetValueMapFailure(ASC.getPointerOperand());
457 return;
458 }
459 // AddrSpaceCast does not change the offset we track.
460 ValueOffsetMap[&ASC] = ValueOffsetMap[ASC.getPointerOperand()];
461 }
462 DEBUG(Logger::logs("proteus-pass")
463 << " [PTR use analysis]: Setting offset " << ASC << " = "
464 << ValueOffsetMap[&ASC]);
465
466 pushPointerUsers(&ASC);
467 }
468
469 void visitMemIntrinsic(MemIntrinsic &I) {
470 if (auto *MS = dyn_cast<MemSetInst>(&I)) {
471 if (Def != MS->getRawDest())
472 return;
473 if (!ValueOffsetMap.contains(Def)) {
475 return;
476 }
477 auto *Len = dyn_cast<ConstantInt>(MS->getLength());
478 if (Len &&
479 !offsetCoveredByRange(ValueOffsetMap[Def], 0, Len->getZExtValue()))
480 return;
481 // A covering memset destroys the tracked provenance, and a dynamic
482 // length cannot be proven not to cover it.
483 DEBUG(Logger::logs("proteus-pass")
484 << " [PTR use analysis]: Memset may overwrite the tracked "
485 "byte: "
486 << I << "\n");
487 AnalysisFailed = true;
488 AnalysisSuccess = false;
489 return;
490 }
491
492 auto *MT = cast<MemTransferInst>(&I); // memcpy/memmove
493
494 // A transfer defines the tracked memory only when the use edge reached it
495 // through the destination operand. Reaching the same intrinsic through
496 // its source is merely a read.
497 if (Def != MT->getRawDest())
498 return;
499
500 if (!ValueOffsetMap.contains(Def)) {
502 return;
503 }
504
505 auto *Len = dyn_cast<ConstantInt>(MT->getLength());
506 if (!Len) {
507 // We cannot prove that a dynamic-sized transfer defines the tracked byte.
508 DEBUG(Logger::logs("proteus-pass")
509 << " [PTR use analysis]: Dynamic-length transfer cannot prove "
510 "provenance for the tracked byte: "
511 << I << "\n");
512 AnalysisFailed = true;
513 AnalysisSuccess = false;
514 return;
515 }
516 if (!offsetCoveredByRange(ValueOffsetMap[Def], 0, Len->getZExtValue()))
517 return;
518
519 int64_t DstOff = 0, SrcOff = 0;
520 Value *DstBase =
521 GetPointerBaseWithConstantOffset(MT->getRawDest(), DstOff, DL);
522 Value *SrcBase =
523 GetPointerBaseWithConstantOffset(MT->getRawSource(), SrcOff, DL);
524 if (!DstBase || !SrcBase) {
525 DEBUG(Logger::logs("proteus-pass")
526 << " [PTR use analysis]: Failure due to nullptr dst/src " << "\n");
527 AnalysisFailed = true;
528 return;
529 }
530
531 DEBUG(Logger::logs("proteus-pass")
532 << " [PTR use analysis]: Completed instrinsic analysis " << "\n");
533 // LambdaArgVisitor applies Result.Offset by subtracting it from its
534 // current, destination-relative offset. The value carried across a
535 // transfer is therefore the difference between the destination and
536 // source bases, rather than the source's absolute offset. For example,
537 // copying a field at byte 8 into a field at byte 24 needs a correction of
538 // 16, so the caller turns 24 into 8.
539 int64_t OffsetCorrection = DstOff - SrcOff;
540 AnalysisSuccess = true;
541 AnalysisFailed = false;
542 Result = {.DominatingWrite = SrcBase,
543 .Offset = OffsetCorrection,
544 .ChangedRCLayout = std::nullopt};
545 }
546
547 void visitInstruction(Instruction &I) {
548 DEBUG(Logger::logs("proteus-pass")
549 << " [PTR use analysis]: Unhandled instruction "
550 << I.getOpcodeName() << ": " << I << "\n");
551 AnalysisFailed = true;
552 AnalysisSuccess = false;
553 }
554};
555
556inline std::optional<LambdaPtrUseAnalysis>
557getDominatingUse(const DataLayout &DL, Value *ValueNeedingAnalysis,
558 Value *SeenUse, int64_t TargetOffset,
559 CallBase *LambdaCB = nullptr) {
560 DEBUG(Logger::logs("proteus-pass")
561 << "Beginning PtrUse analysis with offset = " << TargetOffset << "\n");
562
563 if (auto Store = getPreviousCoveringStore(DL, ValueNeedingAnalysis, SeenUse,
564 TargetOffset))
565 return Store;
566
567 LambdaInstUseVisitor Visitor(ValueNeedingAnalysis, SeenUse, LambdaCB, DL,
568 TargetOffset);
569 // Analysis loop
570 while (!Visitor.empty() && !Visitor.success() && !Visitor.failed()) {
571 auto *V = Visitor.popBack().CurVal;
572 // Prevent loops/infinite recursion
573 if (Visitor.seen(V))
574 continue;
575 Visitor.markAsSeen(V);
576 DEBUG(Logger::logs("proteus-pass")
577 << " [PTR use analysis]: Visiting ptr use " << *V << "\n");
578 // Analyze the instruction
579 if (auto *I = dyn_cast<Instruction>(V))
580 Visitor.visit(*I);
581 else
582 continue;
583 }
584 if (!Visitor.success() || Visitor.failed()) {
585 DEBUG(
586 Logger::logs("proteus-pass")
587 << " [PTR use analysis] [WARNING]: Dominating use analysis FAILED for "
588 << *ValueNeedingAnalysis << " <-- " << *SeenUse << "\n");
589 return std::nullopt;
590 }
592 if (!Info.DominatingWrite)
593 return std::nullopt;
594 DEBUG(Logger::logs("proteus-pass")
595 << " [PTR USE ANALYSIS]: Computed offset " << Info.Offset << "\n");
596 return Info;
597
598 return std::nullopt;
599}
600} // namespace proteus
601
602#endif
uint32_t int32_t Type
Definition CompilerInterfaceDevice.cpp:98
uint64_t uint32_t uint32_t int64_t Offset
Definition CompilerInterfaceDevice.cpp:75
#define DEBUG(x)
Definition Helpers.h:14
Definition KernelArgPtrUseVisitor.h:169
LambdaInstUseVisitor(Value *PtrBegin, Value *SeenUse, CallBase *LambdaCB, const DataLayout &Dl, int64_t TargetOff)
Definition KernelArgPtrUseVisitor.h:197
void visitStoreInst(StoreInst &SI)
Definition KernelArgPtrUseVisitor.h:259
void visitInstruction(Instruction &I)
Definition KernelArgPtrUseVisitor.h:547
void visitGetElementPtrInst(GetElementPtrInst &GEP)
Definition KernelArgPtrUseVisitor.h:369
bool success()
Definition KernelArgPtrUseVisitor.h:221
void visitLoadInst(LoadInst &LI)
Definition KernelArgPtrUseVisitor.h:298
void markAsSeen(Value *Val)
Definition KernelArgPtrUseVisitor.h:219
auto popBack()
Definition KernelArgPtrUseVisitor.h:211
void offsetValueMapFailure(Value *V)
Definition KernelArgPtrUseVisitor.h:231
bool seen(Value *Val)
Definition KernelArgPtrUseVisitor.h:218
void visitBitCastInst(BitCastInst &BC)
Definition KernelArgPtrUseVisitor.h:441
bool failed()
Definition KernelArgPtrUseVisitor.h:222
auto getLastDef()
Definition KernelArgPtrUseVisitor.h:217
void visitCallBase(CallBase &CB)
Definition KernelArgPtrUseVisitor.h:315
auto back()
Definition KernelArgPtrUseVisitor.h:210
void pushBack(Value *NextVal, Value *CurVal)
Definition KernelArgPtrUseVisitor.h:227
bool empty()
Definition KernelArgPtrUseVisitor.h:220
void pushPointerUsers(Value *V)
Definition KernelArgPtrUseVisitor.h:243
void visitMemIntrinsic(MemIntrinsic &I)
Definition KernelArgPtrUseVisitor.h:469
void visitAllocaInst(AllocaInst &Alloca)
Definition KernelArgPtrUseVisitor.h:416
auto getAnalysisResult()
Definition KernelArgPtrUseVisitor.h:224
void visitAddrSpaceCastInst(AddrSpaceCastInst &ASC)
Definition KernelArgPtrUseVisitor.h:451
static llvm::raw_ostream & logs(const std::string &Name)
Definition Logger.h:19
Definition CompiledLibrary.h:8
Definition MemoryCache.h:27
std::optional< LambdaPtrUseAnalysis > getDominatingUse(const DataLayout &DL, Value *ValueNeedingAnalysis, Value *SeenUse, int64_t TargetOffset, CallBase *LambdaCB=nullptr)
Definition KernelArgPtrUseVisitor.h:557
bool offsetCoveredByRange(int64_t TargetOffset, int64_t RangeOffset, uint64_t RangeSize)
Definition KernelArgPtrUseVisitor.h:48
std::optional< MemoryLocation > getTrackedPointerLocation(const DataLayout &DL, Value *Ptr)
Definition KernelArgPtrUseVisitor.h:88
bool needsDefUseAnalysis(Value *Val)
Definition KernelArgPtrUseVisitor.h:43
std::optional< LambdaPtrUseAnalysis > getPreviousCoveringStore(const DataLayout &DL, Value *Ptr, Value *UseBoundary, int64_t TargetOffset)
Definition KernelArgPtrUseVisitor.h:109
std::optional< uint64_t > getTypeStoreSize(const DataLayout &DL, Type *Ty)
Definition KernelArgPtrUseVisitor.h:60
Definition KernelArgPtrUseVisitor.h:75
Value * ValEnteringCallBase
Definition KernelArgPtrUseVisitor.h:76
CallBase * CallerCB
Definition KernelArgPtrUseVisitor.h:77
Function * Callee
Definition KernelArgPtrUseVisitor.h:78
Definition KernelArgPtrUseVisitor.h:67
Value * DominatingWrite
Definition KernelArgPtrUseVisitor.h:68
std::optional< RuntimeConstantType > ChangedRCLayout
Definition KernelArgPtrUseVisitor.h:72
int64_t Offset
Definition KernelArgPtrUseVisitor.h:69
Definition KernelArgPtrUseVisitor.h:82
Value * LastVal
Definition KernelArgPtrUseVisitor.h:84
Value * CurVal
Definition KernelArgPtrUseVisitor.h:83