Enzyme main
Loading...
Searching...
No Matches
DifferentialUseAnalysis.h
Go to the documentation of this file.
1//===- DifferentialUseAnalysis.h - Determine values needed in reverse pass-===//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file contains the declaration of Differential USe Analysis -- an
22// AD-specific analysis that deduces if a given value is needed in the reverse
23// pass.
24//
25//===----------------------------------------------------------------------===//
26
27#ifndef ENZYME_DIFFERENTIALUSEANALYSIS_H_
28#define ENZYME_DIFFERENTIALUSEANALYSIS_H_
29
30#include <deque>
31#include <map>
32#include <set>
33
34#include "llvm/IR/BasicBlock.h"
35#include "llvm/IR/Instruction.h"
36
37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/SetVector.h"
39#include "llvm/ADT/SmallPtrSet.h"
40#include "llvm/ADT/SmallVector.h"
41
42#include "llvm/Support/Casting.h"
43#include "llvm/Support/ErrorHandling.h"
44
45#include "DiffeGradientUtils.h"
46#include "GradientUtils.h"
47#include "LibraryFuncs.h"
48
49extern "C" {
50extern llvm::cl::opt<bool> EnzymePrintDiffUse;
51}
52
53extern llvm::StringMap<
54 std::function<bool(const llvm::CallInst *, const GradientUtils *,
55 const llvm::Value *, bool, DerivativeMode, bool &)>>
57
58static inline std::string to_string(QueryType mode) {
59 switch (mode) {
61 return "Primal";
63 return "Shadow";
65 return "ShadowByConstPrimal";
66 }
67 llvm_unreachable("illegal QueryType");
68}
69
71
72/// Determine if a value is needed directly to compute the adjoint
73/// of the given instruction user. `shadow` denotes whether we are considering
74/// the shadow of the value (shadow=true) or the primal of the value
75/// (shadow=false).
76/// Recursive use is only usable in shadow mode.
78 const GradientUtils *gutils, const llvm::Value *val, DerivativeMode mode,
79 const llvm::Instruction *user,
80 const llvm::SmallPtrSetImpl<llvm::BasicBlock *> &oldUnreachable,
81 QueryType shadow, bool *recursiveUse = nullptr);
82
83bool checkLoopyReductionPHI(const GradientUtils *gutils,
84 const llvm::PHINode *P0,
85 const llvm::Value *incomingVal);
86
87void pushLoopyPHIPreheader(const GradientUtils *gutils, llvm::Value *V,
88 llvm::SetVector<llvm::Value *> &Intermediates,
89 std::deque<llvm::Value *> &todo);
90
91template <QueryType VT, bool OneLevel = false>
93 const GradientUtils *gutils, const llvm::Value *inst, DerivativeMode mode,
94 std::map<UsageKey, bool> &seen,
95 const llvm::SmallPtrSetImpl<llvm::BasicBlock *> &oldUnreachable) {
96 using namespace llvm;
97
98 TypeResults const &TR = gutils->TR;
99 static_assert(VT == QueryType::Primal || VT == QueryType::Shadow ||
101 auto idx = UsageKey(inst, VT);
102 if (seen.find(idx) != seen.end())
103 return seen[idx];
104 if (auto ainst = dyn_cast<Instruction>(inst)) {
105 assert(ainst->getParent()->getParent() == gutils->oldFunc);
106 }
107
108 // Inductively claim we aren't needed (and try to find contradiction)
109 seen[idx] = false;
110
111 if (VT == QueryType::Primal) {
112 if (auto op = dyn_cast<BinaryOperator>(inst)) {
113 if (op->getOpcode() == Instruction::FDiv) {
114 if (!gutils->isConstantValue(const_cast<Value *>(inst)) &&
115 !gutils->isConstantValue(op->getOperand(1))) {
117 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
118 << " in reverse as is active div\n";
119 return seen[idx] = true;
120 }
121 }
122 }
123 if (gutils->mode == DerivativeMode::ForwardModeError &&
124 !gutils->isConstantValue(const_cast<Value *>(inst))) {
126 llvm::errs()
127 << " Need: " << to_string(VT) << " of " << *inst
128 << " in reverse as forward mode error always needs result\n";
129 return seen[idx] = true;
130 }
131 }
132
133 if (auto CI = dyn_cast<CallInst>(inst)) {
134 StringRef funcName = getFuncNameFromCall(const_cast<CallInst *>(CI));
135 if (funcName == "julia.get_pgcstack" || funcName == "julia.ptls_states")
136 return seen[idx] = true;
137 }
138
139 bool inst_cv = gutils->isConstantValue(const_cast<Value *>(inst));
140
141 // Consider all users of this value, do any of them need this in the reverse?
142 for (auto use : inst->users()) {
143 if (use == inst)
144 continue;
145
146 const Instruction *user = dyn_cast<Instruction>(use);
147
148 // A shadow value is only needed in reverse if it or one of its descendants
149 // is used in an active instruction.
150 // If inst is a constant value, the primal may be used in its place and
151 // thus required.
153 inst_cv) {
154 bool recursiveUse = false;
156 gutils, inst, mode, user, oldUnreachable,
159 &recursiveUse)) {
160 return seen[idx] = true;
161 }
162
163 if (recursiveUse && !OneLevel) {
164 bool val;
165 if (VT == QueryType::Shadow)
167 gutils, user, mode, seen, oldUnreachable);
168 else
170 gutils, user, mode, seen, oldUnreachable);
171 if (val) {
173 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
174 << " in reverse as shadow sub-need " << *user << "\n";
175 return seen[idx] = true;
176 }
177 }
178
179 if (!TR.allFloat(const_cast<Value *>(inst)))
180 if (auto IVI = dyn_cast<Instruction>(user)) {
181 bool inserted = false;
182 if (auto II = dyn_cast<InsertValueInst>(IVI))
183 inserted = II->getInsertedValueOperand() == inst ||
184 II->getAggregateOperand() == inst;
185 if (auto II = dyn_cast<ExtractValueInst>(IVI))
186 inserted = II->getAggregateOperand() == inst;
187 if (auto II = dyn_cast<InsertElementInst>(IVI))
188 inserted = II->getOperand(1) == inst || II->getOperand(0) == inst;
189 if (auto II = dyn_cast<ExtractElementInst>(IVI))
190 inserted = II->getOperand(0) == inst;
191 if (inserted) {
192 SmallVector<const Instruction *, 1> todo;
193 todo.push_back(IVI);
194 while (todo.size()) {
195 auto cur = todo.pop_back_val();
196 for (auto u : cur->users()) {
197 if (auto IVI2 = dyn_cast<InsertValueInst>(u)) {
198 todo.push_back(IVI2);
199 continue;
200 }
201 if (auto IVI2 = dyn_cast<ExtractValueInst>(u)) {
202 todo.push_back(IVI2);
203 continue;
204 }
205 if (auto IVI2 = dyn_cast<InsertElementInst>(u)) {
206 todo.push_back(IVI2);
207 continue;
208 }
209 if (auto IVI2 = dyn_cast<ExtractElementInst>(u)) {
210 todo.push_back(IVI2);
211 continue;
212 }
213
214 bool partial = false;
215 if (auto UI = dyn_cast<Instruction>(u)) {
216 if (!gutils->isConstantValue(
217 const_cast<Instruction *>(cur))) {
218 bool recursiveUse = false;
220 gutils, cur, mode, UI, oldUnreachable,
221 QueryType::Shadow, &recursiveUse)) {
222 partial = true;
223 } else if (recursiveUse && !OneLevel) {
225 gutils, UI, mode, seen, oldUnreachable);
226 }
227 } else if (VT == QueryType::Shadow) {
228 bool recursiveUse = false;
230 gutils, cur, mode, UI, oldUnreachable,
231 QueryType::ShadowByConstPrimal, &recursiveUse)) {
232 partial = true;
233 } else if (recursiveUse && !OneLevel) {
235 QueryType::ShadowByConstPrimal>(gutils, UI, mode,
236 seen, oldUnreachable);
237 }
238 }
239 }
240
241 if (partial) {
242
244 llvm::errs()
245 << " Need (partial) direct " << to_string(VT) << " of "
246 << *inst << " in reverse from insertelem " << *user
247 << " via " << *cur << " in " << *u << "\n";
248 return seen[idx] = true;
249 }
250 }
251 }
252 }
253 }
254
255 if (VT != QueryType::Primal)
256 continue;
257 }
258
259 assert(VT == QueryType::Primal);
260
261 if (auto P0 = dyn_cast<PHINode>(user)) {
262 if (checkLoopyReductionPHI(gutils, P0, inst)) {
264 llvm::errs() << " Need: " << to_string(VT) << "(" << mode << ") of "
265 << *inst
266 << " in reverse as loopy reduction preheader of "
267 << *user << "\n";
268 return seen[idx] = true;
269 }
270 if (P0->getNumIncomingValues() == 1) {
271 for (auto u2 : P0->users()) {
272 if (auto P1 = dyn_cast<PHINode>(u2)) {
273 if (checkLoopyReductionPHI(gutils, P1, inst) ||
274 checkLoopyReductionPHI(gutils, P1, P0)) {
276 llvm::errs()
277 << " Need: " << to_string(VT) << "(" << mode << ") of "
278 << *inst
279 << " in reverse via LCSSA loopy reduction preheader of "
280 << *P1 << "\n";
281 return seen[idx] = true;
282 }
283 }
284 }
285 }
286 }
287
288 // If a sub user needs, we need
289 if (!OneLevel && is_value_needed_in_reverse<VT>(gutils, user, mode, seen,
290 oldUnreachable)) {
292 llvm::errs() << " Need: " << to_string(VT) << "(" << mode << ") of "
293 << *inst << " in reverse as sub-need " << *user << "\n";
294 return seen[idx] = true;
295 }
296
297 // Anything we may try to rematerialize requires its store operands for
298 // the reverse pass.
299 if (!OneLevel) {
300 bool isStored = false;
301 if (auto SI = dyn_cast<StoreInst>(user))
302 isStored = inst == SI->getValueOperand();
303 else if (auto MTI = dyn_cast<MemTransferInst>(user)) {
304 isStored = inst == MTI->getSource() || inst == MTI->getLength();
305 } else if (auto MS = dyn_cast<MemSetInst>(user)) {
306 isStored = inst == MS->getLength() || inst == MS->getValue();
307 } else if (auto CB = dyn_cast<CallBase>(user)) {
308 auto name = getFuncNameFromCall(CB);
309 if (name == "julia.write_barrier" ||
310 name == "julia.write_barrier_binding") {
311 auto sz = CB->arg_size();
312 // First pointer is the destination
313 for (size_t i = 1; i < sz; i++)
314 isStored |= inst == CB->getArgOperand(i);
315 }
316 }
317 if (isStored) {
318 for (auto pair : gutils->rematerializableAllocations) {
319 // If already decided to cache the whole allocation, ignore
320 if (gutils->needsCacheWholeAllocation(pair.first)) {
321 continue;
322 }
323
324 // If caching the outer allocation and have already set that this is
325 // not needed return early. This is necessary to avoid unnecessarily
326 // deciding stored values are needed if we have already decided to
327 // cache the whole allocation.
328 auto found = seen.find(std::make_pair(pair.first, QueryType::Primal));
329 if (found != seen.end() && !found->second) {
330 continue;
331 }
332
333 // Directly consider all the load uses to avoid an illegal inductive
334 // recurrence. Specifically if we're asking if the alloca is used,
335 // we'll set it to unused, then check the gep, then here we'll
336 // directly say unused by induction instead of checking the final
337 // loads.
338 if (pair.second.stores.count(user)) {
339 bool allocNeeded =
340 gutils->allocationsToBeRematerialized.count(pair.first);
341 if (allocNeeded) {
343 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
344 << " in reverse from rematerialized alloc "
345 << *pair.first << "\n";
346 return seen[idx] = true;
347 }
348 for (LoadInst *L : pair.second.loads)
349 if (is_value_needed_in_reverse<VT>(gutils, L, mode, seen,
350 oldUnreachable)) {
352 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
353 << " in reverse as rematload " << *L << "\n";
354 return seen[idx] = true;
355 }
356 for (auto &pair : pair.second.loadLikeCalls)
358 gutils, pair.operand, mode, pair.loadCall, oldUnreachable,
360 is_value_needed_in_reverse<VT>(gutils, pair.loadCall, mode,
361 seen, oldUnreachable)) {
363 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
364 << " in reverse as rematloadcall "
365 << *pair.loadCall << "\n";
366 return seen[idx] = true;
367 }
368
369 if (is_value_needed_in_reverse<VT>(gutils, pair.first, mode, seen,
370 oldUnreachable)) {
372 llvm::errs()
373 << " Need: " << to_string(VT) << " of " << *inst
374 << " in reverse as rematalloc " << *pair.first << "\n";
375 return seen[idx] = true;
376 }
377 }
378 }
379 }
380 }
381
382 // One may need to this value in the computation of loop
383 // bounds/comparisons/etc (which even though not active -- will be used for
384 // the reverse pass)
385 // We could potentially optimize this to avoid caching if in combined mode
386 // and the instruction dominates all returns
387 // otherwise it will use the local cache (rather than save for a separate
388 // backwards cache)
389 // We also don't need this if looking at the shadow rather than primal
390 {
391 // Proving that none of the uses (or uses' uses) are used in control flow
392 // allows us to safely not do this load
393
394 // TODO save loop bounds for dynamic loop
395
396 // TODO make this more aggressive and dont need to save loop latch
397 if (isAnyBranch(use) || isa<SwitchInst>(use)) {
398 size_t num = 0;
399 for (auto suc : successors(cast<Instruction>(use)->getParent())) {
400 if (!oldUnreachable.count(suc)) {
401 num++;
402 }
403 }
404 if (num <= 1)
405 continue;
407 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
408 << " in reverse as control-flow " << *user << "\n";
409 return seen[idx] = true;
410 }
411
412 if (auto CI = dyn_cast<CallInst>(use)) {
413 if (auto F = CI->getCalledFunction()) {
414 if (F->getName() == "__kmpc_for_static_init_4" ||
415 F->getName() == "__kmpc_for_static_init_4u" ||
416 F->getName() == "__kmpc_for_static_init_8" ||
417 F->getName() == "__kmpc_for_static_init_8u") {
419 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
420 << " in reverse as omp init " << *user << "\n";
421 return seen[idx] = true;
422 }
423 }
424 }
425 }
426
427 // The following are types we know we don't need to compute adjoints
428
429 // If a primal value is needed to compute a shadow pointer (e.g. int offset
430 // in gep), it needs preserving.
431 bool primalUsedInShadowPointer = true;
432 if (isa<CastInst>(user) || isa<LoadInst>(user))
433 primalUsedInShadowPointer = false;
434 if (auto CI = dyn_cast<CallInst>(user)) {
435 auto funcName = getFuncNameFromCall(CI);
436 if (funcName == "julia.pointer_from_objref") {
437 primalUsedInShadowPointer = false;
438 }
439 if (funcName == "julia.gc_loaded") {
440 primalUsedInShadowPointer = false;
441 }
442 if (funcName.contains("__enzyme_todense")) {
443 primalUsedInShadowPointer = false;
444 }
445 if (funcName.contains("__enzyme_ignore_derivatives")) {
446 primalUsedInShadowPointer = false;
447 }
448 }
449 if (auto GEP = dyn_cast<GetElementPtrInst>(user)) {
450 bool idxUsed = false;
451 for (auto &idx : GEP->indices()) {
452 if (idx.get() == inst)
453 idxUsed = true;
454 }
455 if (!idxUsed)
456 primalUsedInShadowPointer = false;
457 }
458 if (auto II = dyn_cast<IntrinsicInst>(user)) {
459 if (isIntelSubscriptIntrinsic(*II)) {
460 const std::array<size_t, 4> idxArgsIndices{{0, 1, 2, 4}};
461 bool idxUsed = false;
462 for (auto i : idxArgsIndices) {
463 if (II->getOperand(i) == inst)
464 idxUsed = true;
465 }
466 if (!idxUsed)
467 primalUsedInShadowPointer = false;
468 }
469 }
470 // No need for insert/extractvalue since indices are unsigned
471 // not llvm runtime values
472 if (isa<InsertValueInst>(user) || isa<ExtractValueInst>(user))
473 primalUsedInShadowPointer = false;
474
475 if (primalUsedInShadowPointer)
476 if (!user->getType()->isVoidTy() &&
477 TR.anyPointer(const_cast<Instruction *>(user))) {
479 gutils, user, mode, seen, oldUnreachable)) {
481 llvm::errs() << " Need: " << to_string(VT) << " of " << *inst
482 << " in reverse as used to compute shadow ptr "
483 << *user << "\n";
484 return seen[idx] = true;
485 }
486 }
487
489 gutils, inst, mode, user, oldUnreachable, QueryType::Primal);
490 if (!direct)
491 continue;
492
493 if (inst->getType()->isTokenTy()) {
494 llvm::errs() << " need " << *inst << " via " << *user << "\n";
495 }
496 assert(!inst->getType()->isTokenTy());
497
498 return seen[idx] = true;
499 }
500 return false;
501}
502
503template <QueryType VT>
504static inline bool is_value_needed_in_reverse(
505 const GradientUtils *gutils, const llvm::Value *inst, DerivativeMode mode,
506 const llvm::SmallPtrSetImpl<llvm::BasicBlock *> &oldUnreachable) {
507 static_assert(VT == QueryType::Primal || VT == QueryType::Shadow);
508 std::map<UsageKey, bool> seen;
509 return is_value_needed_in_reverse<VT>(gutils, inst, mode, seen,
510 oldUnreachable);
511}
512
513struct Node {
514 llvm::Value *V;
516 Node(llvm::Value *V, bool outgoing) : V(V), outgoing(outgoing){};
517 bool operator<(const Node N) const {
518 if (V < N.V)
519 return true;
520 return !(N.V < V) && outgoing < N.outgoing;
521 }
522 void dump() {
523 if (V)
524 llvm::errs() << "[" << *V << ", " << (int)outgoing << "]\n";
525 else
526 llvm::errs() << "[" << V << ", " << (int)outgoing << "]\n";
527 }
528};
529
530using Graph = std::map<Node, std::set<Node>>;
531
532void dump(std::map<Node, std::set<Node>> &G);
533
534/* Returns true if there is a path from source 's' to sink 't' in
535 residual graph. Also fills parent[] to store the path */
536void bfs(const std::map<Node, std::set<Node>> &G,
537 const llvm::SetVector<llvm::Value *> &Recompute,
538 std::map<Node, Node> &parent);
539
540// Return 1 if next is better
541// 0 if equal
542// -1 if prev is better, or unknown
543int cmpLoopNest(llvm::Loop *prev, llvm::Loop *next);
544
545void minCut(const llvm::DataLayout &DL, llvm::LoopInfo &OrigLI,
546 const llvm::SetVector<llvm::Value *> &Recomputes,
547 const llvm::SetVector<llvm::Value *> &Intermediates,
548 llvm::SetVector<llvm::Value *> &Required,
549 llvm::SetVector<llvm::Value *> &MinReq, const GradientUtils *gutils,
550 llvm::TargetLibraryInfo &TLI);
551
552__attribute__((always_inline)) static inline void
553forEachDirectInsertUser(llvm::function_ref<void(llvm::Instruction *)> f,
554 const GradientUtils *gutils, llvm::Instruction *IVI,
555 llvm::Value *val, bool useCheck) {
556 using namespace llvm;
557 if (!gutils->isConstantValue(IVI))
558 return;
559 bool inserted = false;
560 if (auto II = dyn_cast<InsertValueInst>(IVI))
561 inserted = II->getInsertedValueOperand() == val ||
562 II->getAggregateOperand() == val;
563 if (auto II = dyn_cast<ExtractValueInst>(IVI))
564 inserted = II->getAggregateOperand() == val;
565 if (auto II = dyn_cast<InsertElementInst>(IVI))
566 inserted = II->getOperand(1) == val || II->getOperand(0) == val;
567 if (auto II = dyn_cast<ExtractElementInst>(IVI))
568 inserted = II->getOperand(0) == val;
569 if (inserted) {
570 SmallVector<Instruction *, 1> todo;
571 todo.push_back(IVI);
572 while (todo.size()) {
573 auto cur = todo.pop_back_val();
574 for (auto u : cur->users()) {
575 if (isa<InsertValueInst>(u) || isa<InsertElementInst>(u) ||
576 isa<ExtractValueInst>(u) || isa<ExtractElementInst>(u)) {
577 auto I2 = cast<Instruction>(u);
578 bool subCheck = useCheck;
579 if (!subCheck) {
581 gutils, I2, gutils->mode, gutils->notForAnalysis);
582 }
583 if (subCheck)
584 f(I2);
585 todo.push_back(I2);
586 continue;
587 }
588 }
589 }
590 }
591}
592
593__attribute__((always_inline)) static inline void
594forEachDifferentialUser(llvm::function_ref<void(llvm::Value *)> f,
595 const GradientUtils *gutils, llvm::Value *V,
596 bool useCheck = false) {
597 for (auto V2 : V->users()) {
598 if (auto Inst = llvm::dyn_cast<llvm::Instruction>(V2)) {
599 for (const auto &pair : gutils->rematerializableAllocations) {
600 if (pair.second.stores.count(Inst)) {
601 f(llvm::cast<llvm::Instruction>(pair.first));
602 }
603 }
604 f(Inst);
605 forEachDirectInsertUser(f, gutils, Inst, V, useCheck);
606 }
607 }
608}
609
610//! Return whether or not this is a constant and should use reverse pass
612 llvm::CallBase &orig, QueryType qtype,
613 const llvm::Value *val);
614
615}; // namespace DifferentialUseAnalysis
616
617#endif
static std::string to_string(QueryType mode)
llvm::StringMap< std::function< bool(const llvm::CallInst *, const GradientUtils *, const llvm::Value *, bool, DerivativeMode, bool &)> > customDiffUseHandlers
llvm::cl::opt< bool > EnzymePrintDiffUse
constexpr const char * to_string(ActivityAnalyzer::UseActivity UA)
QueryType
Classification of what type of use is requested.
@ ShadowByConstPrimal
std::pair< const llvm::Value *, QueryType > UsageKey
static bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II)
Definition Utils.h:1649
static bool isAnyBranch(const llvm::Value *V)
Definition Utils.h:57
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
Definition Utils.h:1470
DerivativeMode
Definition Utils.h:524
DerivativeMode mode
TypeResults TR
llvm::SmallPtrSet< llvm::Value *, 4 > allocationsToBeRematerialized
llvm::ValueMap< llvm::Value *, Rematerializer > rematerializableAllocations
llvm::Function * oldFunc
bool isConstantValue(llvm::Value *val) const
bool needsCacheWholeAllocation(const llvm::Value *V) const
llvm::SmallPtrSet< llvm::BasicBlock *, 4 > notForAnalysis
A holder class representing the results of running TypeAnalysis on a given function.
bool anyPointer(llvm::Value *val) const
Whether any part of the top level register can contain a pointer e.g.
bool allFloat(llvm::Value *val) const
Whether all of the top level register is known to contain float data.
void minCut(const llvm::DataLayout &DL, llvm::LoopInfo &OrigLI, const llvm::SetVector< llvm::Value * > &Recomputes, const llvm::SetVector< llvm::Value * > &Intermediates, llvm::SetVector< llvm::Value * > &Required, llvm::SetVector< llvm::Value * > &MinReq, const GradientUtils *gutils, llvm::TargetLibraryInfo &TLI)
bool callShouldNotUseDerivative(const GradientUtils *gutils, llvm::CallBase &orig, QueryType qtype, const llvm::Value *val)
Return whether or not this is a constant and should use reverse pass.
bool is_value_needed_in_reverse(const GradientUtils *gutils, const llvm::Value *inst, DerivativeMode mode, std::map< UsageKey, bool > &seen, const llvm::SmallPtrSetImpl< llvm::BasicBlock * > &oldUnreachable)
void bfs(const std::map< Node, std::set< Node > > &G, const llvm::SetVector< llvm::Value * > &Recompute, std::map< Node, Node > &parent)
void dump(std::map< Node, std::set< Node > > &G)
void pushLoopyPHIPreheader(const GradientUtils *gutils, llvm::Value *V, llvm::SetVector< llvm::Value * > &Intermediates, std::deque< llvm::Value * > &todo)
bool is_use_directly_needed_in_reverse(const GradientUtils *gutils, const llvm::Value *val, DerivativeMode mode, const llvm::Instruction *user, const llvm::SmallPtrSetImpl< llvm::BasicBlock * > &oldUnreachable, QueryType shadow, bool *recursiveUse=nullptr)
Determine if a value is needed directly to compute the adjoint of the given instruction user.
std::map< Node, std::set< Node > > Graph
__attribute__((always_inline)) static inline void forEachDirectInsertUser(llvm
bool checkLoopyReductionPHI(const GradientUtils *gutils, const llvm::PHINode *P0, const llvm::Value *incomingVal)
int cmpLoopNest(llvm::Loop *prev, llvm::Loop *next)
Node(llvm::Value *V, bool outgoing)