Enzyme main
Loading...
Searching...
No Matches
Utils.h
Go to the documentation of this file.
1//===- Utils.h - Declaration of miscellaneous utilities -------------------===//
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// @misc{enzymeGithub,
11// author = {William S. Moses and Valentin Churavy},
12// title = {Enzyme: High Performance Automatic Differentiation of LLVM},
13// year = {2020},
14// howpublished = {\url{https://github.com/wsmoses/Enzyme}},
15// note = {commit xxxxxxx}
16// }
17//
18//===----------------------------------------------------------------------===//
19//
20// This file declares miscellaneous utilities that are used as part of the
21// AD process.
22//
23//===----------------------------------------------------------------------===//
24
25#ifndef ENZYME_UTILS_H
26#define ENZYME_UTILS_H
27
28#include "llvm/ADT/MapVector.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallPtrSet.h"
31
32#include "llvm/Analysis/AliasAnalysis.h"
33#include "llvm/Analysis/ValueTracking.h"
34#include "llvm/IR/Attributes.h"
35#include "llvm/IR/Function.h"
36#include "llvm/IR/IRBuilder.h"
37#include "llvm/IR/IntrinsicInst.h"
38#include "llvm/IR/Module.h"
39#include "llvm/IR/Operator.h"
40#include "llvm/IR/Type.h"
41
42#include "llvm/IR/Function.h"
43#include "llvm/IR/IntrinsicInst.h"
44
45#include "llvm/IR/ValueMap.h"
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/raw_ostream.h"
48
49#include "llvm/Support/CommandLine.h"
50
51#include "llvm/IR/Instructions.h"
52
53// LLVM 24 split BranchInst into CondBrInst and UncondBrInst. These helpers
54// ask the questions Enzyme asks of a branch in one spelling for every
55// supported LLVM; successors need no helper, Instruction::getSuccessor and
56// getNumSuccessors say them generically on both sides of the split.
57static inline bool isAnyBranch(const llvm::Value *V) {
58#if LLVM_VERSION_MAJOR >= 24
59 return llvm::isa<llvm::CondBrInst, llvm::UncondBrInst>(V);
60#else
61 return llvm::isa<llvm::BranchInst>(V);
62#endif
63}
64
65static inline bool isConditionalBranch(const llvm::Value *V) {
66#if LLVM_VERSION_MAJOR >= 24
67 return llvm::isa<llvm::CondBrInst>(V);
68#else
69 if (auto *BI = llvm::dyn_cast<llvm::BranchInst>(V))
70 return BI->isConditional();
71 return false;
72#endif
73}
74
75static inline bool isUnconditionalBranch(const llvm::Value *V) {
76#if LLVM_VERSION_MAJOR >= 24
77 return llvm::isa<llvm::UncondBrInst>(V);
78#else
79 if (auto *BI = llvm::dyn_cast<llvm::BranchInst>(V))
80 return BI->isUnconditional();
81 return false;
82#endif
83}
84
85// LLVM 23 persists every function's GUID as `!guid` metadata and
86// CloneFunctionInto copies it, so a derivative clone would share its primal's
87// GUID. LTO keys per-function state by GUID and then mishandles the pair; a
88// definition that is reachable only through the clone (e.g. a registered
89// custom rule) is dropped to a declaration and the link fails. Give a fresh
90// clone a GUID derived from its own name. No-op before LLVM 23.
91static inline void resetClonedGUID(llvm::Function *F) {
92#if LLVM_VERSION_MAJOR >= 23
93 F->reassignGUID();
94#else
95 (void)F;
96#endif
97}
98
99/// The condition of a branch isConditionalBranch says yes to.
100static inline llvm::Value *getBranchCondition(llvm::Value *V) {
101#if LLVM_VERSION_MAJOR >= 24
102 return llvm::cast<llvm::CondBrInst>(V)->getCondition();
103#else
104 return llvm::cast<llvm::BranchInst>(V)->getCondition();
105#endif
106}
107
108static inline void setBranchCondition(llvm::Value *V, llvm::Value *Cond) {
109#if LLVM_VERSION_MAJOR >= 24
110 llvm::cast<llvm::CondBrInst>(V)->setCondition(Cond);
111#else
112 llvm::cast<llvm::BranchInst>(V)->setCondition(Cond);
113#endif
114}
115
116#if LLVM_VERSION_MAJOR >= 24
117static inline llvm::Instruction *
118createUnconditionalBranch(llvm::BasicBlock *Target,
119 llvm::InsertPosition InsertBefore) {
120 return llvm::UncondBrInst::Create(Target, InsertBefore);
121}
122#else
123// llvm::InsertPosition arrives later than some supported majors; accept
124// whatever this LLVM's BranchInst::Create does.
125template <typename PosT>
126static inline llvm::Instruction *
127createUnconditionalBranch(llvm::BasicBlock *Target, PosT InsertBefore) {
128 return llvm::BranchInst::Create(Target, InsertBefore);
129}
130#endif
131
132#include "llvm/ADT/SetVector.h"
133#include "llvm/ADT/StringMap.h"
134
135#include "llvm/IR/Dominators.h"
136#include "llvm/IR/IntrinsicsAMDGPU.h"
137#include "llvm/IR/IntrinsicsNVPTX.h"
138
139#include <map>
140#include <set>
141
142#if LLVM_VERSION_MAJOR >= 16
143#include <optional>
144#endif
145
146#if LLVM_VERSION_MAJOR >= 23
147#define amd_target amdgpu
148#else
149#define amd_target amdgcn
150#endif
151
152#if LLVM_VERSION_MAJOR >= 16
153#include "llvm/TargetParser/Triple.h"
154#else
155#include "llvm/ADT/Triple.h"
156#endif
157
158#include "llvm/Support/AMDGPUMetadata.h"
159
160// Returns true if the given target triple is a GPU kernel architecture
161// (NVPTX, AMDGPU, or Apple Metal AIR).
162static inline bool isGPUArch(const llvm::Triple &TT) {
163 auto Arch = TT.getArch();
164 return Arch == llvm::Triple::nvptx || Arch == llvm::Triple::nvptx64 ||
165 Arch == llvm::Triple::amd_target || TT.getArchName() == "air64";
166}
167
168// Returns the address space used for GPU shared/threadgroup memory.
169static inline unsigned getGPUSharedAddrSpace(const llvm::Triple &TT) {
170 return TT.getArch() == llvm::Triple::amd_target
171 ? (unsigned)llvm::AMDGPU::HSAMD::AddressSpaceQualifier::Local
172 : 3;
173}
174
175#include "llvm/IR/DiagnosticInfo.h"
176
177#include "llvm/Analysis/OptimizationRemarkEmitter.h"
178
180
181class TypeResults;
182
183namespace llvm {
184class ScalarEvolution;
185class SCEV;
186class SCEVAddRecExpr;
187} // namespace llvm
188
189enum class ErrorType {
190 NoDerivative = 0,
191 NoShadow = 1,
193 NoType = 3,
195 InternalError = 5,
199 GetIndexError = 9,
200 NoTruncate = 10,
201 GCRewrite = 11,
202 NaNError = 12,
204 NoAccumulate = 13,
205};
206
207extern "C" {
208/// Print additional debug info relevant to performance
209extern llvm::cl::opt<bool> EnzymePrintPerf;
210extern llvm::cl::opt<bool> EnzymeNonPower2Cache;
211extern llvm::cl::opt<bool> EnzymeBlasCopy;
212extern llvm::cl::opt<bool> EnzymeLapackCopy;
213extern llvm::cl::opt<bool> EnzymeJuliaAddrLoad;
214extern LLVMValueRef (*CustomErrorHandler)(const char *, LLVMValueRef, ErrorType,
215 const void *, LLVMValueRef,
216 LLVMBuilderRef);
217}
218
219llvm::SmallVector<llvm::Instruction *, 2> PostCacheStore(llvm::StoreInst *SI,
220 llvm::IRBuilder<> &B);
221
222llvm::Value *CreateAllocation(llvm::IRBuilder<> &B, llvm::Type *T,
223 llvm::Value *Count, const llvm::Twine &Name = "",
224 llvm::CallInst **caller = nullptr,
225 llvm::Instruction **ZeroMem = nullptr,
226 bool isDefault = false);
227llvm::CallInst *CreateDealloc(llvm::IRBuilder<> &B, llvm::Value *ToFree);
228void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj,
229 bool isTape);
230
231llvm::Value *CreateReAllocation(llvm::IRBuilder<> &B, llvm::Value *prev,
232 llvm::Type *T, llvm::Value *OuterCount,
233 llvm::Value *InnerCount,
234 const llvm::Twine &Name = "",
235 llvm::CallInst **caller = nullptr,
236 bool ZeroMem = false);
237
238llvm::PointerType *getDefaultAnonymousTapeType(llvm::LLVMContext &C);
239
240class GradientUtils;
241extern llvm::StringMap<std::function<llvm::Value *(
242 llvm::IRBuilder<> &, llvm::CallInst *, llvm::ArrayRef<llvm::Value *>,
243 GradientUtils *)>>
245
246static inline bool EmitWarningEnabled(const llvm::LLVMContext &Ctx) {
247 return EnzymePrintPerf ||
248 Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled("enzyme");
249}
250
251template <typename... Args>
252void EmitWarning(llvm::StringRef RemarkName,
253 const llvm::DiagnosticLocation &Loc,
254 const llvm::BasicBlock *BB, const Args &...args) {
255
256 llvm::LLVMContext &Ctx = BB->getContext();
257 if (Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled("enzyme")) {
258 std::string str;
259 llvm::raw_string_ostream ss(str);
260 (ss << ... << args);
261 auto R = llvm::OptimizationRemark("enzyme", RemarkName, Loc, BB)
262 << ss.str();
263 Ctx.diagnose(R);
264 }
265
266 if (EnzymePrintPerf)
267 (llvm::errs() << ... << args) << "\n";
268}
269
270template <typename... Args>
271void EmitWarning(llvm::StringRef RemarkName, const llvm::Instruction &I,
272 const Args &...args) {
273 EmitWarning(RemarkName, I.getDebugLoc(), I.getParent(), args...);
274}
275
276class EnzymeWarning final : public llvm::DiagnosticInfoUnsupported {
277public:
278 EnzymeWarning(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc,
279 const llvm::Instruction *CodeRegion);
280 EnzymeWarning(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc,
281 const llvm::Function *CodeRegion);
282};
283
284/// Emit a diagnostic which is always shown, unlike EmitWarning which requires
285/// the user to opt in with -Rpass=enzyme. Use this for messages which describe
286/// a potential correctness problem (e.g. mismatched activity) as opposed to a
287/// performance note.
288template <typename... Args>
289void EmitWarningAlways(llvm::StringRef RemarkName,
290 const llvm::DiagnosticLocation &Loc,
291 const llvm::Function &F, const Args &...args) {
292 // DiagnosticInfoUnsupported does not copy its message, so keep the backing
293 // storage alive until diagnose() returns.
294 std::string str = "Enzyme: ";
295 llvm::raw_string_ostream ss(str);
296 (ss << ... << args);
297 F.getContext().diagnose(EnzymeWarning(str, Loc, &F));
298}
299
300template <typename... Args>
301void EmitWarningAlways(llvm::StringRef RemarkName, const llvm::Instruction &I,
302 const Args &...args) {
303 EmitWarningAlways(RemarkName, I.getDebugLoc(), *I.getParent()->getParent(),
304 args...);
305}
306
307template <typename... Args>
308void EmitWarningAlways(llvm::StringRef RemarkName, const llvm::Function &F,
309 const Args &...args) {
310 EmitWarningAlways(RemarkName, F.getSubprogram(), F, args...);
311}
312
313/// Remedy appended to mismatched-activity warnings, which are emitted when a
314/// value Enzyme proved inactive is used where an active value may be required.
315static constexpr const char *MixedActivityHint =
316 ". If this value may be active at runtime, enable runtime activity "
317 "analysis (pass \"enzyme_runtime_activity\" to __enzyme_autodiff); "
318 "otherwise its derivative will be assumed zero.";
319
320template <typename... Args>
321void EmitWarning(llvm::StringRef RemarkName, const llvm::Function &F,
322 const Args &...args) {
323 llvm::LLVMContext &Ctx = F.getContext();
324 if (Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled("enzyme")) {
325 std::string str;
326 llvm::raw_string_ostream ss(str);
327 (ss << ... << args);
328 auto R = llvm::OptimizationRemark("enzyme", RemarkName, &F) << ss.str();
329 Ctx.diagnose(R);
330 }
331 if (EnzymePrintPerf)
332 (llvm::errs() << ... << args) << "\n";
333}
334
335class EnzymeFailure final : public llvm::DiagnosticInfoUnsupported {
336public:
337 EnzymeFailure(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc,
338 const llvm::Instruction *CodeRegion);
339 EnzymeFailure(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc,
340 const llvm::Function *CodeRegion);
341};
342
343// Forward declaration needed for EmitFailure template
344llvm::Function *getFirstFunctionDefinition(llvm::Module &M);
345
346template <typename... Args>
347void EmitFailure(llvm::StringRef RemarkName,
348 const llvm::DiagnosticLocation &Loc,
349 const llvm::Instruction *CodeRegion, Args &...args) {
350 std::string *str = new std::string();
351 llvm::raw_string_ostream ss(*str);
352 (ss << ... << args);
353 CodeRegion->getContext().diagnose(
354 (EnzymeFailure("Enzyme: " + ss.str(), Loc, CodeRegion)));
355}
356
357template <typename... Args>
358void EmitFailure(llvm::StringRef RemarkName,
359 const llvm::DiagnosticLocation &Loc,
360 const llvm::Function *CodeRegion, Args &...args) {
361 std::string *str = new std::string();
362 llvm::raw_string_ostream ss(*str);
363 (ss << ... << args);
364 CodeRegion->getContext().diagnose(
365 (EnzymeFailure("Enzyme: " + ss.str(), Loc, CodeRegion)));
366}
367
368template <typename... Args>
369void EmitFailure(llvm::StringRef RemarkName, llvm::Module &M, Args &...args) {
370 // Use the first function definition in the module as context for the
371 // diagnostic
372 if (llvm::Function *FirstFunc = getFirstFunctionDefinition(M)) {
373 EmitFailure(RemarkName, FirstFunc->getSubprogram(), FirstFunc, args...);
374 } else {
375 // Fallback if no functions in module
376 std::string *str = new std::string();
377 llvm::raw_string_ostream ss(*str);
378 (ss << ... << args);
379 llvm::report_fatal_error(llvm::StringRef(*str));
380 }
381}
382
383static inline llvm::Function *isCalledFunction(llvm::Value *val) {
384 if (llvm::CallInst *CI = llvm::dyn_cast<llvm::CallInst>(val)) {
385 return CI->getCalledFunction();
386 }
387 return nullptr;
388}
389
390class GradientUtils;
391struct RequestContext;
392llvm::Value *EmitNoDerivativeError(const std::string &message,
393 llvm::Instruction &inst,
394 GradientUtils *gutils, llvm::IRBuilder<> &B,
395 llvm::Value *condition = nullptr);
396bool EmitNoDerivativeError(const std::string &message, llvm::Value *todiff,
397 RequestContext &ctx);
398
399void EmitNoTypeError(const std::string &, llvm::Instruction &inst,
400 GradientUtils *gutils, llvm::IRBuilder<> &B);
401
402/// Get LLVM fast math flags
403llvm::FastMathFlags getFast();
404
405/// Pick the maximum value
406template <typename T> static inline T max(T a, T b) {
407 if (a > b)
408 return a;
409 return b;
410}
411/// Pick the maximum value
412template <typename T> static inline T min(T a, T b) {
413 if (a < b)
414 return a;
415 return b;
416}
417
418/// Output a set as a string
419template <typename T>
420static inline std::string to_string(const std::set<T> &us) {
421 std::string s = "{";
422 for (const auto &y : us)
423 s += std::to_string(y) + ",";
424 return s + "}";
425}
426
427/// Print a map, optionally with a shouldPrint function
428/// to decide to print a given value
429template <typename T, typename N>
430static inline void dumpMap(
431 const llvm::ValueMap<T, N> &o,
432 llvm::function_ref<bool(const llvm::Value *)> shouldPrint = [](T) {
433 return true;
434 }) {
435 llvm::errs() << "<begin dump>\n";
436 for (auto a : o) {
437 if (shouldPrint(a.first))
438 llvm::errs() << "key=" << *a.first << " val=" << *a.second << "\n";
439 }
440 llvm::errs() << "</end dump>\n";
441}
442
443/// Print a set
444template <typename T>
445static inline void dumpSet(const llvm::SmallPtrSetImpl<T *> &o) {
446 llvm::errs() << "<begin dump>\n";
447 for (auto a : o)
448 llvm::errs() << *a << "\n";
449 llvm::errs() << "</end dump>\n";
450}
451
452template <typename T>
453static inline void dumpSet(const llvm::SetVector<T *> &o) {
454 llvm::errs() << "<begin dump>\n";
455 for (auto a : o)
456 llvm::errs() << *a << "\n";
457 llvm::errs() << "</end dump>\n";
458}
459
460/// Get the next non-debug instruction, if one exists
461static inline llvm::Instruction *
462getNextNonDebugInstructionOrNull(llvm::Instruction *Z) {
463 for (llvm::Instruction *I = Z->getNextNode(); I; I = I->getNextNode())
464 if (!llvm::isa<llvm::DbgInfoIntrinsic>(I))
465 return I;
466 return nullptr;
467}
468
469/// Get the next non-debug instruction, erring if none exists
470static inline llvm::Instruction *
471getNextNonDebugInstruction(llvm::Instruction *Z) {
473 if (z)
474 return z;
475 llvm::errs() << *Z->getParent() << "\n";
476 llvm::errs() << *Z << "\n";
477 llvm_unreachable("No valid subsequent non debug instruction");
478 exit(1);
479 return nullptr;
480}
481
482/// Check if a global has metadata
483static inline llvm::MDNode *hasMetadata(const llvm::GlobalObject *O,
484 llvm::StringRef kind) {
485 return O->getMetadata(kind);
486}
487
488/// Check if an instruction has metadata
489static inline llvm::MDNode *hasMetadata(const llvm::Instruction *O,
490 llvm::StringRef kind) {
491 return O->getMetadata(kind);
492}
493static inline llvm::MDNode *hasMetadata(const llvm::Instruction *O,
494 unsigned kind) {
495 return O->getMetadata(kind);
496}
497
498/// Potential return type of generated functions
499enum class ReturnType {
500 /// Return is a struct of all args and the original return
502 /// Return is a struct of all args and two of the original return
504 /// Return is a struct of all args
505 Args,
506 /// Return is a tape type and the original return
508 /// Return is a tape type and the two of the original return
510 /// Return is a tape type
511 Tape,
513 Return,
514 Void,
515};
516
517#include "EnzymeCallMarkers.h"
518
519enum class BATCH_TYPE {
520 SCALAR = 0,
521 VECTOR = 1,
522};
523
524enum class DerivativeMode {
525 ForwardMode = 0,
531};
532
533enum class ProbProgMode {
534 Likelihood = 0,
535 Trace = 1,
536 Condition = 2,
537};
538
539/// Classification of value as an original program
540/// variable, a derivative variable, neither, or both.
541/// This type is used both in differential use analysis
542/// and to describe argument bundles.
543enum class ValueType {
544 // A value that is neither a value in the original
545 // program, nor the derivative.
546 None = 0,
547 // The original program value
548 Primal = 1,
549 // The derivative value
550 Shadow = 2,
551 // Both the original program value and the shadow.
552 Both = Primal | Shadow,
553};
554
555static inline std::string to_string(ValueType mode) {
556 switch (mode) {
557 case ValueType::None:
558 return "None";
560 return "Primal";
562 return "Shadow";
563 case ValueType::Both:
564 return "Both";
565 }
566 llvm_unreachable("illegal valuetype");
567}
568
569static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
570 ValueType mode) {
571 return os << to_string(mode);
572}
573
574static inline std::string to_string(DerivativeMode mode) {
575 switch (mode) {
577 return "ForwardMode";
579 return "ForwardModeError";
581 return "ForwardModeSplit";
583 return "ReverseModePrimal";
585 return "ReverseModeGradient";
587 return "ReverseModeCombined";
588 }
589 llvm_unreachable("illegal derivative mode");
590}
591
592static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
593 DerivativeMode mode) {
594 return os << to_string(mode);
595}
596
597/// Convert DIFFE_TYPE to a string
598static inline std::string to_string(DIFFE_TYPE t) {
599 switch (t) {
601 return "OUT_DIFF";
603 return "CONSTANT";
605 return "DUP_ARG";
607 return "DUP_NONEED";
608 default:
609 assert(0 && "illegal diffetype");
610 return "";
611 }
612}
613
614static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
615 DIFFE_TYPE mode) {
616 return os << to_string(mode);
617}
618
619/// Convert ReturnType to a string
620static inline std::string to_string(ReturnType t) {
621 switch (t) {
623 return "ArgsWithReturn";
625 return "ArgsWithTwoReturns";
626 case ReturnType::Args:
627 return "Args";
629 return "TapeAndReturn";
631 return "TapeAndTwoReturns";
632 case ReturnType::Tape:
633 return "Tape";
635 return "TwoReturns";
637 return "Return";
638 case ReturnType::Void:
639 return "Void";
640 }
641 llvm_unreachable("illegal ReturnType");
642}
643
644static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
645 ReturnType mode) {
646 return os << to_string(mode);
647}
648
649#include <set>
650
651/// Attempt to automatically detect the differentiable
652/// classification based off of a given type
653static inline DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode,
654 bool integersAreConstant,
655 std::set<llvm::Type *> &seen) {
656 assert(arg);
657 if (seen.find(arg) != seen.end())
659 seen.insert(arg);
660
661 if (arg->isVoidTy() || arg->isEmptyTy()) {
663 }
664
665 if (arg->isPointerTy()) {
666#if LLVM_VERSION_MAJOR >= 17
667 return DIFFE_TYPE::DUP_ARG;
668#else
669#if LLVM_VERSION_MAJOR >= 15
670 if (!arg->getContext().supportsTypedPointers()) {
671 return DIFFE_TYPE::DUP_ARG;
672 }
673#elif LLVM_VERSION_MAJOR >= 13
674 if (arg->isOpaquePointerTy()) {
675 return DIFFE_TYPE::DUP_ARG;
676 }
677#endif
678 switch (whatType(arg->getPointerElementType(), mode, integersAreConstant,
679 seen)) {
681 return DIFFE_TYPE::DUP_ARG;
685 return DIFFE_TYPE::DUP_ARG;
687 llvm_unreachable("impossible case");
688 }
689 assert(arg);
690 llvm::errs() << "arg: " << *arg << "\n";
691 assert(0 && "Cannot handle type0");
693#endif
694 } else if (arg->isArrayTy()) {
695 return whatType(llvm::cast<llvm::ArrayType>(arg)->getElementType(), mode,
696 integersAreConstant, seen);
697 } else if (arg->isStructTy()) {
698 auto st = llvm::cast<llvm::StructType>(arg);
699 if (st->getNumElements() == 0)
701
702 auto ty = DIFFE_TYPE::CONSTANT;
703 for (unsigned i = 0; i < st->getNumElements(); ++i) {
704 auto midTy =
705 whatType(st->getElementType(i), mode, integersAreConstant, seen);
706 switch (midTy) {
708 switch (ty) {
712 break;
715 return ty;
717 llvm_unreachable("impossible case");
718 }
719 break;
721 switch (ty) {
724 break;
726 break;
729 return ty;
731 llvm_unreachable("impossible case");
732 }
733 break;
735 return DIFFE_TYPE::DUP_ARG;
737 llvm_unreachable("impossible case");
738 }
739 }
740 return ty;
741 } else if (arg->isIntOrIntVectorTy() || arg->isFunctionTy()) {
742 return integersAreConstant ? DIFFE_TYPE::CONSTANT : DIFFE_TYPE::DUP_ARG;
743 } else if (arg->isFPOrFPVectorTy()) {
744 return (mode == DerivativeMode::ForwardMode ||
749 } else {
750 assert(arg);
751 llvm::errs() << "arg: " << *arg << "\n";
752 assert(0 && "Cannot handle type");
754 }
755}
756
757llvm::Value *get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res);
758
759static inline DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode) {
760 std::set<llvm::Type *> seen;
761 return whatType(arg, mode, /*intconst*/ true, seen);
762}
763
764/// Check whether this instruction is returned
765static inline bool isReturned(llvm::Instruction *inst) {
766 for (const auto a : inst->users()) {
767 if (llvm::isa<llvm::ReturnInst>(a))
768 return true;
769 }
770 return false;
771}
772
773/// Convert a floating point type to an integer type
774/// of the same size
775static inline llvm::Type *FloatToIntTy(llvm::Type *T) {
776 assert(T->isFPOrFPVectorTy());
777 if (auto ty = llvm::dyn_cast<llvm::VectorType>(T)) {
778 return llvm::VectorType::get(FloatToIntTy(ty->getElementType()),
779 ty->getElementCount());
780 }
781 if (T->isHalfTy())
782 return llvm::IntegerType::get(T->getContext(), 16);
783 if (T->isBFloatTy())
784 return llvm::IntegerType::get(T->getContext(), 16);
785 if (T->isFloatTy())
786 return llvm::IntegerType::get(T->getContext(), 32);
787 if (T->isDoubleTy())
788 return llvm::IntegerType::get(T->getContext(), 64);
789 if (T->isX86_FP80Ty())
790 return llvm::IntegerType::get(T->getContext(), 80);
791 if (T->isFP128Ty())
792 return llvm::IntegerType::get(T->getContext(), 128);
793 assert(0 && "unknown floating point type");
794 return nullptr;
795}
796
797/// Convert a integer type to a floating point type
798/// of the same size
799static inline llvm::Type *IntToFloatTy(llvm::Type *T) {
800 assert(T->isIntOrIntVectorTy());
801 if (auto ty = llvm::dyn_cast<llvm::VectorType>(T)) {
802 return llvm::VectorType::get(IntToFloatTy(ty->getElementType()),
803 ty->getElementCount());
804 }
805 if (auto ty = llvm::dyn_cast<llvm::IntegerType>(T)) {
806 switch (ty->getBitWidth()) {
807 case 16:
808 return llvm::Type::getHalfTy(T->getContext());
809 // return llvm::Type::getBFloat16Ty(T->getContext());
810 case 32:
811 return llvm::Type::getFloatTy(T->getContext());
812 case 64:
813 return llvm::Type::getDoubleTy(T->getContext());
814 case 80:
815 return llvm::Type::getX86_FP80Ty(T->getContext());
816 case 128:
817 return llvm::Type::getFP128Ty(T->getContext());
818 }
819 }
820 assert(0 && "unknown int to floating point type");
821 return nullptr;
822}
823
824static inline bool isDebugFunction(llvm::Function *called) {
825 if (!called)
826 return false;
827 if (called->getName() == "llvm.enzyme.lifetime_start" ||
828 called->getName() == "llvm.enzyme.lifetime_end") {
829 return true;
830 }
831 switch (called->getIntrinsicID()) {
832 case llvm::Intrinsic::dbg_declare:
833 case llvm::Intrinsic::dbg_value:
834 case llvm::Intrinsic::dbg_label:
835#if LLVM_VERSION_MAJOR <= 16
836 case llvm::Intrinsic::dbg_addr:
837#endif
838 case llvm::Intrinsic::lifetime_start:
839 case llvm::Intrinsic::lifetime_end:
840 return true;
841 default:
842 break;
843 }
844 return false;
845}
846
847static inline bool startsWith(llvm::StringRef string, llvm::StringRef prefix) {
848#if LLVM_VERSION_MAJOR >= 18
849 return string.starts_with(prefix);
850#else
851 return string.startswith(prefix);
852#endif // LLVM_VERSION_MAJOR
853}
854
855static inline bool endsWith(llvm::StringRef string, llvm::StringRef suffix) {
856#if LLVM_VERSION_MAJOR >= 18
857 return string.ends_with(suffix);
858#else
859 return string.endswith(suffix);
860#endif // LLVM_VERSION_MAJOR
861}
862
863static inline bool isCertainPrint(const llvm::StringRef name) {
864 if (name == "printf" || name == "puts" || name == "fprintf" ||
865 name == "putchar" || name == "fputc" ||
866 startsWith(name,
867 "_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_") ||
868 startsWith(name, "_ZNSolsE") || startsWith(name, "_ZNSo9_M_insert") ||
869 startsWith(name, "_ZSt16__ostream_insert") ||
870 startsWith(name, "_ZNSo3put") || startsWith(name, "_ZSt4endl") ||
871 startsWith(name, "_ZN3std2io5stdio6_print") ||
872 startsWith(name, "_ZNSo5flushEv") || startsWith(name, "_ZN4core3fmt") ||
873 name == "vprintf")
874 return true;
875 return false;
876}
877
878struct BlasInfo {
879 std::string floatType;
880 std::string prefix;
881 std::string suffix;
882 std::string function;
883 bool is64;
884
885 llvm::Type *fpType(llvm::LLVMContext &ctx, bool to_scalar = false) const;
886 llvm::IntegerType *intType(llvm::LLVMContext &ctx) const;
887};
888
889#if LLVM_VERSION_MAJOR >= 16
890std::optional<BlasInfo> extractBLAS(llvm::StringRef in);
891#else
892llvm::Optional<BlasInfo> extractBLAS(llvm::StringRef in);
893#endif
894
895std::vector<std::tuple<llvm::Type *, size_t, size_t>>
896parseTrueType(const llvm::MDNode *, DerivativeMode, bool const_src);
897
898/// Restrict an `enzyme_truetype` annotation describing a memory region to the
899/// byte range [start, start + length), rebasing its offsets to that range.
900/// Returns nullptr if no type information covers the range.
901llvm::MDNode *sliceTrueType(const llvm::MDNode *md, size_t start,
902 size_t length);
903
904bool isAtomic(llvm::Value *origptr, bool AtomicAdd, llvm::Function *newFunc);
905
906/// Create function for type that performs the derivative memcpy on floating
907/// point memory
909 llvm::Module &M, llvm::Type *T, unsigned dstalign, unsigned srcalign,
910 unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity,
911 bool atomic);
912
913/// Create function for type that performs memcpy with a stride using blas copy
914void callMemcpyStridedBlas(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas,
915 llvm::ArrayRef<llvm::Value *> args,
916 llvm::Type *cublas_retty,
917 llvm::ArrayRef<llvm::OperandBundleDef> bundles);
918
919/// Create function for type that performs memcpy using lapack copy
920void callMemcpyStridedLapack(llvm::IRBuilder<> &B, llvm::Module &M,
921 BlasInfo blas, llvm::ArrayRef<llvm::Value *> args,
922 llvm::ArrayRef<llvm::OperandBundleDef> bundles);
923
924void callSPMVDiagUpdate(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas,
925 llvm::IntegerType *IT, llvm::Type *BlasCT,
926 llvm::Type *BlasFPT, llvm::Type *BlasPT,
927 llvm::Type *BlasIT, llvm::Type *fpTy,
928 llvm::ArrayRef<llvm::Value *> args,
929 const llvm::ArrayRef<llvm::OperandBundleDef> bundles,
930 bool byRef, bool julia_decl);
931
932llvm::CallInst *
933getorInsertInnerProd(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas,
934 llvm::IntegerType *IT, llvm::Type *BlasPT,
935 llvm::Type *BlasIT, llvm::Type *fpTy,
936 llvm::ArrayRef<llvm::Value *> args,
937 const llvm::ArrayRef<llvm::OperandBundleDef> bundles,
938 bool byRef, bool cublas, bool julia_decl);
939
940/// Create function for type that performs memcpy with a stride
941llvm::Function *getOrInsertMemcpyStrided(llvm::Module &M,
942 llvm::Type *elementType,
943 llvm::PointerType *T, llvm::Type *IT,
944 unsigned dstalign, unsigned srcalign);
945
946/// Turned out to be a faster alternatives to lapacks lacpy function
947llvm::Function *getOrInsertMemcpyMat(llvm::Module &M, llvm::Type *elementType,
948 llvm::PointerType *PT,
949 llvm::IntegerType *IT, unsigned dstalign,
950 unsigned srcalign);
951
953 llvm::Module &M, llvm::Type *elementType, llvm::PointerType *PT,
954 llvm::IntegerType *IT, llvm::IntegerType *CT, unsigned dstalign,
955 unsigned srcalign, bool zeroSrc);
956
957/// Create function for type that performs the derivative memmove on floating
958/// point memory
960 llvm::Module &M, llvm::Type *T, unsigned dstalign, unsigned srcalign,
961 unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity,
962 bool atomic);
963
964llvm::Function *getOrInsertCheckedFree(llvm::Module &M, llvm::CallInst *call,
965 llvm::Type *Type, unsigned width);
966
967/// Create function for type that performs the derivative MPI_Wait
968llvm::Function *getOrInsertDifferentialMPI_Wait(llvm::Module &M,
969 llvm::ArrayRef<llvm::Type *> T,
970 llvm::Type *reqType,
971 llvm::StringRef caller);
972
973/// Create function to computer nearest power of two
974llvm::Value *nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V);
975
976/// Insert into a map
977template <typename K, typename V>
978static inline typename std::map<K, V>::iterator
979insert_or_assign(std::map<K, V> &map, K &key, V &&val) {
980 auto found = map.find(key);
981 if (found != map.end()) {
982 map.erase(found);
983 }
984 return map.emplace(key, val).first;
985}
986
987/// Insert into a map
988template <typename K, typename V>
989static inline typename std::map<K, V>::iterator
990insert_or_assign2(std::map<K, V> &map, K key, V val) {
991 auto found = map.find(key);
992 if (found != map.end()) {
993 map.erase(found);
994 }
995 return map.emplace(key, val).first;
996}
997
998template <typename K, typename V>
999static inline V *findInMap(std::map<K, V> &map, K key) {
1000 auto found = map.find(key);
1001 if (found == map.end())
1002 return nullptr;
1003 V *val = &found->second;
1004 return val;
1005}
1006
1007#include "llvm/IR/CFG.h"
1008#include <deque>
1009#include <functional>
1010/// Call the function f for all instructions that happen after inst
1011/// If the function returns true, the iteration will early exit
1012static inline void
1013allFollowersOf(llvm::Instruction *inst,
1014 llvm::function_ref<bool(llvm::Instruction *)> f) {
1015
1016 for (auto uinst = inst->getNextNode(); uinst != nullptr;
1017 uinst = uinst->getNextNode()) {
1018 if (f(uinst))
1019 return;
1020 }
1021
1022 std::deque<llvm::BasicBlock *> todo;
1023 std::set<llvm::BasicBlock *> done;
1024 for (auto suc : llvm::successors(inst->getParent())) {
1025 todo.push_back(suc);
1026 }
1027 while (todo.size()) {
1028 auto BB = todo.front();
1029 todo.pop_front();
1030 if (done.count(BB))
1031 continue;
1032 done.insert(BB);
1033 for (auto &ni : *BB) {
1034 if (f(&ni))
1035 return;
1036 if (&ni == inst)
1037 break;
1038 }
1039 for (auto suc : llvm::successors(BB)) {
1040 todo.push_back(suc);
1041 }
1042 }
1043}
1044
1045/// Call the function f for all instructions that happen before inst
1046/// If the function returns true, the iteration will early exit
1047static inline void
1048allPredecessorsOf(llvm::Instruction *inst,
1049 llvm::function_ref<bool(llvm::Instruction *)> f) {
1050
1051 for (auto uinst = inst->getPrevNode(); uinst != nullptr;
1052 uinst = uinst->getPrevNode()) {
1053 if (f(uinst))
1054 return;
1055 }
1056
1057 std::deque<llvm::BasicBlock *> todo;
1058 std::set<llvm::BasicBlock *> done;
1059 for (auto suc : llvm::predecessors(inst->getParent())) {
1060 todo.push_back(suc);
1061 }
1062 while (todo.size()) {
1063 auto BB = todo.front();
1064 todo.pop_front();
1065 if (done.count(BB))
1066 continue;
1067 done.insert(BB);
1068
1069 llvm::BasicBlock::reverse_iterator I = BB->rbegin(), E = BB->rend();
1070 for (; I != E; ++I) {
1071 if (f(&*I))
1072 return;
1073 if (&*I == inst)
1074 break;
1075 }
1076 for (auto suc : llvm::predecessors(BB)) {
1077 todo.push_back(suc);
1078 }
1079 }
1080}
1081
1082/// Call the function f for all instructions that happen before inst
1083/// If the function returns true, the iteration will early exit
1084static inline void
1085allDomPredecessorsOf(llvm::Instruction *inst, llvm::DominatorTree &DT,
1086 llvm::function_ref<bool(llvm::Instruction *)> f) {
1087
1088 for (auto uinst = inst->getPrevNode(); uinst != nullptr;
1089 uinst = uinst->getPrevNode()) {
1090 if (f(uinst))
1091 return;
1092 }
1093
1094 std::deque<llvm::BasicBlock *> todo;
1095 std::set<llvm::BasicBlock *> done;
1096 for (auto suc : llvm::predecessors(inst->getParent())) {
1097 todo.push_back(suc);
1098 }
1099 while (todo.size()) {
1100 auto BB = todo.front();
1101 todo.pop_front();
1102 if (done.count(BB))
1103 continue;
1104 done.insert(BB);
1105
1106 if (DT.properlyDominates(BB, inst->getParent())) {
1107 llvm::BasicBlock::reverse_iterator I = BB->rbegin(), E = BB->rend();
1108 for (; I != E; ++I) {
1109 if (f(&*I))
1110 return;
1111 if (&*I == inst)
1112 break;
1113 }
1114 for (auto suc : llvm::predecessors(BB)) {
1115 todo.push_back(suc);
1116 }
1117 }
1118 }
1119}
1120
1121/// Call the function f for all instructions that happen before inst
1122/// If the function returns true, the iteration will early exit
1123static inline void
1124allUnsyncdPredecessorsOf(llvm::Instruction *inst,
1125 llvm::function_ref<bool(llvm::Instruction *)> f,
1126 llvm::function_ref<void()> preEntry) {
1127
1128 for (auto uinst = inst->getPrevNode(); uinst != nullptr;
1129 uinst = uinst->getPrevNode()) {
1130 if (auto II = llvm::dyn_cast<llvm::IntrinsicInst>(uinst)) {
1131 if (II->getIntrinsicID() == llvm::Intrinsic::amdgcn_s_barrier) {
1132 return;
1133 }
1134#if LLVM_VERSION_MAJOR > 20
1135 if (II->getIntrinsicID() ==
1136 llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_all) {
1137 return;
1138 }
1139#else
1140 if (II->getIntrinsicID() == llvm::Intrinsic::nvvm_barrier0) {
1141 return;
1142 }
1143#endif
1144 }
1145 if (f(uinst))
1146 return;
1147 }
1148
1149 std::deque<llvm::BasicBlock *> todo;
1150 std::set<llvm::BasicBlock *> done;
1151 for (auto suc : llvm::predecessors(inst->getParent())) {
1152 todo.push_back(suc);
1153 }
1154 while (todo.size()) {
1155 auto BB = todo.front();
1156 todo.pop_front();
1157 if (done.count(BB))
1158 continue;
1159 done.insert(BB);
1160
1161 bool syncd = false;
1162 llvm::BasicBlock::reverse_iterator I = BB->rbegin(), E = BB->rend();
1163 for (; I != E; ++I) {
1164 if (auto II = llvm::dyn_cast<llvm::IntrinsicInst>(&*I)) {
1165 if (II->getIntrinsicID() == llvm::Intrinsic::amdgcn_s_barrier) {
1166 syncd = true;
1167 break;
1168 }
1169#if LLVM_VERSION_MAJOR > 20
1170 if (II->getIntrinsicID() ==
1171 llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_all) {
1172#else
1173 if (II->getIntrinsicID() == llvm::Intrinsic::nvvm_barrier0) {
1174#endif
1175 syncd = true;
1176 break;
1177 }
1178 }
1179 if (f(&*I))
1180 return;
1181 if (&*I == inst)
1182 break;
1183 }
1184 if (!syncd) {
1185 for (auto suc : llvm::predecessors(BB)) {
1186 todo.push_back(suc);
1187 }
1188 if (&BB->getParent()->getEntryBlock() == BB) {
1189 preEntry();
1190 }
1191 }
1192 }
1193}
1194
1195#include "llvm/Analysis/LoopInfo.h"
1196
1197static inline llvm::Loop *getAncestor(llvm::Loop *R1, llvm::Loop *R2) {
1198 if (!R1 || !R2)
1199 return nullptr;
1200 for (llvm::Loop *L1 = R1; L1; L1 = L1->getParentLoop())
1201 for (llvm::Loop *L2 = R2; L2; L2 = L2->getParentLoop()) {
1202 if (L1 == L2) {
1203 return L1;
1204 }
1205 }
1206 return nullptr;
1207}
1208
1209// Add all of the stores which may execute after the instruction `inst`
1210// into the resutls vector.
1211void mayExecuteAfter(llvm::SmallVectorImpl<llvm::Instruction *> &results,
1212 llvm::Instruction *inst,
1213 const llvm::SetVector<llvm::Instruction *> &stores,
1214 const llvm::Loop *region);
1215
1216/// Return whether maybeReader can read from memory written to by maybeWriter
1217bool writesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA,
1218 llvm::TargetLibraryInfo &TLI,
1219 llvm::Instruction *maybeReader,
1220 llvm::Instruction *maybeWriter);
1221
1222// A more advanced version of writesToMemoryReadBy, where the writing
1223// instruction comes after the reading function. Specifically, even if the two
1224// instructions may access the same location, this variant checks whether
1225// also checks whether ScalarEvolution ensures that a subsequent write will not
1226// overwrite the value read by the load.
1227// A simple example: the load/store might write/read from the same
1228// location. However, no store will overwrite a previous load.
1229// for(int i=0; i<N; i++) {
1230// load A[i-1]
1231// store A[i] = ...
1232// }
1233bool overwritesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA,
1234 llvm::TargetLibraryInfo &TLI,
1235 llvm::ScalarEvolution &SE, llvm::LoopInfo &LI,
1236 llvm::DominatorTree &DT,
1237 llvm::Instruction *maybeReader,
1238 llvm::Instruction *maybeWriter,
1239 llvm::Loop *scope = nullptr);
1240static inline void
1241/// Call the function f for all instructions that happen between inst1 and inst2
1242/// If the function returns true, the iteration will early exit
1243allInstructionsBetween(llvm::LoopInfo &LI, llvm::Instruction *inst1,
1244 llvm::Instruction *inst2,
1245 llvm::function_ref<bool(llvm::Instruction *)> f) {
1246 assert(inst1->getParent()->getParent() == inst2->getParent()->getParent());
1247 for (auto uinst = inst1->getNextNode(); uinst != nullptr;
1248 uinst = uinst->getNextNode()) {
1249 if (f(uinst))
1250 return;
1251 if (uinst == inst2)
1252 return;
1253 }
1254
1255 std::set<llvm::Instruction *> instructions;
1256
1257 llvm::Loop *l1 = LI.getLoopFor(inst1->getParent());
1258 while (l1 && !l1->contains(inst2->getParent()))
1259 l1 = l1->getParentLoop();
1260
1261 // Do all instructions from inst1 up to first instance of inst2's start block
1262 {
1263 std::deque<llvm::BasicBlock *> todo;
1264 std::set<llvm::BasicBlock *> done;
1265 for (auto suc : llvm::successors(inst1->getParent())) {
1266 todo.push_back(suc);
1267 }
1268 while (todo.size()) {
1269 auto BB = todo.front();
1270 todo.pop_front();
1271 if (done.count(BB))
1272 continue;
1273 done.insert(BB);
1274
1275 for (auto &ni : *BB) {
1276 instructions.insert(&ni);
1277 }
1278 for (auto suc : llvm::successors(BB)) {
1279 if (!l1 || suc != l1->getHeader()) {
1280 todo.push_back(suc);
1281 }
1282 }
1283 }
1284 }
1285
1286 allPredecessorsOf(inst2, [&](llvm::Instruction *I) -> bool {
1287 if (instructions.find(I) == instructions.end())
1288 return /*earlyReturn*/ false;
1289 return f(I);
1290 });
1291}
1292
1293enum class MPI_CallType {
1294 ISEND = 1,
1295 IRECV = 2,
1296};
1297
1298enum class MPI_Elem {
1299 Buf = 0,
1300 Count = 1,
1301 DataType = 2,
1302 Src = 3,
1303 Tag = 4,
1304 Comm = 5,
1305 Call = 6,
1306 Old = 7
1307};
1308
1309/// Pointer into the given address space. From LLVM 17 on, and on LLVM 15/16
1310/// whenever the context has opaque pointers enabled, the element type is
1311/// ignored -- so callers with no element type to hand may pass any type
1312/// belonging to the right context (see getInt8PtrTy).
1313static inline llvm::PointerType *getPointerType(llvm::Type *T,
1314 unsigned AddressSpace = 0) {
1315#if LLVM_VERSION_MAJOR >= 17
1316 // NOLINTNEXTLINE(enzyme-pointer-type): this is the wrapper
1317 return llvm::PointerType::get(T->getContext(), AddressSpace);
1318#else
1319 // NOLINTNEXTLINE(enzyme-pointer-type): this is the wrapper
1320 return llvm::PointerType::get(T, AddressSpace);
1321#endif
1322}
1323
1324static inline llvm::PointerType *getInt8PtrTy(llvm::LLVMContext &Context,
1325 unsigned AddressSpace = 0) {
1326 return getPointerType(llvm::Type::getInt8Ty(Context), AddressSpace);
1327}
1328
1329static inline llvm::PointerType *getUnqual(llvm::Type *T) {
1330 return getPointerType(T);
1331}
1332
1333/// The same pointer type, in a different address space. On a typed-pointer
1334/// build the pointee type is carried over; an opaque pointer has none to carry.
1335/// Prefer this to rebuilding a pointer type by hand: it says that only the
1336/// address space changes, rather than leaving it to the reader to check that
1337/// dropping the pointee was deliberate.
1338static inline llvm::PointerType *changePointerAddrSpace(llvm::PointerType *PT,
1339 unsigned AddressSpace) {
1340#if LLVM_VERSION_MAJOR < 17
1341 if (PT->getContext().supportsTypedPointers())
1342 return getPointerType(PT->getPointerElementType(), AddressSpace);
1343#endif
1344 // NOLINTNEXTLINE(enzyme-pointer-type): this is the wrapper
1345 return llvm::PointerType::get(PT->getContext(), AddressSpace);
1346}
1347
1348/// The byte offset of the aggregate element addressed by `Idxs` within `T`,
1349/// that is, what `gep T, ptr, 0, Idxs...` would compute. The data layout
1350/// already records this, so read it off directly rather than materializing a
1351/// throwaway GEP and folding it: callers run this once per field of every
1352/// aggregate they walk.
1353static inline uint64_t
1354getAggregateElementOffset(const llvm::DataLayout &DL, llvm::Type *T,
1355 llvm::ArrayRef<unsigned> Idxs) {
1356 uint64_t Offset = 0;
1357 for (unsigned Idx : Idxs) {
1358 llvm::Type *ElTy =
1359 llvm::GetElementPtrInst::getTypeAtIndex(T, (uint64_t)Idx);
1360 assert(ElTy && "index out of range of aggregate type");
1361 if (auto ST = llvm::dyn_cast<llvm::StructType>(T)) {
1362 Offset += DL.getStructLayout(ST)->getElementOffset(Idx);
1363 } else {
1364 // Array and vector elements sit at successive multiples of the element's
1365 // alloc size, which is the stride a gep applies to a sequential index.
1366 uint64_t Stride = DL.getTypeAllocSize(ElTy);
1367 Offset += Idx * Stride;
1368 }
1369 T = ElTy;
1370 }
1371 return Offset;
1372}
1373
1374/// The byte offset of element `Idx` of the aggregate type `T`.
1375static inline uint64_t getAggregateElementOffset(const llvm::DataLayout &DL,
1376 llvm::Type *T, uint64_t Idx) {
1377 unsigned Idxs[1] = {(unsigned)Idx};
1378 return getAggregateElementOffset(DL, T, Idxs);
1379}
1380
1381static inline llvm::StructType *getMPIHelper(llvm::LLVMContext &Context) {
1382 using namespace llvm;
1383 auto i64 = Type::getInt64Ty(Context);
1384 Type *types[] = {
1385 /*buf 0 */ getInt8PtrTy(Context),
1386 /*count 1 */ i64,
1387 /*datatype 2 */ getInt8PtrTy(Context),
1388 /*src 3 */ i64,
1389 /*tag 4 */ i64,
1390 /*comm 5 */ getInt8PtrTy(Context),
1391 /*fn 6 */ Type::getInt8Ty(Context),
1392 /*old 7 */ getInt8PtrTy(Context),
1393 };
1394 return StructType::get(Context, types, false);
1395}
1396
1397template <MPI_Elem E, bool Pointer = true>
1398static inline llvm::Value *getMPIMemberPtr(llvm::IRBuilder<> &B, llvm::Value *V,
1399 llvm::Type *T) {
1400 using namespace llvm;
1401 auto i64 = Type::getInt64Ty(V->getContext());
1402 auto i32 = Type::getInt32Ty(V->getContext());
1403 auto c0_64 = ConstantInt::get(i64, 0);
1404
1405 if (Pointer) {
1406 return B.CreateInBoundsGEP(T, V,
1407 {c0_64, ConstantInt::get(i32, (uint64_t)E)});
1408 } else {
1409 return B.CreateExtractValue(V, {(unsigned)E});
1410 }
1411}
1412
1413/// `templateFn` is the MPI entry point this reduction is being built for; the
1414/// MPI_Op_create it needs is named to match.
1415llvm::Value *getOrInsertOpFloatSum(llvm::Module &M, llvm::Function *templateFn,
1416 llvm::Type *OpPtr, llvm::Type *OpType,
1417 ConcreteType CT, llvm::Type *intType,
1418 llvm::IRBuilder<> &B2);
1419
1420class AssertingReplacingVH final : public llvm::CallbackVH {
1421public:
1423
1424 AssertingReplacingVH(llvm::Value *new_value) { setValPtr(new_value); }
1425
1426 void deleted() override final {
1427 assert(0 && "attempted to delete value with remaining handle use");
1428 llvm_unreachable("attempted to delete value with remaining handle use");
1429 }
1430
1431 void allUsesReplacedWith(llvm::Value *new_value) override final {
1432 setValPtr(new_value);
1433 }
1435};
1436
1437template <typename T> static inline llvm::Function *getFunctionFromCall(T *op) {
1438 const llvm::Function *called = nullptr;
1439 using namespace llvm;
1440 const llvm::Value *callVal;
1441 callVal = op->getCalledOperand();
1442 while (!called) {
1443 if (auto castinst = dyn_cast<ConstantExpr>(callVal))
1444 if (castinst->isCast()) {
1445 callVal = castinst->getOperand(0);
1446 continue;
1447 }
1448 if (auto fn = dyn_cast<Function>(callVal)) {
1449 called = fn;
1450 break;
1451 }
1452 if (auto alias = dyn_cast<GlobalAlias>(callVal)) {
1453 callVal = alias->getAliasee();
1454 continue;
1455 }
1456 break;
1457 }
1458 return called ? const_cast<llvm::Function *>(called) : nullptr;
1459}
1460
1461static inline llvm::StringRef getFuncName(llvm::Function *called) {
1462 if (called->hasFnAttribute("enzyme_math"))
1463 return called->getFnAttribute("enzyme_math").getValueAsString();
1464 else if (called->hasFnAttribute("enzyme_allocator"))
1465 return "enzyme_allocator";
1466 else
1467 return called->getName();
1468}
1469
1470static inline llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op) {
1471 auto AttrList =
1472 op->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
1473 if (AttrList.hasAttribute("enzyme_math"))
1474 return AttrList.getAttribute("enzyme_math").getValueAsString();
1475 if (AttrList.hasAttribute("enzyme_allocator"))
1476 return "enzyme_allocator";
1477
1478 if (auto called = getFunctionFromCall(op)) {
1479 return getFuncName(called);
1480 }
1481 return "";
1482}
1483
1484static inline bool hasNoCache(llvm::Value *op) {
1485 using namespace llvm;
1486 if (auto CB = dyn_cast<CallBase>(op)) {
1487 if (auto called = getFunctionFromCall(CB)) {
1488 if (called->hasFnAttribute("enzyme_nocache"))
1489 return true;
1490 }
1491 if (EnzymeJuliaAddrLoad && getFuncNameFromCall(CB) == "julia.gc_loaded") {
1492 return true;
1493 }
1494 }
1495 if (auto I = dyn_cast<Instruction>(op))
1496 if (hasMetadata(I, "enzyme_nocache"))
1497 return true;
1498
1499 if (EnzymeJuliaAddrLoad) {
1500 if (auto PT = dyn_cast<PointerType>(op->getType())) {
1501 if (PT->getAddressSpace() == 11 || PT->getAddressSpace() == 13) {
1502 if (isa<CastInst>(op) || isa<GetElementPtrInst>(op))
1503 return true;
1504 }
1505 }
1506 }
1507 if (auto IT = dyn_cast<IntegerType>(op->getType()))
1508 if (!isPowerOf2_64(IT->getBitWidth()) && !EnzymeNonPower2Cache)
1509 return true;
1510
1511 return false;
1512}
1513
1514#if LLVM_VERSION_MAJOR >= 16
1515static inline std::optional<size_t>
1516getAllocationIndexFromCall(const llvm::CallBase *op)
1517#else
1518static inline llvm::Optional<size_t>
1519getAllocationIndexFromCall(const llvm::CallBase *op)
1520#endif
1521{
1522 auto AttrList =
1523 op->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
1524 if (AttrList.hasAttribute("enzyme_allocator")) {
1525 size_t res;
1526 bool b = AttrList.getAttribute("enzyme_allocator")
1527 .getValueAsString()
1528 .getAsInteger(10, res);
1529 (void)b;
1530 assert(!b);
1531#if LLVM_VERSION_MAJOR >= 16
1532 return std::optional<size_t>(res);
1533#else
1534 return llvm::Optional<size_t>(res);
1535#endif
1536 }
1537
1538 if (auto called = getFunctionFromCall(op)) {
1539 if (called->hasFnAttribute("enzyme_allocator")) {
1540 size_t res;
1541 bool b = called->getFnAttribute("enzyme_allocator")
1542 .getValueAsString()
1543 .getAsInteger(10, res);
1544 (void)b;
1545 assert(!b);
1546#if LLVM_VERSION_MAJOR >= 16
1547 return std::optional<size_t>(res);
1548#else
1549 return llvm::Optional<size_t>(res);
1550#endif
1551 }
1552 }
1553#if LLVM_VERSION_MAJOR >= 16
1554 return std::optional<size_t>();
1555#else
1556 return llvm::Optional<size_t>();
1557#endif
1558}
1559
1560template <typename T>
1561static inline llvm::Function *getDeallocatorFnFromCall(T *op) {
1562 if (auto MD = hasMetadata(op, "enzyme_deallocator_fn")) {
1563 auto md2 = llvm::cast<llvm::MDTuple>(MD);
1564 assert(md2->getNumOperands() == 1);
1565 return llvm::cast<llvm::Function>(
1566 llvm::cast<llvm::ConstantAsMetadata>(md2->getOperand(0))->getValue());
1567 }
1568 if (auto called = getFunctionFromCall(op)) {
1569 if (auto MD = hasMetadata(called, "enzyme_deallocator_fn")) {
1570 auto md2 = llvm::cast<llvm::MDTuple>(MD);
1571 assert(md2->getNumOperands() == 1);
1572 return llvm::cast<llvm::Function>(
1573 llvm::cast<llvm::ConstantAsMetadata>(md2->getOperand(0))->getValue());
1574 }
1575 }
1576 llvm::errs() << "dealloc fn: " << *op->getParent()->getParent()->getParent()
1577 << "\n";
1578 llvm_unreachable("Illegal deallocatorfn");
1579}
1580
1581template <typename T>
1582static inline std::vector<ssize_t> getDeallocationIndicesFromCall(T *op) {
1583 llvm::StringRef res = "";
1584 auto AttrList =
1585 op->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
1586 if (AttrList.hasAttribute("enzyme_deallocator"))
1587 res = AttrList.getAttribute("enzyme_deaellocator").getValueAsString();
1588
1589 if (auto called = getFunctionFromCall(op)) {
1590 if (called->hasFnAttribute("enzyme_deallocator"))
1591 res = called->getFnAttribute("enzyme_deallocator").getValueAsString();
1592 }
1593 if (res.size() == 0)
1594 llvm_unreachable("Illegal deallocator");
1595 llvm::SmallVector<llvm::StringRef, 1> inds;
1596 res.split(inds, ",");
1597 std::vector<ssize_t> vinds;
1598 for (auto ind : inds) {
1599 ssize_t Result;
1600 bool b = ind.getAsInteger(10, Result);
1601 (void)b;
1602 assert(!b);
1603 vinds.push_back(Result);
1604 }
1605 return vinds;
1606}
1607
1608llvm::Function *
1610 llvm::ArrayRef<llvm::Type *> T,
1611 llvm::PointerType *reqType);
1612
1613void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal,
1614 llvm::Value *shadow, const char *Message,
1615 llvm::DebugLoc &&loc, llvm::Instruction *orig);
1616
1617llvm::Function *GetFunctionFromValue(llvm::Value *fn);
1618
1619llvm::Function *getFirstFunctionDefinition(llvm::Module &M);
1620
1621llvm::Value *simplifyLoad(llvm::Value *LI, size_t valSz = 0,
1622 size_t preOffset = 0);
1623
1624// Whether a custom derivative was registered for the function or call through
1625// metadata (__enzyme_register_derivative and friends).
1626template <typename T> static inline bool hasCustomRuleMetadata(const T *V) {
1627 return hasMetadata(V, "enzyme_augment") ||
1628 hasMetadata(V, "enzyme_gradient") ||
1629 hasMetadata(V, "enzyme_derivative") ||
1630 hasMetadata(V, "enzyme_splitderivative");
1631}
1632
1633static inline bool shouldDisableNoWrite(const llvm::CallInst *CI) {
1634 auto F = getFunctionFromCall(CI);
1635 auto funcName = getFuncNameFromCall(CI);
1636
1637 if (CI->hasFnAttr("enzyme_preserve_primal") || hasCustomRuleMetadata(CI) ||
1638 (F && (F->hasFnAttribute("enzyme_preserve_primal") ||
1639 hasCustomRuleMetadata(F))) ||
1640 !F) {
1641 return true;
1642 }
1643 if (funcName == "MPI_Wait" || funcName == "MPI_Waitall") {
1644 return true;
1645 }
1646 return false;
1647}
1648
1649static inline bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II) {
1650 return startsWith(getFuncNameFromCall(&II), "llvm.intel.subscript");
1651}
1652
1653static inline bool isIntelSubscriptIntrinsic(const llvm::Value *val) {
1654 if (auto II = llvm::dyn_cast<llvm::IntrinsicInst>(val)) {
1655 return isIntelSubscriptIntrinsic(*II);
1656 }
1657 return false;
1658}
1659
1660static inline bool isPointerArithmeticInst(const llvm::Value *V,
1661 bool includephi = true,
1662 bool includebin = true) {
1663 if (llvm::isa<llvm::CastInst>(V) || llvm::isa<llvm::GetElementPtrInst>(V) ||
1664 (includephi && llvm::isa<llvm::PHINode>(V)))
1665 return true;
1666
1667 if (includebin)
1668 if (auto BI = llvm::dyn_cast<llvm::BinaryOperator>(V)) {
1669 switch (BI->getOpcode()) {
1670 case llvm::BinaryOperator::Add:
1671 case llvm::BinaryOperator::Sub:
1672 case llvm::BinaryOperator::Mul:
1673 case llvm::BinaryOperator::SDiv:
1674 case llvm::BinaryOperator::UDiv:
1675 case llvm::BinaryOperator::SRem:
1676 case llvm::BinaryOperator::URem:
1677 case llvm::BinaryOperator::Or:
1678 case llvm::BinaryOperator::And:
1679 case llvm::BinaryOperator::Shl:
1680 case llvm::BinaryOperator::LShr:
1681 case llvm::BinaryOperator::AShr:
1682 return true;
1683 default:
1684 break;
1685 }
1686 }
1687
1689 return true;
1690 }
1691
1692 if (auto *Call = llvm::dyn_cast<llvm::CallInst>(V)) {
1693 auto funcName = getFuncNameFromCall(Call);
1694 if (funcName == "julia.pointer_from_objref") {
1695 return true;
1696 }
1697 if (funcName == "julia.gc_loaded") {
1698 return true;
1699 }
1700 if (funcName.contains("__enzyme_todense")) {
1701 return true;
1702 }
1703 if (funcName.contains("__enzyme_ignore_derivatives")) {
1704 return true;
1705 }
1706 }
1707
1708 return false;
1709}
1710
1711static inline llvm::Value *getBaseObject(llvm::Value *V,
1712 bool offsetAllowed = true) {
1713 while (true) {
1714 if (auto CI = llvm::dyn_cast<llvm::CastInst>(V)) {
1715 V = CI->getOperand(0);
1716 continue;
1717 } else if (auto CI = llvm::dyn_cast<llvm::GetElementPtrInst>(V)) {
1718 if (offsetAllowed || CI->hasAllZeroIndices()) {
1719 V = CI->getOperand(0);
1720 continue;
1721 }
1722 } else if (auto II = llvm::dyn_cast<llvm::IntrinsicInst>(V);
1723 II && isIntelSubscriptIntrinsic(*II)) {
1724 if (offsetAllowed) {
1725 V = II->getOperand(3);
1726 continue;
1727 }
1728 } else if (auto CI = llvm::dyn_cast<llvm::PHINode>(V)) {
1729 if (CI->getNumIncomingValues() == 1) {
1730 V = CI->getOperand(0);
1731 continue;
1732 }
1733 } else if (auto *GA = llvm::dyn_cast<llvm::GlobalAlias>(V)) {
1734 if (GA->isInterposable())
1735 break;
1736 V = GA->getAliasee();
1737 continue;
1738 } else if (auto CE = llvm::dyn_cast<llvm::ConstantExpr>(V)) {
1739 if (CE->isCast() || CE->getOpcode() == llvm::Instruction::GetElementPtr) {
1740 V = CE->getOperand(0);
1741 continue;
1742 }
1743 } else if (auto *Call = llvm::dyn_cast<llvm::CallInst>(V)) {
1744 auto funcName = getFuncNameFromCall(Call);
1745 auto AttrList = Call->getAttributes().getAttributes(
1746 llvm::AttributeList::FunctionIndex);
1747 if (AttrList.hasAttribute("enzyme_pointermath") && offsetAllowed) {
1748 size_t res = 0;
1749 bool failed = AttrList.getAttribute("enzyme_pointermath")
1750 .getValueAsString()
1751 .getAsInteger(10, res);
1752 (void)failed;
1753 assert(!failed);
1754 V = Call->getArgOperand(res);
1755 continue;
1756 }
1757 if (funcName == "julia.pointer_from_objref") {
1758 V = Call->getArgOperand(0);
1759 continue;
1760 }
1761 if (funcName == "julia.gc_loaded") {
1762 V = Call->getArgOperand(1);
1763 continue;
1764 }
1765 if (funcName == "jl_reshape_array" || funcName == "ijl_reshape_array") {
1766 V = Call->getArgOperand(1);
1767 continue;
1768 }
1769 if (funcName.contains("__enzyme_ignore_derivatives")) {
1770 V = Call->getArgOperand(0);
1771 continue;
1772 }
1773 if (funcName.contains("__enzyme_todense")) {
1774#if LLVM_VERSION_MAJOR >= 14
1775 size_t numargs = Call->arg_size();
1776#else
1777 size_t numargs = Call->getNumArgOperands();
1778#endif
1779 if (numargs == 3) {
1780 V = Call->getArgOperand(2);
1781 continue;
1782 }
1783 }
1784 if (auto fn = getFunctionFromCall(Call)) {
1785 auto AttrList = fn->getAttributes().getAttributes(
1786 llvm::AttributeList::FunctionIndex);
1787 if (AttrList.hasAttribute("enzyme_pointermath") && offsetAllowed) {
1788 size_t res = 0;
1789 bool failed = AttrList.getAttribute("enzyme_pointermath")
1790 .getValueAsString()
1791 .getAsInteger(10, res);
1792 (void)failed;
1793 assert(!failed);
1794 V = Call->getArgOperand(res);
1795 continue;
1796 }
1797 bool found = false;
1798 for (auto &arg : fn->args()) {
1799 if (arg.hasAttribute(llvm::Attribute::Returned)) {
1800 found = true;
1801 V = Call->getArgOperand(arg.getArgNo());
1802 }
1803 }
1804 if (found)
1805 continue;
1806 }
1807
1808 // CaptureTracking can know about special capturing properties of some
1809 // intrinsics like launder.invariant.group, that can't be expressed with
1810 // the attributes, but have properties like returning aliasing pointer.
1811 // Because some analysis may assume that nocaptured pointer is not
1812 // returned from some special intrinsic (because function would have to
1813 // be marked with returns attribute), it is crucial to use this function
1814 // because it should be in sync with CaptureTracking. Not using it may
1815 // cause weird miscompilations where 2 aliasing pointers are assumed to
1816 // noalias.
1817 if (offsetAllowed)
1818 if (auto *RP =
1819 llvm::getArgumentAliasingToReturnedPointer(Call, false)) {
1820 V = RP;
1821 continue;
1822 }
1823 }
1824
1825 if (offsetAllowed)
1826 if (auto I = llvm::dyn_cast<llvm::Instruction>(V)) {
1827#if LLVM_VERSION_MAJOR >= 12
1828 auto V2 = llvm::getUnderlyingObject(I, 100);
1829#else
1830 auto V2 = llvm::GetUnderlyingObject(
1831 I, I->getParent()->getParent()->getParent()->getDataLayout(), 100);
1832#endif
1833 if (V2 != V) {
1834 V = V2;
1835 break;
1836 }
1837 }
1838 break;
1839 }
1840 return V;
1841}
1842static inline const llvm::Value *getBaseObject(const llvm::Value *V) {
1843 return getBaseObject(const_cast<llvm::Value *>(V));
1844}
1845
1846static inline llvm::SetVector<llvm::Value *>
1847getBaseObjects(llvm::Value *V, bool offsetAllowed = true) {
1848 llvm::SmallPtrSet<llvm::Value *, 1> seen;
1849 llvm::SetVector<llvm::Value *> results;
1850 llvm::SmallVector<llvm::Value *, 1> todo = {V};
1851
1852 while (todo.size()) {
1853 auto obj = todo.back();
1854 todo.pop_back();
1855 if (seen.contains(obj))
1856 continue;
1857 seen.insert(obj);
1858
1859 if (auto PN = llvm::dyn_cast<llvm::PHINode>(obj)) {
1860 for (auto &x : PN->incoming_values()) {
1861 todo.push_back(x);
1862 }
1863 continue;
1864 }
1865
1866 if (auto SI = llvm::dyn_cast<llvm::SelectInst>(obj)) {
1867 todo.push_back(SI->getTrueValue());
1868 todo.push_back(SI->getFalseValue());
1869 continue;
1870 }
1871
1872 auto cur = getBaseObject(obj, offsetAllowed);
1873 if (cur != obj) {
1874 todo.push_back(cur);
1875 continue;
1876 }
1877
1878 results.insert(obj);
1879 }
1880 return results;
1881}
1882
1883static inline bool isReadOnly(const llvm::Function *F, ssize_t arg = -1) {
1884 if (F->onlyReadsMemory())
1885 return true;
1886
1887 if (F->hasFnAttribute(llvm::Attribute::ReadOnly) ||
1888 F->hasFnAttribute(llvm::Attribute::ReadNone))
1889 return true;
1890 if (arg != -1) {
1891 if (F->hasParamAttribute(arg, llvm::Attribute::ReadOnly) ||
1892 F->hasParamAttribute(arg, llvm::Attribute::ReadNone))
1893 return true;
1894#if LLVM_VERSION_MAJOR >= 16
1895 // Later LLVM folds readonly/readnone into the memory(...) attribute. The
1896 // argument's bytes must not be written through the argument pointers
1897 // (ArgMem) nor through anything else that could alias them (Other);
1898 // inaccessible memory cannot alias an argument, so it alone may be
1899 // written.
1900 {
1901 auto ME = F->getMemoryEffects();
1902 if (!llvm::isModSet(
1903 ME.getModRef(llvm::MemoryEffects::Location::ArgMem)) &&
1904 !llvm::isModSet(ME.getModRef(llvm::MemoryEffects::Location::Other)))
1905 return true;
1906 }
1907#endif
1908 // if (F->getAttributes().hasParamAttribute(arg, "enzyme_ReadOnly") ||
1909 // F->getAttributes().hasParamAttribute(arg, "enzyme_ReadNone"))
1910 // return true;
1911 }
1912 return false;
1913}
1914
1915static inline bool isReadOnly(const llvm::CallBase *call, ssize_t arg = -1) {
1916 if (call->onlyReadsMemory())
1917 return true;
1918 if (arg != -1 && call->onlyReadsMemory(arg))
1919 return true;
1920#if LLVM_VERSION_MAJOR >= 16
1921 if (arg != -1) {
1922 // Use the callee's effects only under a matching calling convention,
1923 // for the same reason as the attribute path below. As above, both the
1924 // argument-memory and aliasable-other locations must be write-free.
1925 auto F2 = getFunctionFromCall(call);
1926 if (!F2 || F2->getCallingConv() == call->getCallingConv()) {
1927 auto ME = call->getMemoryEffects();
1928 if (!llvm::isModSet(
1929 ME.getModRef(llvm::MemoryEffects::Location::ArgMem)) &&
1930 !llvm::isModSet(ME.getModRef(llvm::MemoryEffects::Location::Other)))
1931 return true;
1932 }
1933 }
1934#endif
1935
1936 if (auto F = getFunctionFromCall(call)) {
1937 // Do not use function attrs for if different calling conv, such as a julia
1938 // call wrapping args into an array. This is because the wrapped array
1939 // may be nocapure/readonly, but the actual arg (which will be put in the
1940 // array) may not be.
1941 if (F->getCallingConv() == call->getCallingConv())
1942 if (isReadOnly(F, arg))
1943 return true;
1944 }
1945 return false;
1946}
1947
1948// Whether the function does not write to memory visible before the function in
1949// all cases that it doesn't error. In other words, the legal operations here
1950// are:
1951// 1) Throw [in which case any operation guaranteed to throw is valid]
1952// 2) Read from any memory
1953// 3) Write to memory which did not exist prior to the function call, i.e.
1954// memory allocated within the call to F (including a local alloca, or a
1955// malloc call, even if returned), and to an sret and/or returnroots
1956// parameter (which must be an alloca).
1957// A call to F may thus return memory it wrote, directly or through such a
1958// parameter.
1959static inline bool isLocalReadOnlyOrThrow(const llvm::Function *F) {
1960 if (isReadOnly(F))
1961 return true;
1962
1963 if (F->hasFnAttribute("enzyme_LocalReadOnlyOrThrow") ||
1964 F->hasFnAttribute("enzyme_ReadOnlyOrThrow"))
1965 return true;
1966
1967 return false;
1968}
1969
1970static inline bool isLocalReadOnlyOrThrow(const llvm::CallBase *call) {
1971 if (isReadOnly(call))
1972 return true;
1973
1974 if (call->hasFnAttr("enzyme_LocalReadOnlyOrThrow") ||
1975 call->hasFnAttr("enzyme_ReadOnlyOrThrow"))
1976 return true;
1977
1978 if (auto F = getFunctionFromCall(call)) {
1979 // Do not use function attrs for if different calling conv, such as a julia
1980 // call wrapping args into an array. This is because the wrapped array
1981 // may be nocapure/readonly, but the actual arg (which will be put in the
1982 // array) may not be.
1983 if (F->getCallingConv() == call->getCallingConv())
1985 return true;
1986 }
1987 return false;
1988}
1989
1990// Whether the function does not write to memory visible outside the function in
1991// all cases that it doesn't error. In other words, the legal operations here
1992// are:
1993// 1) Throw [in which case any operation guaranteed to throw is valid]
1994// 2) Read from any memory
1995// 3) Write to memory whose lifetime is entirely contained within the call to
1996// F (including a local alloca, or a malloc call locally freed), but not
1997// to memory it returns or otherwise lets escape, even if allocated within
1998// it.
1999// Allocating memory, even memory F returns, is not a write. A call to F thus
2000// never returns memory it wrote: if it returns a pointer, derivatives can only
2001// flow through the memory it points to, not through the call. A function that
2002// may return memory it wrote, including memory a callee wrote and returned to
2003// it, is only local read-only-or-throw.
2004static inline bool isReadOnlyOrThrow(const llvm::Function *F) {
2005 if (isReadOnly(F))
2006 return true;
2007
2008 if (F->hasFnAttribute("enzyme_ReadOnlyOrThrow"))
2009 return true;
2010
2011 return false;
2012}
2013
2014static inline bool isReadOnlyOrThrow(const llvm::CallBase *call) {
2015 if (isReadOnly(call))
2016 return true;
2017
2018 if (call->hasFnAttr("enzyme_ReadOnlyOrThrow"))
2019 return true;
2020
2021 if (auto F = getFunctionFromCall(call)) {
2022 // Do not use function attrs for if different calling conv, such as a julia
2023 // call wrapping args into an array. This is because the wrapped array
2024 // may be nocapure/readonly, but the actual arg (which will be put in the
2025 // array) may not be.
2026 if (F->getCallingConv() == call->getCallingConv())
2027 if (isReadOnlyOrThrow(F))
2028 return true;
2029 }
2030 return false;
2031}
2032
2033static inline bool isWriteOnly(const llvm::Function *F, ssize_t arg = -1) {
2034#if LLVM_VERSION_MAJOR >= 14
2035 if (F->onlyWritesMemory())
2036 return true;
2037#endif
2038 if (F->hasFnAttribute(llvm::Attribute::WriteOnly) ||
2039 F->hasFnAttribute(llvm::Attribute::ReadNone))
2040 return true;
2041 if (arg != -1) {
2042 if (F->hasParamAttribute(arg, llvm::Attribute::WriteOnly) ||
2043 F->hasParamAttribute(arg, llvm::Attribute::ReadNone))
2044 return true;
2045#if LLVM_VERSION_MAJOR >= 16
2046 // As in isReadOnly: the argument's bytes must be unread both through the
2047 // argument pointers and through anything that could alias them; only
2048 // inaccessible memory may be read.
2049 {
2050 auto ME = F->getMemoryEffects();
2051 if (!llvm::isRefSet(
2052 ME.getModRef(llvm::MemoryEffects::Location::ArgMem)) &&
2053 !llvm::isRefSet(ME.getModRef(llvm::MemoryEffects::Location::Other)))
2054 return true;
2055 }
2056#endif
2057 }
2058 return false;
2059}
2060
2061static inline bool isWriteOnly(const llvm::CallBase *call, ssize_t arg = -1) {
2062#if LLVM_VERSION_MAJOR >= 14
2063 if (call->onlyWritesMemory())
2064 return true;
2065 if (arg != -1 && call->onlyWritesMemory(arg))
2066 return true;
2067#if LLVM_VERSION_MAJOR >= 16
2068 if (arg != -1) {
2069 auto F2 = getFunctionFromCall(call);
2070 if (!F2 || F2->getCallingConv() == call->getCallingConv()) {
2071 auto ME = call->getMemoryEffects();
2072 if (!llvm::isRefSet(
2073 ME.getModRef(llvm::MemoryEffects::Location::ArgMem)) &&
2074 !llvm::isRefSet(ME.getModRef(llvm::MemoryEffects::Location::Other)))
2075 return true;
2076 }
2077 }
2078#endif
2079#else
2080 if (call->hasFnAttr(llvm::Attribute::WriteOnly) ||
2081 call->hasFnAttr(llvm::Attribute::ReadNone))
2082 return true;
2083 if (arg != -1) {
2084 if (call->dataOperandHasImpliedAttr(arg + 1, llvm::Attribute::WriteOnly) ||
2085 call->dataOperandHasImpliedAttr(arg + 1, llvm::Attribute::ReadNone))
2086 return true;
2087 }
2088#endif
2089
2090 if (auto F = getFunctionFromCall(call)) {
2091 // Do not use function attrs for if different calling conv, such as a julia
2092 // call wrapping args into an array. This is because the wrapped array
2093 // may be nocapure/readonly, but the actual arg (which will be put in the
2094 // array) may not be.
2095 if (F->getCallingConv() == call->getCallingConv())
2096 return isWriteOnly(F, arg);
2097 }
2098 return false;
2099}
2100
2101static inline bool isReadNone(const llvm::CallBase *call, ssize_t arg = -1) {
2102 return isReadOnly(call, arg) && isWriteOnly(call, arg);
2103}
2104
2105static inline bool isReadNone(const llvm::Function *F, ssize_t arg = -1) {
2106 return isReadOnly(F, arg) && isWriteOnly(F, arg);
2107}
2108
2109static inline bool isNoCapture(const llvm::CallBase *call, size_t idx) {
2110 if (call->doesNotCapture(idx))
2111 return true;
2112
2113 if (auto F = getFunctionFromCall(call)) {
2114 // Do not use function attrs for if different calling conv, such as a julia
2115 // call wrapping args into an array. This is because the wrapped array
2116 // may be nocapure/readonly, but the actual arg (which will be put in the
2117 // array) may not be.
2118 if (F->getCallingConv() == call->getCallingConv())
2119 if (idx < F->arg_size() && F->getArg(idx)->hasNoCaptureAttr())
2120 return true;
2121 // if (F->getAttributes().hasParamAttribute(idx, "enzyme_NoCapture"))
2122 // return true;
2123 }
2124 return false;
2125}
2126
2127static inline bool isNoAlias(const llvm::CallBase *call) {
2128 if (call->returnDoesNotAlias())
2129 return true;
2130
2131 if (auto F = getFunctionFromCall(call)) {
2132 if (F->returnDoesNotAlias())
2133 return true;
2134 }
2135 return false;
2136}
2137
2138static inline bool isNoAlias(const llvm::Value *val) {
2139 if (auto CB = llvm::dyn_cast<llvm::CallBase>(val))
2140 return isNoAlias(CB);
2141 if (auto arg = llvm::dyn_cast<llvm::Argument>(val)) {
2142 arg->hasNoAliasAttr();
2143 }
2144 if (llvm::isa<llvm::AllocaInst>(val))
2145 return true;
2146 return false;
2147}
2148
2149/// LLVM flang runtime functions that neither free memory of the program nor
2150/// return or store an allocation into it: I/O (which frees only the I/O
2151/// statement state that _FortranAioBegin* allocated and
2152/// _FortranAioEndIoStatement releases), FLUSH, the scalar character
2153/// inquiries and comparison, and MOD/MODULO.
2154bool isFlangRuntimeNoFree(llvm::StringRef name);
2155
2156static inline bool isNoEscapingAllocation(const llvm::Function *F) {
2157 if (F->hasFnAttribute("enzyme_no_escaping_allocation"))
2158 return true;
2159 if (F->getName() == "llvm.enzyme.lifetime_start" ||
2160 F->getName() == "llvm.enzyme.lifetime_end") {
2161 return true;
2162 }
2163 using namespace llvm;
2164 switch (F->getIntrinsicID()) {
2165 case Intrinsic::memset:
2166 case Intrinsic::memcpy:
2167 case Intrinsic::memmove:
2168#if LLVM_VERSION_MAJOR >= 12
2169 case Intrinsic::experimental_noalias_scope_decl:
2170#endif
2171 case Intrinsic::objectsize:
2172 case Intrinsic::floor:
2173 case Intrinsic::ceil:
2174 case Intrinsic::trunc:
2175 case Intrinsic::rint:
2176 case Intrinsic::lrint:
2177 case Intrinsic::llrint:
2178 case Intrinsic::nearbyint:
2179 case Intrinsic::round:
2180 case Intrinsic::roundeven:
2181 case Intrinsic::lround:
2182 case Intrinsic::llround:
2183#if LLVM_VERSION_MAJOR <= 20
2184 case Intrinsic::nvvm_barrier0:
2185#else
2186 case Intrinsic::nvvm_barrier_cta_sync_aligned_all:
2187 case Intrinsic::nvvm_barrier_cta_sync_aligned_count:
2188#endif
2189#if LLVM_VERSION_MAJOR < 22
2190 case Intrinsic::nvvm_barrier0_popc:
2191 case Intrinsic::nvvm_barrier0_and:
2192 case Intrinsic::nvvm_barrier0_or:
2193#else
2194 case Intrinsic::nvvm_barrier_cta_red_and_aligned_all:
2195 case Intrinsic::nvvm_barrier_cta_red_and_aligned_count:
2196 case Intrinsic::nvvm_barrier_cta_red_or_aligned_all:
2197 case Intrinsic::nvvm_barrier_cta_red_or_aligned_count:
2198 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_all:
2199 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_count:
2200#endif
2201 case Intrinsic::nvvm_membar_cta:
2202 case Intrinsic::nvvm_membar_gl:
2203 case Intrinsic::nvvm_membar_sys:
2204 case Intrinsic::amdgcn_s_barrier:
2205 case Intrinsic::assume:
2206 case Intrinsic::lifetime_start:
2207 case Intrinsic::lifetime_end:
2208#if LLVM_VERSION_MAJOR <= 16
2209 case Intrinsic::dbg_addr:
2210#endif
2211
2212 case Intrinsic::dbg_declare:
2213 case Intrinsic::dbg_value:
2214 case Intrinsic::dbg_label:
2215 case Intrinsic::invariant_start:
2216 case Intrinsic::invariant_end:
2217 case Intrinsic::var_annotation:
2218 case Intrinsic::ptr_annotation:
2219 case Intrinsic::annotation:
2220 case Intrinsic::codeview_annotation:
2221 case Intrinsic::expect:
2222 case Intrinsic::type_test:
2223 case Intrinsic::donothing:
2224 case Intrinsic::prefetch:
2225 case Intrinsic::trap:
2226 case Intrinsic::is_constant:
2227#if LLVM_VERSION_MAJOR >= 12
2228 case Intrinsic::smax:
2229 case Intrinsic::smin:
2230 case Intrinsic::umax:
2231 case Intrinsic::umin:
2232#endif
2233 case Intrinsic::ctlz:
2234 case Intrinsic::cttz:
2235 case Intrinsic::sadd_with_overflow:
2236 case Intrinsic::ssub_with_overflow:
2237#if LLVM_VERSION_MAJOR >= 12
2238 case Intrinsic::abs:
2239#endif
2240 case Intrinsic::sqrt:
2241 case Intrinsic::exp:
2242 case Intrinsic::cos:
2243 case Intrinsic::sin:
2244#if LLVM_VERSION_MAJOR >= 19
2245 case Intrinsic::tanh:
2246 case Intrinsic::cosh:
2247 case Intrinsic::sinh:
2248#endif
2249 case Intrinsic::copysign:
2250 case Intrinsic::fabs:
2251 return true;
2252 default:
2253 break;
2254 }
2255 // if (F->empty())
2256 // llvm::errs() << " may escape:" << F->getName() << "\n";
2257 return false;
2258}
2259static inline bool isNoEscapingAllocation(const llvm::CallBase *call) {
2260 auto AttrList =
2261 call->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
2262 if (AttrList.hasAttribute("enzyme_no_escaping_allocation"))
2263 return true;
2264 if (auto F = getFunctionFromCall(call)) {
2265 auto res = isNoEscapingAllocation(F);
2266 // if (!res && F->empty()) {
2267 // llvm::errs() << " may escape:" << *call << "\n";
2268 //}
2269 return res;
2270 }
2271 return false;
2272}
2273
2274bool attributeKnownFunctions(llvm::Function &F);
2275
2276llvm::Constant *getUndefinedValueForType(llvm::Module &M, llvm::Type *T,
2277 bool forceZero = false);
2278
2279llvm::Value *SanitizeDerivatives(llvm::Value *val, llvm::Value *toset,
2280 llvm::IRBuilder<> &BuilderM,
2281 llvm::Value *mask = nullptr);
2282
2283static inline llvm::Value *CreateSelect(llvm::IRBuilder<> &Builder2,
2284 llvm::Value *cmp, llvm::Value *tval,
2285 llvm::Value *fval,
2286 const llvm::Twine &Name = "") {
2287 if (auto cmpi = llvm::dyn_cast<llvm::ConstantInt>(cmp)) {
2288 if (cmpi->isZero())
2289 return fval;
2290 else
2291 return tval;
2292 }
2293 return Builder2.CreateSelect(cmp, tval, fval, Name);
2294}
2295
2296static inline llvm::Value *checkedMul(bool strongZero,
2297 llvm::IRBuilder<> &Builder2,
2298 llvm::Value *idiff, llvm::Value *pres,
2299 const llvm::Twine &Name = "") {
2300 llvm::Value *res = Builder2.CreateFMul(idiff, pres, Name);
2301 if (strongZero) {
2302 llvm::Value *zero = llvm::Constant::getNullValue(idiff->getType());
2303 if (auto C = llvm::dyn_cast<llvm::ConstantFP>(pres))
2304 if (!C->isInfinity() && !C->isNaN())
2305 return res;
2306 res = Builder2.CreateSelect(Builder2.CreateFCmpOEQ(idiff, zero), zero, res);
2307 }
2308 return res;
2309}
2310static inline llvm::Value *checkedDiv(bool strongZero,
2311 llvm::IRBuilder<> &Builder2,
2312 llvm::Value *idiff, llvm::Value *pres,
2313 const llvm::Twine &Name = "") {
2314 llvm::Value *res = Builder2.CreateFDiv(idiff, pres, Name);
2315 if (strongZero) {
2316 llvm::Value *zero = llvm::Constant::getNullValue(idiff->getType());
2317 if (auto C = llvm::dyn_cast<llvm::ConstantFP>(pres))
2318 if (!C->isZero() && !C->isNaN())
2319 return res;
2320 res = Builder2.CreateSelect(Builder2.CreateFCmpOEQ(idiff, zero), zero, res);
2321 }
2322 return res;
2323}
2324
2325static inline bool containsOnlyAtMostTopBit(const llvm::Value *V,
2326 llvm::Type *FT,
2327 const llvm::DataLayout &dl,
2328 llvm::Type **vFT = nullptr) {
2329 using namespace llvm;
2330 if (auto CI = dyn_cast_or_null<ConstantInt>(V)) {
2331 if (CI->isZero()) {
2332 if (vFT)
2333 *vFT = FT;
2334 return true;
2335 }
2336 if (dl.getTypeSizeInBits(FT) == dl.getTypeSizeInBits(CI->getType())) {
2337 if (CI->isNegative() && CI->isMinValue(/*signed*/ true)) {
2338 if (vFT)
2339 *vFT = FT;
2340 return true;
2341 }
2342 }
2343 }
2344 if (auto CV = dyn_cast_or_null<ConstantVector>(V)) {
2345 bool legal = true;
2346 for (size_t i = 0, end = CV->getNumOperands(); i < end; ++i) {
2347 legal &= containsOnlyAtMostTopBit(CV->getOperand(i), FT, dl);
2348 }
2349 if (legal && vFT) {
2350#if LLVM_VERSION_MAJOR >= 12
2351 *vFT = VectorType::get(FT, CV->getType()->getElementCount());
2352#else
2353 *vFT = VectorType::get(FT, CV->getType()->getNumElements());
2354#endif
2355 }
2356 return legal;
2357 }
2358
2359 if (auto CV = dyn_cast_or_null<ConstantDataVector>(V)) {
2360 bool legal = true;
2361 for (size_t i = 0, end = CV->getNumElements(); i < end; ++i) {
2362 auto CI = CV->getElementAsAPInt(i);
2363#if LLVM_VERSION_MAJOR > 16
2364 if (CI.isZero())
2365 continue;
2366#else
2367 if (CI.isNullValue())
2368 continue;
2369#endif
2370 if (dl.getTypeSizeInBits(FT) !=
2371 dl.getTypeSizeInBits(CV->getElementType())) {
2372 legal = false;
2373 break;
2374 }
2375 if (!CI.isMinSignedValue()) {
2376 legal = false;
2377 break;
2378 }
2379 }
2380 if (legal && vFT) {
2381#if LLVM_VERSION_MAJOR >= 12
2382 *vFT = VectorType::get(FT, CV->getType()->getElementCount());
2383#else
2384 *vFT = VectorType::get(FT, CV->getType()->getNumElements());
2385#endif
2386 }
2387 return legal;
2388 }
2389 if (auto BO = dyn_cast<BinaryOperator>(V)) {
2390 if (BO->getOpcode() == Instruction::And) {
2391 for (size_t i = 0; i < 2; i++) {
2392 if (containsOnlyAtMostTopBit(BO->getOperand(i), FT, dl))
2393 return true;
2394 }
2395 return false;
2396 }
2397 }
2398 return false;
2399}
2400
2401void addValueToCache(llvm::Value *arg, bool cache_arg, llvm::Type *ty,
2402 llvm::SmallVectorImpl<llvm::Value *> &cacheValues,
2403 llvm::IRBuilder<> &BuilderZ, const llvm::Twine &name = "");
2404
2405llvm::Value *load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType,
2406 llvm::Value *V, bool byRef);
2407
2408void copy_lower_to_upper(llvm::IRBuilder<> &B, llvm::Type *fpType,
2409 BlasInfo blas, bool byRef, llvm::Value *layout,
2410 llvm::Value *uplo, llvm::Value *A, llvm::Value *lda,
2411 llvm::Value *N);
2412
2413// julia_decl null means not julia decl, otherwise it is the integer type needed
2414// to cast to
2415llvm::Value *to_blas_callconv(llvm::IRBuilder<> &B, llvm::Value *V, bool byRef,
2416 bool cublas, llvm::IntegerType *julia_decl,
2417 llvm::IRBuilder<> &entryBuilder,
2418 llvm::Twine const & = "");
2419llvm::Value *to_blas_fp_callconv(llvm::IRBuilder<> &B, llvm::Value *V,
2420 bool byRef, llvm::Type *julia_decl,
2421 llvm::IRBuilder<> &entryBuilder,
2422 llvm::Twine const & = "");
2423
2424llvm::Value *get_cached_mat_width(llvm::IRBuilder<> &B,
2425 llvm::ArrayRef<llvm::Value *> trans,
2426 llvm::Value *arg_ld, llvm::Value *dim_1,
2427 llvm::Value *dim_2, bool cacheMat, bool byRef,
2428 bool cublas);
2429
2430template <typename T>
2431static inline void append(llvm::SmallVectorImpl<T> &vec) {}
2432template <typename T, typename... T2>
2433static inline void append(llvm::SmallVectorImpl<T> &vec, llvm::ArrayRef<T> vals,
2434 T2 &&...ts) {
2435 vec.append(vals.begin(), vals.end());
2436 append(vec, std::forward<T2>(ts)...);
2437}
2438template <typename... T>
2439static inline llvm::SmallVector<llvm::Value *, 1> concat_values(T &&...t) {
2440 llvm::SmallVector<llvm::Value *, 1> res;
2441 append(res, std::forward<T>(t)...);
2442 return res;
2443}
2444
2445llvm::Value *is_normal(llvm::IRBuilder<> &B, llvm::Value *trans, bool byRef,
2446 bool cublas);
2447llvm::Value *is_left(llvm::IRBuilder<> &B, llvm::Value *side, bool byRef,
2448 bool cublas);
2449llvm::Value *is_lower(llvm::IRBuilder<> &B, llvm::Value *uplo, bool byRef,
2450 bool cublas);
2451llvm::Value *is_nonunit(llvm::IRBuilder<> &B, llvm::Value *uplo, bool byRef,
2452 bool cublas);
2453
2454llvm::Value *lookup_with_layout(llvm::IRBuilder<> &B, llvm::Type *fpType,
2455 llvm::Value *layout, llvm::Value *base,
2456 llvm::Value *lda, llvm::Value *row,
2457 llvm::Value *col);
2458
2459// first one assume V is an Integer
2460llvm::Value *transpose(std::string floatType, llvm::IRBuilder<> &B,
2461 llvm::Value *V, bool cublas);
2462// secon one assume V is an Integer or a ptr to an int (depends on byRef)
2463llvm::Value *transpose(std::string floatType, llvm::IRBuilder<> &B,
2464 llvm::Value *V, bool byRef, bool cublas,
2465 llvm::IntegerType *IT, llvm::IRBuilder<> &entryBuilder,
2466 const llvm::Twine &name);
2467llvm::SmallVector<llvm::Value *, 1>
2468get_blas_row(llvm::IRBuilder<> &B, llvm::ArrayRef<llvm::Value *> trans,
2469 llvm::ArrayRef<llvm::Value *> row,
2470 llvm::ArrayRef<llvm::Value *> col, bool byRef, bool cublas);
2471
2472llvm::SmallVector<llvm::Value *, 1>
2473get_blas_row(llvm::IRBuilder<> &B, llvm::ArrayRef<llvm::Value *> trans,
2474 bool byRef, bool cublas);
2475
2476#ifdef __clang__
2477#pragma clang diagnostic push
2478#pragma clang diagnostic ignored "-Wunused-variable"
2479#else
2480#pragma GCC diagnostic push
2481#pragma GCC diagnostic ignored "-Wunused-variable"
2482#endif
2483
2484// Parameter attributes from the original function/call that
2485// we should preserve on the primal of the derivative code.
2486static inline llvm::Attribute::AttrKind PrimalParamAttrsToPreserve[] = {
2487 llvm::Attribute::AttrKind::ReadOnly,
2488 llvm::Attribute::AttrKind::WriteOnly,
2489 llvm::Attribute::AttrKind::ZExt,
2490 llvm::Attribute::AttrKind::SExt,
2491 llvm::Attribute::AttrKind::InReg,
2492 llvm::Attribute::AttrKind::ByVal,
2493#if LLVM_VERSION_MAJOR >= 12
2494 llvm::Attribute::AttrKind::ByRef,
2495#endif
2496 llvm::Attribute::AttrKind::Preallocated,
2497 llvm::Attribute::AttrKind::InAlloca,
2498#if LLVM_VERSION_MAJOR >= 13
2499 llvm::Attribute::AttrKind::ElementType,
2500#endif
2501#if LLVM_VERSION_MAJOR >= 15
2502 llvm::Attribute::AttrKind::AllocAlign,
2503#endif
2504 llvm::Attribute::AttrKind::NoFree,
2505 llvm::Attribute::AttrKind::Alignment,
2506 llvm::Attribute::AttrKind::StackAlignment,
2507#if LLVM_VERSION_MAJOR >= 20
2508 llvm::Attribute::AttrKind::Captures,
2509#else
2510 llvm::Attribute::AttrKind::NoCapture,
2511#endif
2512 llvm::Attribute::AttrKind::ReadNone
2513};
2514
2515// Parameter attributes from the original function/call that
2516// we should preserve on the shadow of the derivative code.
2517// Note that this will not occur on vectore > 1.
2518static inline llvm::Attribute::AttrKind ShadowParamAttrsToPreserve[] = {
2519 llvm::Attribute::AttrKind::ZExt,
2520 llvm::Attribute::AttrKind::SExt,
2521#if LLVM_VERSION_MAJOR >= 13
2522 llvm::Attribute::AttrKind::ElementType,
2523#endif
2524 llvm::Attribute::AttrKind::NoFree,
2525 llvm::Attribute::AttrKind::Alignment,
2526 llvm::Attribute::AttrKind::StackAlignment,
2527#if LLVM_VERSION_MAJOR >= 20
2528 llvm::Attribute::AttrKind::Captures,
2529#else
2530 llvm::Attribute::AttrKind::NoCapture,
2531#endif
2532 llvm::Attribute::AttrKind::ReadNone,
2533};
2534#ifdef __clang__
2535#pragma clang diagnostic pop
2536#else
2537#pragma GCC diagnostic pop
2538#endif
2539
2540static inline llvm::Function *
2541getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id,
2542 llvm::ArrayRef<llvm::Type *> Tys = {}) {
2543#if LLVM_VERSION_MAJOR >= 20
2544 return llvm::Intrinsic::getOrInsertDeclaration(M, id, Tys);
2545#else
2546 return llvm::Intrinsic::getDeclaration(M, id, Tys);
2547#endif
2548}
2549
2550static inline llvm::Instruction *getFirstNonPHI(llvm::BasicBlock *B) {
2551#if LLVM_VERSION_MAJOR >= 18
2552 // NOLINTNEXTLINE(enzyme-first-non-phi): this is the wrapper
2553 return &*B->getFirstNonPHIIt();
2554#else
2555 // NOLINTNEXTLINE(enzyme-first-non-phi): this is the wrapper
2556 return B->getFirstNonPHI();
2557#endif
2558}
2559
2560// LLVM 24 dropped the llvm::Instruction* overloads of insertBefore and
2561// moveBefore in favour of ones taking a position in the instruction list.
2562static inline void insertBeforeInst(llvm::Instruction *I,
2563 llvm::Instruction *Before) {
2564#if LLVM_VERSION_MAJOR >= 24
2565 I->insertBefore(Before->getIterator());
2566#else
2567 I->insertBefore(Before);
2568#endif
2569}
2570
2571static inline void moveBeforeInst(llvm::Instruction *I,
2572 llvm::Instruction *Before) {
2573#if LLVM_VERSION_MAJOR >= 24
2574 I->moveBefore(Before->getIterator());
2575#else
2576 I->moveBefore(Before);
2577#endif
2578}
2579
2580static inline llvm::Instruction *getFirstNonPHIOrDbg(llvm::BasicBlock *B) {
2581#if LLVM_VERSION_MAJOR >= 20
2582 // NOLINTNEXTLINE(enzyme-first-non-phi): this is the wrapper
2583 return &*B->getFirstNonPHIOrDbg();
2584#else
2585 // NOLINTNEXTLINE(enzyme-first-non-phi): this is the wrapper
2586 return B->getFirstNonPHIOrDbg();
2587#endif
2588}
2589
2590static inline llvm::Instruction *
2591getFirstNonPHIOrDbgOrLifetime(llvm::BasicBlock *B) {
2592#if LLVM_VERSION_MAJOR >= 20
2593 return &*B->getFirstNonPHIOrDbgOrLifetime();
2594#else
2595 return B->getFirstNonPHIOrDbgOrLifetime();
2596#endif
2597}
2598
2599static inline void addCallSiteNoCapture(llvm::CallBase *call, size_t idx) {
2600#if LLVM_VERSION_MAJOR > 20
2601 call->addParamAttr(
2602 idx, llvm::Attribute::get(call->getContext(), llvm::Attribute::Captures,
2603 llvm::CaptureInfo::none().toIntValue()));
2604#else
2605 call->addParamAttr(idx, llvm::Attribute::NoCapture);
2606#endif
2607}
2608
2609static inline void addFunctionNoCapture(llvm::Function *call, size_t idx) {
2610#if LLVM_VERSION_MAJOR > 20
2611 call->addParamAttr(
2612 idx, llvm::Attribute::get(call->getContext(), llvm::Attribute::Captures,
2613 llvm::CaptureInfo::none().toIntValue()));
2614#else
2615 call->addParamAttr(idx, llvm::Attribute::NoCapture);
2616#endif
2617}
2618
2619[[nodiscard]] static inline llvm::AttributeList
2620addFunctionNoCapture(llvm::LLVMContext &ctx, llvm::AttributeList list,
2621 size_t idx) {
2622 unsigned idxs = {(unsigned)idx};
2623#if LLVM_VERSION_MAJOR > 20
2624 return list.addParamAttribute(
2625 ctx, idxs,
2626 llvm::Attribute::get(ctx, llvm::Attribute::Captures,
2627 llvm::CaptureInfo::none().toIntValue()));
2628#else
2629 return list.addParamAttribute(ctx, idxs, llvm::Attribute::NoCapture);
2630#endif
2631}
2632
2633static inline llvm::Type *getSubType(llvm::Type *T) { return T; }
2634
2635template <typename Arg1, typename... Args>
2636static inline llvm::Type *getSubType(llvm::Type *T, Arg1 i, Args... args) {
2637 if (auto AT = llvm::dyn_cast<llvm::ArrayType>(T))
2638 return getSubType(AT->getElementType(), args...);
2639 if (auto VT = llvm::dyn_cast<llvm::VectorType>(T))
2640 return getSubType(VT->getElementType(), args...);
2641 if (auto ST = llvm::dyn_cast<llvm::StructType>(T)) {
2642 assert((int)i != -1);
2643 return getSubType(ST->getElementType(i), args...);
2644 }
2645 llvm::errs() << *T << "\n";
2646 llvm_unreachable("unknown subtype");
2647}
2648
2659 unsigned count = 0;
2660 bool all = true;
2661 bool derived = false;
2662 CountTrackedPointers(llvm::Type *T);
2663};
2664static inline bool isSpecialPtr(llvm::Type *Ty) {
2665 llvm::PointerType *PTy = llvm::dyn_cast<llvm::PointerType>(Ty);
2666 if (!PTy)
2667 return false;
2668 unsigned AS = PTy->getAddressSpace();
2670}
2671
2672#if LLVM_VERSION_MAJOR >= 20
2673bool collectOffset(
2674 llvm::GEPOperator *gep, const llvm::DataLayout &DL, unsigned BitWidth,
2675 llvm::SmallMapVector<llvm::Value *, llvm::APInt, 4> &VariableOffsets,
2676 llvm::APInt &ConstantOffset);
2677#else
2678bool collectOffset(llvm::GEPOperator *gep, const llvm::DataLayout &DL,
2679 unsigned BitWidth,
2680 llvm::MapVector<llvm::Value *, llvm::APInt> &VariableOffsets,
2681 llvm::APInt &ConstantOffset);
2682#endif
2683
2684llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &B,
2685 llvm::Intrinsic::ID ID, llvm::Type *RetTy,
2686 llvm::ArrayRef<llvm::Value *> Args,
2687 llvm::Instruction *FMFSource = nullptr,
2688 const llvm::Twine &Name = "");
2689
2690bool isNVLoad(const llvm::Value *V);
2691
2692//! Check if value if b captured after definition before executing inst.
2693//! If checkLoadCaptured != 0, also consider catpures of any loads of the value
2694//! as a capture (for the number of loads set). With TLI, passing the value to
2695//! a deallocation function is not a capture.
2696bool notCapturedBefore(llvm::Value *V, llvm::Instruction *inst,
2697 size_t checkLoadCaptured,
2698 llvm::Instruction *startinst = nullptr,
2699 llvm::TargetLibraryInfo *TLI = nullptr);
2700
2701//! Check if value if b captured
2702bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI = nullptr);
2703
2704// Return true if guaranteed not to alias
2705// Return false if guaranteed to alias [with possible offset depending on flag].
2706// Return {} if no information is given.
2707#if LLVM_VERSION_MAJOR >= 16
2708std::optional<bool>
2709#else
2710llvm::Optional<bool>
2711#endif
2712arePointersGuaranteedNoAlias(llvm::TargetLibraryInfo &TLI, llvm::AAResults &AA,
2713 llvm::DominatorTree &DT, llvm::LoopInfo &LI,
2714 llvm::Value *op0, llvm::Value *op1,
2715 bool offsetAllowed = false);
2716
2717/// MPI routines recognized by Enzyme, keyed by their lowercase,
2718/// underscore-free base name and mapped to the canonical C name (without the
2719/// profiling "P" prefix).
2720static const std::pair<const char *, const char *> KnownMPIFunctions[] = {
2721 {"abort", "MPI_Abort"},
2722 {"allgather", "MPI_Allgather"},
2723 {"allreduce", "MPI_Allreduce"},
2724 {"barrier", "MPI_Barrier"},
2725 {"bcast", "MPI_Bcast"},
2726 {"brecv", "MPI_Brecv"},
2727 {"bsend", "MPI_Bsend"},
2728 {"comm_accept", "MPI_Comm_accept"},
2729 {"comm_call_errhandler", "MPI_Comm_call_errhandler"},
2730 {"comm_compare", "MPI_Comm_compare"},
2731 {"comm_connect", "MPI_Comm_connect"},
2732 {"comm_create", "MPI_Comm_create"},
2733 {"comm_create_errhandler", "MPI_Comm_create_errhandler"},
2734 {"comm_create_group", "MPI_Comm_create_group"},
2735 {"comm_disconnect", "MPI_Comm_disconnect"},
2736 {"comm_dup", "MPI_Comm_dup"},
2737 {"comm_free", "MPI_Comm_free"},
2738 {"comm_get_info", "MPI_Comm_get_info"},
2739 {"comm_get_name", "MPI_Comm_get_name"},
2740 {"comm_get_parent", "MPI_Comm_get_parent"},
2741 {"comm_idup", "MPI_Comm_idup"},
2742 {"comm_join", "MPI_Comm_join"},
2743 {"comm_rank", "MPI_Comm_rank"},
2744 {"comm_remote_size", "MPI_Comm_remote_size"},
2745 {"comm_set_info", "MPI_Comm_set_info"},
2746 {"comm_set_name", "MPI_Comm_set_name"},
2747 {"comm_size", "MPI_Comm_size"},
2748 {"comm_spawn", "MPI_Comm_spawn"},
2749 {"comm_spawn_multiple", "MPI_Comm_spawn_multiple"},
2750 {"comm_split", "MPI_Comm_split"},
2751 {"finalize", "MPI_Finalize"},
2752 {"gather", "MPI_Gather"},
2753 {"get_count", "MPI_Get_count"},
2754 {"get_processor_name", "MPI_Get_processor_name"},
2755 {"graph_create", "MPI_Graph_create"},
2756 {"init", "MPI_Init"},
2757 {"intercomm_create", "MPI_Intercomm_create"},
2758 {"irecv", "MPI_Irecv"},
2759 {"isend", "MPI_Isend"},
2760 {"op_create", "MPI_Op_create"},
2761 {"probe", "MPI_Probe"},
2762 {"recv", "MPI_Recv"},
2763 {"reduce", "MPI_Reduce"},
2764 {"reduce_scatter_block", "MPI_Reduce_scatter_block"},
2765 {"scatter", "MPI_Scatter"},
2766 {"send", "MPI_Send"},
2767 {"ssend", "MPI_Ssend"},
2768 {"test", "MPI_Test"},
2769 {"type_size", "MPI_Type_size"},
2770 {"wait", "MPI_Wait"},
2771 {"waitall", "MPI_Waitall"},
2772 {"wtime", "MPI_Wtime"},
2773};
2774
2775/// Canonicalize the name of an MPI routine across calling conventions: both
2776/// the C convention ("MPI_Recv", "PMPI_Recv") and common Fortran ABI
2777/// manglings ("mpi_recv_", "mpi_recv__", "pmpi_recv_", "MPI_RECV", ...) map
2778/// to the canonical C name without profiling prefix (e.g. "MPI_Recv").
2779/// Returns an empty StringRef if the name is not a known MPI routine.
2780static inline llvm::StringRef canonicalizeMPIName(llvm::StringRef Name) {
2781 llvm::StringRef Base = Name;
2782 if (!Base.consume_front_insensitive("pmpi_") &&
2783 !Base.consume_front_insensitive("mpi_"))
2784 return "";
2785 while (Base.consume_back("_")) {
2786 }
2787 if (Base.empty())
2788 return "";
2789 for (const auto &E : KnownMPIFunctions)
2790 if (Base.equals_insensitive(E.first))
2791 return E.second;
2792 return "";
2793}
2794
2795/// True if `Name` uses a Fortran ABI mangling of an MPI routine (trailing
2796/// underscore(s), e.g. "mpi_recv_", "mpi_comm_rank__"). Such calls pass all
2797/// arguments by reference and take an extra trailing `ierr` argument.
2798static inline bool isFortranMPICall(llvm::StringRef Name) {
2799 return endsWith(Name, "_") && !canonicalizeMPIName(Name).empty();
2800}
2801
2802static inline std::tuple<llvm::StringRef, llvm::StringRef, llvm::StringRef>
2803tripleSplitDollar(llvm::StringRef caller) {
2804 if (!startsWith(caller, "ejl")) {
2805 return {"", caller, ""};
2806 }
2807 auto &&[prefix, todo] = caller.split("$");
2808 auto &&[name, postfix] = todo.split("$");
2809 return std::make_tuple(prefix, name, postfix);
2810}
2811
2812static inline std::string getRenamedPerCallingConv(llvm::StringRef caller,
2813 llvm::StringRef callee) {
2814 if (startsWith(caller, "ejl")) {
2815 auto &&[prefix, name, postfix] = tripleSplitDollar(caller);
2816 return (prefix + "$" + getRenamedPerCallingConv(name, callee) + "$" +
2817 postfix)
2818 .str();
2819 }
2820 if (startsWith(caller, "PMPI_")) {
2821 assert(startsWith(callee, "MPI"));
2822 return ("P" + callee).str();
2823 }
2824 // A caller using a Fortran MPI ABI convention (e.g. "mpi_reduce_") needs
2825 // the callee mangled the same way so that the call resolves against the
2826 // MPI library's Fortran bindings (e.g. "mpi_bcast_").
2827 if (startsWith(callee, "MPI_") && isFortranMPICall(caller)) {
2828 bool profiling = caller.substr(0, 5).equals_insensitive("pmpi_");
2829 size_t underscores = 0;
2830 while (underscores < caller.size() &&
2831 caller[caller.size() - 1 - underscores] == '_')
2832 underscores++;
2833 std::string result = profiling ? "pmpi_" : "mpi_";
2834 result += callee.drop_front(strlen("MPI_")).lower();
2835 result.append(underscores, '_');
2836 return result;
2837 }
2838 return callee.str();
2839}
2840
2841/// Declare `callee` in `M` under whatever naming convention the frontend used
2842/// to reach `templateFn`, recording the plain name in enzyme_math so that the
2843/// declaration is still recognized afterwards. A helper introduced next to a
2844/// call has to follow that call's convention to resolve at link time: Julia,
2845/// for one, names its lazily bound ccalls "ejlstr$<function>$<library>" and
2846/// loads those libraries RTLD_LOCAL, so a plainly named declaration would not
2847/// be reachable via dlsym.
2848llvm::FunctionCallee getOrInsertPerCallingConv(llvm::Module &M,
2849 llvm::Function *templateFn,
2850 llvm::StringRef callee,
2851 llvm::FunctionType *FT);
2852
2853static inline std::string convertSRetTypeToString(llvm::Type *T) {
2854 return std::to_string((size_t)T);
2855}
2856
2857static inline llvm::Type *
2858convertSRetTypeFromString(llvm::StringRef str, llvm::LLVMContext *C = nullptr) {
2859 if (str == "test_type") {
2860 assert(C);
2861 llvm::SmallVector<llvm::Type *, 1> elts;
2862 elts.push_back(
2863 getPointerType(llvm::StructType::get(*C, {}), AddressSpace::Tracked));
2864 llvm::Type *inner = llvm::StructType::get(*C, elts);
2865 llvm::SmallVector<llvm::Type *, 1> innerElts;
2866 innerElts.push_back(inner);
2867 return llvm::StructType::get(*C, innerElts);
2868 }
2869 if (str == "test_type2") {
2870 assert(C);
2871 return llvm::ArrayType::get(llvm::Type::getInt64Ty(*C), 6);
2872 }
2873 if (str == "test_type3") {
2874 assert(C);
2875 llvm::SmallVector<llvm::Type *, 1> elts;
2876 elts.push_back(llvm::Type::getDoubleTy(*C));
2877 return llvm::StructType::get(*C, elts);
2878 }
2879 if (str == "test_type4") {
2880 assert(C);
2881 llvm::SmallVector<llvm::Type *, 3> elts;
2882 elts.push_back(llvm::ArrayType::get(llvm::Type::getDoubleTy(*C), 2));
2883 elts.push_back(llvm::Type::getDoubleTy(*C));
2884 elts.push_back(llvm::Type::getInt64Ty(*C));
2885 return llvm::StructType::get(*C, elts);
2886 }
2887 if (str == "test_type5") {
2888 assert(C);
2889 llvm::SmallVector<llvm::Type *, 3> elts;
2890 elts.push_back(llvm::ArrayType::get(llvm::Type::getDoubleTy(*C), 1));
2891 elts.push_back(llvm::Type::getDoubleTy(*C));
2892 elts.push_back(llvm::Type::getInt64Ty(*C));
2893 return llvm::StructType::get(*C, elts);
2894 }
2895 if (str == "test_type6") {
2896 assert(C);
2897 llvm::SmallVector<llvm::Type *, 2> elts;
2898 elts.push_back(
2899 getPointerType(llvm::StructType::get(*C, {}), AddressSpace::Tracked));
2900 elts.push_back(llvm::Type::getInt64Ty(*C));
2901 return llvm::StructType::get(*C, elts);
2902 }
2903 size_t idx;
2904 bool failed = str.consumeInteger(10, idx);
2905 (void)failed;
2906 assert(!failed);
2907 return (llvm::Type *)idx;
2908}
2909
2910static inline size_t convertRRootCountFromString(llvm::StringRef str) {
2911 size_t idx;
2912 bool failed = str.consumeInteger(10, idx);
2913 (void)failed;
2914 assert(!failed);
2915 return idx;
2916}
2917
2918static inline bool hasSRetRRootsOrUnionSRet(llvm::CallBase *CB) {
2919 if (CB->hasStructRetAttr())
2920 return true;
2921 for (size_t i = 0; i < CB->arg_size(); i++) {
2922 if (CB->getAttributeAtIndex(llvm::AttributeList::FirstArgIndex + i,
2923 "enzymejl_sret_union_bytes")
2924 .isValid())
2925 return true;
2926 if (CB->getAttributeAtIndex(llvm::AttributeList::FirstArgIndex + i,
2927 "enzymejl_returnRoots")
2928 .isValid())
2929 return true;
2930 }
2931 return false;
2932}
2933
2941
2942llvm::Value *moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype,
2943 llvm::Value *sret, llvm::Type *root_ty,
2944 llvm::Value *rootRet, size_t rootOffset,
2945 SRetRootMovement direction);
2946
2947void copyNonJLValueInto(llvm::IRBuilder<> &B, llvm::Type *curType,
2948 llvm::Type *dstType, llvm::Value *dst,
2949 llvm::ArrayRef<unsigned> dstPrefix, llvm::Type *srcType,
2950 llvm::Value *src, llvm::ArrayRef<unsigned> srcPrefix,
2951 bool shouldZero);
2952
2953static bool anyJuliaObjects(llvm::Type *T) {
2954 if (isSpecialPtr(T))
2955 return true;
2956 if (auto ST = llvm::dyn_cast<llvm::StructType>(T)) {
2957 for (auto elem : ST->elements()) {
2958 if (anyJuliaObjects(elem))
2959 return true;
2960 }
2961 return false;
2962 }
2963 if (auto AT = llvm::dyn_cast<llvm::ArrayType>(T)) {
2964 return anyJuliaObjects(AT->getElementType());
2965 }
2966 if (auto VT = llvm::dyn_cast<llvm::VectorType>(T)) {
2967 return anyJuliaObjects(VT->getElementType());
2968 }
2969 return false;
2970}
2971
2972llvm::SmallVector<llvm::Value *, 1> getJuliaObjects(llvm::Value *v,
2973 llvm::IRBuilder<> &B);
2974
2975// Find all user instructions of AI, returning tuples of <instruction, value,
2976// byte offet from AI> Unlike a simple get users, this will recurse through any
2977// constant gep offsets and casts
2978llvm::SmallVector<std::tuple<llvm::Instruction *, llvm::Value *, size_t>, 1>
2979findAllUsersOf(llvm::Value *AI);
2980
2981static bool hasTerminator(llvm::BasicBlock *BB) {
2982#if LLVM_VERSION_MAJOR >= 23
2983 return BB->hasTerminator();
2984#else
2985 return BB->getTerminator();
2986#endif
2987}
2988
2989/// The value of the add recurrence AR at iteration It, like
2990/// SCEVAddRecExpr::evaluateAtIteration, but without the widening that its
2991/// BinomialCoefficient performs: LLVM computes I*(I-1)/2 in a type one bit
2992/// wider than AR so that the halving is exact, and SCEVExpander then has to
2993/// materialize a canonical induction variable of that wider type, a new
2994/// loop-carried phi. Here the halving is done on whichever of I and I-1 is
2995/// even, in the wider of AR's and It's own types, so that every product is of
2996/// exact integers and no new type appears. Recurrences of degree at most
2997/// three are handled this way; higher degrees fall back to LLVM's version.
2998const llvm::SCEV *evaluateAtIterationWithoutExt(const llvm::SCEVAddRecExpr *AR,
2999 const llvm::SCEV *It,
3000 llvm::ScalarEvolution &SE);
3001
3002#endif // ENZYME_UTILS_H
DIFFE_TYPE
Potential differentiable argument classifications.
static std::string str(AugmentedStruct c)
Definition EnzymeLogic.h:62
static llvm::Value * checkedMul(bool strongZero, llvm::IRBuilder<> &Builder2, llvm::Value *idiff, llvm::Value *pres, const llvm::Twine &Name="")
Definition Utils.h:2296
llvm::Value * EmitNoDerivativeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &B, llvm::Value *condition=nullptr)
Definition Utils.cpp:4593
static llvm::StringRef getFuncName(llvm::Function *called)
Definition Utils.h:1461
static bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II)
Definition Utils.h:1649
llvm::Function * getOrInsertDifferentialFloatMemcpy(llvm::Module &M, llvm::Type *T, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
Create function for type that performs the derivative memcpy on floating point memory.
llvm::Function * getOrInsertDifferentialWaitallSave(llvm::Module &M, llvm::ArrayRef< llvm::Type * > T, llvm::PointerType *reqType)
static void allDomPredecessorsOf(llvm::Instruction *inst, llvm::DominatorTree &DT, llvm::function_ref< bool(llvm::Instruction *)> f)
Call the function f for all instructions that happen before inst If the function returns true,...
Definition Utils.h:1085
llvm::Function * getOrInsertMemcpyMat(llvm::Module &M, llvm::Type *elementType, llvm::PointerType *PT, llvm::IntegerType *IT, unsigned dstalign, unsigned srcalign)
Turned out to be a faster alternatives to lapacks lacpy function.
llvm::Value * load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType, llvm::Value *V, bool byRef)
Definition Utils.cpp:4370
bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI=nullptr)
Check if value if b captured.
Definition Utils.cpp:4956
static llvm::StringRef canonicalizeMPIName(llvm::StringRef Name)
Canonicalize the name of an MPI routine across calling conventions: both the C convention ("MPI_Recv"...
Definition Utils.h:2780
static bool isNoEscapingAllocation(const llvm::Function *F)
Definition Utils.h:2156
llvm::Value * is_nonunit(llvm::IRBuilder<> &B, llvm::Value *uplo, bool byRef, bool cublas)
static bool hasCustomRuleMetadata(const T *V)
Definition Utils.h:1626
static std::string getRenamedPerCallingConv(llvm::StringRef caller, llvm::StringRef callee)
Definition Utils.h:2812
bool isFlangRuntimeNoFree(llvm::StringRef name)
LLVM flang runtime functions that neither free memory of the program nor return or store an allocatio...
Definition Utils.cpp:115
bool isAtomic(llvm::Value *origptr, bool AtomicAdd, llvm::Function *newFunc)
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
Definition Utils.cpp:508
llvm::cl::opt< bool > EnzymeBlasCopy
llvm::SmallVector< llvm::Value *, 1 > get_blas_row(llvm::IRBuilder<> &B, llvm::ArrayRef< llvm::Value * > trans, llvm::ArrayRef< llvm::Value * > row, llvm::ArrayRef< llvm::Value * > col, bool byRef, bool cublas)
static llvm::Loop * getAncestor(llvm::Loop *R1, llvm::Loop *R2)
Definition Utils.h:1197
static llvm::Function * getDeallocatorFnFromCall(T *op)
Definition Utils.h:1561
BATCH_TYPE
Definition Utils.h:519
llvm::SmallVector< llvm::Instruction *, 2 > PostCacheStore(llvm::StoreInst *SI, llvm::IRBuilder<> &B)
Definition Utils.cpp:517
static llvm::Instruction * getNextNonDebugInstructionOrNull(llvm::Instruction *Z)
Get the next non-debug instruction, if one exists.
Definition Utils.h:462
static T max(T a, T b)
Pick the maximum value.
Definition Utils.h:406
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
llvm::PointerType * getDefaultAnonymousTapeType(llvm::LLVMContext &C)
Definition Utils.cpp:531
void callSPMVDiagUpdate(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::IntegerType *IT, llvm::Type *BlasCT, llvm::Type *BlasFPT, llvm::Type *BlasPT, llvm::Type *BlasIT, llvm::Type *fpTy, llvm::ArrayRef< llvm::Value * > args, const llvm::ArrayRef< llvm::OperandBundleDef > bundles, bool byRef, bool julia_decl)
static void dumpMap(const llvm::ValueMap< T, N > &o, llvm::function_ref< bool(const llvm::Value *)> shouldPrint=[](T) { return true;})
Print a map, optionally with a shouldPrint function to decide to print a given value.
Definition Utils.h:430
bool attributeKnownFunctions(llvm::Function &F)
Definition Utils.cpp:139
static bool anyJuliaObjects(llvm::Type *T)
Definition Utils.h:2953
ReturnType
Potential return type of generated functions.
Definition Utils.h:499
@ Tape
Return is a tape type.
@ ArgsWithTwoReturns
Return is a struct of all args and two of the original return.
@ TapeAndReturn
Return is a tape type and the original return.
@ Args
Return is a struct of all args.
@ TapeAndTwoReturns
Return is a tape type and the two of the original return.
@ ArgsWithReturn
Return is a struct of all args and the original return.
static V * findInMap(std::map< K, V > &map, K key)
Definition Utils.h:999
llvm::Value * is_left(llvm::IRBuilder<> &B, llvm::Value *side, bool byRef, bool cublas)
static bool isNoAlias(const llvm::CallBase *call)
Definition Utils.h:2127
static llvm::Type * FloatToIntTy(llvm::Type *T)
Convert a floating point type to an integer type of the same size.
Definition Utils.h:775
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
Definition Utils.h:847
llvm::Value * CreateReAllocation(llvm::IRBuilder<> &B, llvm::Value *prev, llvm::Type *T, llvm::Value *OuterCount, llvm::Value *InnerCount, const llvm::Twine &Name="", llvm::CallInst **caller=nullptr, bool ZeroMem=false)
Definition Utils.cpp:685
static unsigned getGPUSharedAddrSpace(const llvm::Triple &TT)
Definition Utils.h:169
llvm::Function * getOrInsertDifferentialMPI_Wait(llvm::Module &M, llvm::ArrayRef< llvm::Type * > T, llvm::Type *reqType, llvm::StringRef caller)
Create function for type that performs the derivative MPI_Wait.
void EmitNoTypeError(const std::string &, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &B)
Definition Utils.cpp:4675
static size_t convertRRootCountFromString(llvm::StringRef str)
Definition Utils.h:2910
static bool isGPUArch(const llvm::Triple &TT)
Definition Utils.h:162
llvm::Value * CreateAllocation(llvm::IRBuilder<> &B, llvm::Type *T, llvm::Value *Count, const llvm::Twine &Name="", llvm::CallInst **caller=nullptr, llvm::Instruction **ZeroMem=nullptr, bool isDefault=false)
llvm::CallInst * getorInsertInnerProd(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::IntegerType *IT, llvm::Type *BlasPT, llvm::Type *BlasIT, llvm::Type *fpTy, llvm::ArrayRef< llvm::Value * > args, const llvm::ArrayRef< llvm::OperandBundleDef > bundles, bool byRef, bool cublas, bool julia_decl)
void mayExecuteAfter(llvm::SmallVectorImpl< llvm::Instruction * > &results, llvm::Instruction *inst, const llvm::SetVector< llvm::Instruction * > &stores, const llvm::Loop *region)
static void allPredecessorsOf(llvm::Instruction *inst, llvm::function_ref< bool(llvm::Instruction *)> f)
Call the function f for all instructions that happen before inst If the function returns true,...
Definition Utils.h:1048
llvm::Value * get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res)
Definition Utils.cpp:4570
void callMemcpyStridedLapack(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::ArrayRef< llvm::Value * > args, llvm::ArrayRef< llvm::OperandBundleDef > bundles)
Create function for type that performs memcpy using lapack copy.
Definition Utils.cpp:1546
static llvm::PointerType * getUnqual(llvm::Type *T)
Definition Utils.h:1329
llvm::CallInst * createIntrinsicCall(llvm::IRBuilderBase &B, llvm::Intrinsic::ID ID, llvm::Type *RetTy, llvm::ArrayRef< llvm::Value * > Args, llvm::Instruction *FMFSource=nullptr, const llvm::Twine &Name="")
Definition Utils.cpp:4528
llvm::Value * nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V)
Create function to computer nearest power of two.
Definition Utils.cpp:2415
llvm::Function * getOrInsertCheckedFree(llvm::Module &M, llvm::CallInst *call, llvm::Type *Type, unsigned width)
llvm::Value * transpose(std::string floatType, llvm::IRBuilder<> &B, llvm::Value *V, bool cublas)
static llvm::Value * getMPIMemberPtr(llvm::IRBuilder<> &B, llvm::Value *V, llvm::Type *T)
Definition Utils.h:1398
static bool isCertainPrint(const llvm::StringRef name)
Definition Utils.h:863
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask=nullptr)
Definition Utils.cpp:3929
void addValueToCache(llvm::Value *arg, bool cache_arg, llvm::Type *ty, llvm::SmallVectorImpl< llvm::Value * > &cacheValues, llvm::IRBuilder<> &BuilderZ, const llvm::Twine &name="")
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
llvm::Optional< BlasInfo > extractBLAS(llvm::StringRef in)
Definition Utils.cpp:3871
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
Definition Utils.h:1324
static std::map< K, V >::iterator insert_or_assign2(std::map< K, V > &map, K key, V val)
Insert into a map.
Definition Utils.h:990
static uint64_t getAggregateElementOffset(const llvm::DataLayout &DL, llvm::Type *T, llvm::ArrayRef< unsigned > Idxs)
The byte offset of the aggregate element addressed by Idxs within T, that is, what gep T,...
Definition Utils.h:1354
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
Definition Utils.h:2541
llvm::Value * is_normal(llvm::IRBuilder<> &B, llvm::Value *trans, bool byRef, bool cublas)
llvm::Function * getOrInsertDifferentialFloatMemmove(llvm::Module &M, llvm::Type *T, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
Create function for type that performs the derivative memmove on floating point memory.
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Definition Utils.cpp:62
llvm::Value * getOrInsertOpFloatSum(llvm::Module &M, llvm::Function *templateFn, llvm::Type *OpPtr, llvm::Type *OpType, ConcreteType CT, llvm::Type *intType, llvm::IRBuilder<> &B2)
templateFn is the MPI entry point this reduction is being built for; the MPI_Op_create it needs is na...
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
Definition Utils.h:2109
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
Definition Utils.h:2609
static llvm::Type * convertSRetTypeFromString(llvm::StringRef str, llvm::LLVMContext *C=nullptr)
Definition Utils.h:2858
static bool isDebugFunction(llvm::Function *called)
Definition Utils.h:824
static void insertBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
Definition Utils.h:2562
static bool isReturned(llvm::Instruction *inst)
Check whether this instruction is returned.
Definition Utils.h:765
std::vector< std::tuple< llvm::Type *, size_t, size_t > > parseTrueType(const llvm::MDNode *, DerivativeMode, bool const_src)
Definition Utils.cpp:4749
static bool isPointerArithmeticInst(const llvm::Value *V, bool includephi=true, bool includebin=true)
Definition Utils.h:1660
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1711
bool isNVLoad(const llvm::Value *V)
Definition Utils.cpp:4821
static llvm::Function * isCalledFunction(llvm::Value *val)
Definition Utils.h:383
static bool isFortranMPICall(llvm::StringRef Name)
True if Name uses a Fortran ABI mangling of an MPI routine (trailing underscore(s),...
Definition Utils.h:2798
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
Definition Utils.h:483
static bool containsOnlyAtMostTopBit(const llvm::Value *V, llvm::Type *FT, const llvm::DataLayout &dl, llvm::Type **vFT=nullptr)
Definition Utils.h:2325
static llvm::PointerType * changePointerAddrSpace(llvm::PointerType *PT, unsigned AddressSpace)
The same pointer type, in a different address space.
Definition Utils.h:1338
bool writesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter)
Return whether maybeReader can read from memory written to by maybeWriter.
Definition Utils.cpp:3155
llvm::Value * lookup_with_layout(llvm::IRBuilder<> &B, llvm::Type *fpType, llvm::Value *layout, llvm::Value *base, llvm::Value *lda, llvm::Value *row, llvm::Value *col)
static bool shouldDisableNoWrite(const llvm::CallInst *CI)
Definition Utils.h:1633
static llvm::SetVector< llvm::Value * > getBaseObjects(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1847
MPI_Elem
Definition Utils.h:1298
bool overwritesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, llvm::ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter, llvm::Loop *scope=nullptr)
Definition Utils.cpp:3040
void EmitWarning(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::BasicBlock *BB, const Args &...args)
Definition Utils.h:252
static bool isReadOnlyOrThrow(const llvm::Function *F)
Definition Utils.h:2004
static bool isSpecialPtr(llvm::Type *Ty)
Definition Utils.h:2664
static llvm::Instruction * getFirstNonPHI(llvm::BasicBlock *B)
Definition Utils.h:2550
void callMemcpyStridedBlas(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::ArrayRef< llvm::Value * > args, llvm::Type *cublas_retty, llvm::ArrayRef< llvm::OperandBundleDef > bundles)
Create function for type that performs memcpy with a stride using blas copy.
Definition Utils.cpp:1526
ProbProgMode
Definition Utils.h:533
static bool hasNoCache(llvm::Value *op)
Definition Utils.h:1484
static llvm::StructType * getMPIHelper(llvm::LLVMContext &Context)
Definition Utils.h:1381
llvm::CallInst * CreateDealloc(llvm::IRBuilder<> &B, llvm::Value *ToFree)
Definition Utils.cpp:871
const llvm::SCEV * evaluateAtIterationWithoutExt(const llvm::SCEVAddRecExpr *AR, const llvm::SCEV *It, llvm::ScalarEvolution &SE)
The value of the add recurrence AR at iteration It, like SCEVAddRecExpr::evaluateAtIteration,...
static bool isReadOnly(const llvm::Function *F, ssize_t arg=-1)
Definition Utils.h:1883
AddressSpace
Definition Utils.h:2649
@ CalleeRooted
Definition Utils.h:2653
@ Derived
Definition Utils.h:2652
@ LastSpecial
Definition Utils.h:2656
@ Tracked
Definition Utils.h:2651
@ FirstSpecial
Definition Utils.h:2655
@ Generic
Definition Utils.h:2650
@ Loaded
Definition Utils.h:2654
llvm::MDNode * sliceTrueType(const llvm::MDNode *md, size_t start, size_t length)
Restrict an enzyme_truetype annotation describing a memory region to the byte range [start,...
Definition Utils.cpp:4713
static llvm::Type * getSubType(llvm::Type *T)
Definition Utils.h:2633
static llvm::Instruction * getNextNonDebugInstruction(llvm::Instruction *Z)
Get the next non-debug instruction, erring if none exists.
Definition Utils.h:471
llvm::Optional< bool > arePointersGuaranteedNoAlias(llvm::TargetLibraryInfo &TLI, llvm::AAResults &AA, llvm::DominatorTree &DT, llvm::LoopInfo &LI, llvm::Value *op0, llvm::Value *op1, bool offsetAllowed=false)
llvm::StringMap< std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::CallInst *, llvm::ArrayRef< llvm::Value * >, GradientUtils *)> > shadowHandlers
static std::vector< ssize_t > getDeallocationIndicesFromCall(T *op)
Definition Utils.h:1582
llvm::Value * moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype, llvm::Value *sret, llvm::Type *root_ty, llvm::Value *rootRet, size_t rootOffset, SRetRootMovement direction)
Definition Utils.cpp:5148
static void allUnsyncdPredecessorsOf(llvm::Instruction *inst, llvm::function_ref< bool(llvm::Instruction *)> f, llvm::function_ref< void()> preEntry)
Call the function f for all instructions that happen before inst If the function returns true,...
Definition Utils.h:1124
static void allInstructionsBetween(llvm::LoopInfo &LI, llvm::Instruction *inst1, llvm::Instruction *inst2, llvm::function_ref< bool(llvm::Instruction *)> f)
Call the function f for all instructions that happen between inst1 and inst2 If the function returns ...
Definition Utils.h:1243
void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal, llvm::Value *shadow, const char *Message, llvm::DebugLoc &&loc, llvm::Instruction *orig)
Definition Utils.cpp:1031
llvm::cl::opt< bool > EnzymeJuliaAddrLoad
MPI_CallType
Definition Utils.h:1293
static void dumpSet(const llvm::SmallPtrSetImpl< T * > &o)
Print a set.
Definition Utils.h:445
static bool isConditionalBranch(const llvm::Value *V)
Definition Utils.h:65
static llvm::Function * getFunctionFromCall(T *op)
Definition Utils.h:1437
static llvm::Value * getBranchCondition(llvm::Value *V)
The condition of a branch isConditionalBranch says yes to.
Definition Utils.h:100
bool collectOffset(llvm::GEPOperator *gep, const llvm::DataLayout &DL, unsigned BitWidth, llvm::MapVector< llvm::Value *, llvm::APInt > &VariableOffsets, llvm::APInt &ConstantOffset)
llvm::Value * get_cached_mat_width(llvm::IRBuilder<> &B, llvm::ArrayRef< llvm::Value * > trans, llvm::Value *arg_ld, llvm::Value *dim_1, llvm::Value *dim_2, bool cacheMat, bool byRef, bool cublas)
Definition Utils.cpp:4313
ErrorType
Definition Utils.h:189
@ IllegalFirstPointer
@ ShowInternalError
@ MixedActivityError
@ IllegalReplaceFicticiousPHIs
@ TypeDepthExceeded
@ IllegalTypeAnalysis
static llvm::Value * checkedDiv(bool strongZero, llvm::IRBuilder<> &Builder2, llvm::Value *idiff, llvm::Value *pres, const llvm::Twine &Name="")
Definition Utils.h:2310
static T min(T a, T b)
Pick the maximum value.
Definition Utils.h:412
static bool hasSRetRRootsOrUnionSRet(llvm::CallBase *CB)
Definition Utils.h:2918
static std::tuple< llvm::StringRef, llvm::StringRef, llvm::StringRef > tripleSplitDollar(llvm::StringRef caller)
Definition Utils.h:2803
static bool isUnconditionalBranch(const llvm::Value *V)
Definition Utils.h:75
llvm::SmallVector< llvm::Value *, 1 > getJuliaObjects(llvm::Value *v, llvm::IRBuilder<> &B)
Definition Utils.cpp:5347
SRetRootMovement
Definition Utils.h:2934
static void addCallSiteNoCapture(llvm::CallBase *call, size_t idx)
Definition Utils.h:2599
static llvm::Type * IntToFloatTy(llvm::Type *T)
Convert a integer type to a floating point type of the same size.
Definition Utils.h:799
static void resetClonedGUID(llvm::Function *F)
Definition Utils.h:91
void EmitWarningAlways(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Function &F, const Args &...args)
Emit a diagnostic which is always shown, unlike EmitWarning which requires the user to opt in with -R...
Definition Utils.h:289
static llvm::Instruction * getFirstNonPHIOrDbgOrLifetime(llvm::BasicBlock *B)
Definition Utils.h:2591
llvm::cl::opt< bool > EnzymePrintPerf
Print additional debug info relevant to performance.
static bool isAnyBranch(const llvm::Value *V)
Definition Utils.h:57
bool notCapturedBefore(llvm::Value *V, llvm::Instruction *inst, size_t checkLoadCaptured, llvm::Instruction *startinst=nullptr, llvm::TargetLibraryInfo *TLI=nullptr)
Check if value if b captured after definition before executing inst.
llvm::Value * to_blas_callconv(llvm::IRBuilder<> &B, llvm::Value *V, bool byRef, bool cublas, llvm::IntegerType *julia_decl, llvm::IRBuilder<> &entryBuilder, llvm::Twine const &="")
llvm::Function * GetFunctionFromValue(llvm::Value *fn)
static bool EmitWarningEnabled(const llvm::LLVMContext &Ctx)
Definition Utils.h:246
llvm::Value * is_lower(llvm::IRBuilder<> &B, llvm::Value *uplo, bool byRef, bool cublas)
static const std::pair< const char *, const char * > KnownMPIFunctions[]
MPI routines recognized by Enzyme, keyed by their lowercase, underscore-free base name and mapped to ...
Definition Utils.h:2720
static bool endsWith(llvm::StringRef string, llvm::StringRef suffix)
Definition Utils.h:855
static llvm::Value * CreateSelect(llvm::IRBuilder<> &Builder2, llvm::Value *cmp, llvm::Value *tval, llvm::Value *fval, const llvm::Twine &Name="")
Definition Utils.h:2283
static void setBranchCondition(llvm::Value *V, llvm::Value *Cond)
Definition Utils.h:108
static std::string convertSRetTypeToString(llvm::Type *T)
Definition Utils.h:2853
static llvm::SmallVector< llvm::Value *, 1 > concat_values(T &&...t)
Definition Utils.h:2439
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
Definition Utils.h:1470
llvm::Value * to_blas_fp_callconv(llvm::IRBuilder<> &B, llvm::Value *V, bool byRef, llvm::Type *julia_decl, llvm::IRBuilder<> &entryBuilder, llvm::Twine const &="")
llvm::Function * getOrInsertMemcpyStrided(llvm::Module &M, llvm::Type *elementType, llvm::PointerType *T, llvm::Type *IT, unsigned dstalign, unsigned srcalign)
Create function for type that performs memcpy with a stride.
static bool isReadNone(const llvm::CallBase *call, ssize_t arg=-1)
Definition Utils.h:2101
static constexpr const char * MixedActivityHint
Remedy appended to mismatched-activity warnings, which are emitted when a value Enzyme proved inactiv...
Definition Utils.h:315
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
Definition Utils.h:2033
static bool isLocalReadOnlyOrThrow(const llvm::Function *F)
Definition Utils.h:1959
llvm::cl::opt< bool > EnzymeLapackCopy
ValueType
Classification of value as an original program variable, a derivative variable, neither,...
Definition Utils.h:543
static llvm::Instruction * getFirstNonPHIOrDbg(llvm::BasicBlock *B)
Definition Utils.h:2580
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
Definition Utils.cpp:4026
static std::string to_string(const std::set< T > &us)
Output a set as a string.
Definition Utils.h:420
static llvm::Attribute::AttrKind PrimalParamAttrsToPreserve[]
Definition Utils.h:2486
void copyNonJLValueInto(llvm::IRBuilder<> &B, llvm::Type *curType, llvm::Type *dstType, llvm::Value *dst, llvm::ArrayRef< unsigned > dstPrefix, llvm::Type *srcType, llvm::Value *src, llvm::ArrayRef< unsigned > srcPrefix, bool shouldZero)
Definition Utils.cpp:5271
llvm::cl::opt< bool > EnzymeNonPower2Cache
static llvm::Attribute::AttrKind ShadowParamAttrsToPreserve[]
Definition Utils.h:2518
llvm::SmallVector< std::tuple< llvm::Instruction *, llvm::Value *, size_t >, 1 > findAllUsersOf(llvm::Value *AI)
static void moveBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
Definition Utils.h:2571
static llvm::raw_ostream & operator<<(llvm::raw_ostream &os, ValueType mode)
Definition Utils.h:569
static bool hasTerminator(llvm::BasicBlock *BB)
Definition Utils.h:2981
static std::map< K, V >::iterator insert_or_assign(std::map< K, V > &map, K &key, V &&val)
Insert into a map.
Definition Utils.h:979
llvm::FunctionCallee getOrInsertPerCallingConv(llvm::Module &M, llvm::Function *templateFn, llvm::StringRef callee, llvm::FunctionType *FT)
Declare callee in M under whatever naming convention the frontend used to reach templateFn,...
llvm::Constant * getUndefinedValueForType(llvm::Module &M, llvm::Type *T, bool forceZero=false)
Definition Utils.cpp:3918
static void allFollowersOf(llvm::Instruction *inst, llvm::function_ref< bool(llvm::Instruction *)> f)
Call the function f for all instructions that happen after inst If the function returns true,...
Definition Utils.h:1013
DerivativeMode
Definition Utils.h:524
void copy_lower_to_upper(llvm::IRBuilder<> &B, llvm::Type *fpType, BlasInfo blas, bool byRef, llvm::Value *layout, llvm::Value *uplo, llvm::Value *A, llvm::Value *lda, llvm::Value *N)
Definition Utils.cpp:1382
static void append(llvm::SmallVectorImpl< T > &vec)
Definition Utils.h:2431
static llvm::Optional< size_t > getAllocationIndexFromCall(const llvm::CallBase *op)
Definition Utils.h:1519
static llvm::Instruction * createUnconditionalBranch(llvm::BasicBlock *Target, PosT InsertBefore)
Definition Utils.h:127
llvm::Value * simplifyLoad(llvm::Value *LI, size_t valSz=0, size_t preOffset=0)
static DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode, bool integersAreConstant, std::set< llvm::Type * > &seen)
Attempt to automatically detect the differentiable classification based off of a given type.
Definition Utils.h:653
llvm::Function * getFirstFunctionDefinition(llvm::Module &M)
llvm::Function * getOrInsertDifferentialFloatMemcpyMat(llvm::Module &M, llvm::Type *elementType, llvm::PointerType *PT, llvm::IntegerType *IT, llvm::IntegerType *CT, unsigned dstalign, unsigned srcalign, bool zeroSrc)
void deleted() override final
Definition Utils.h:1426
void allUsesReplacedWith(llvm::Value *new_value) override final
Definition Utils.h:1431
virtual ~AssertingReplacingVH()
Definition Utils.h:1434
AssertingReplacingVH()=default
AssertingReplacingVH(llvm::Value *new_value)
Definition Utils.h:1424
Concrete SubType of a given value.
EnzymeFailure(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
Definition Utils.cpp:914
EnzymeWarning(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
Definition Utils.cpp:904
A holder class representing the results of running TypeAnalysis on a given function.
llvm::Type * fpType(llvm::LLVMContext &ctx, bool to_scalar=false) const
Definition Utils.cpp:1110
std::string function
Definition Utils.h:882
bool is64
Definition Utils.h:883
std::string suffix
Definition Utils.h:881
std::string prefix
Definition Utils.h:880
std::string floatType
Definition Utils.h:879
llvm::IntegerType * intType(llvm::LLVMContext &ctx) const
Definition Utils.cpp:1129
CountTrackedPointers(llvm::Type *T)
Definition Utils.cpp:4431