28#include "llvm/ADT/MapVector.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallPtrSet.h"
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"
42#include "llvm/IR/Function.h"
43#include "llvm/IR/IntrinsicInst.h"
45#include "llvm/IR/ValueMap.h"
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/raw_ostream.h"
49#include "llvm/Support/CommandLine.h"
51#include "llvm/IR/Instructions.h"
58#if LLVM_VERSION_MAJOR >= 24
59 return llvm::isa<llvm::CondBrInst, llvm::UncondBrInst>(V);
61 return llvm::isa<llvm::BranchInst>(V);
66#if LLVM_VERSION_MAJOR >= 24
67 return llvm::isa<llvm::CondBrInst>(V);
69 if (
auto *BI = llvm::dyn_cast<llvm::BranchInst>(V))
70 return BI->isConditional();
76#if LLVM_VERSION_MAJOR >= 24
77 return llvm::isa<llvm::UncondBrInst>(V);
79 if (
auto *BI = llvm::dyn_cast<llvm::BranchInst>(V))
80 return BI->isUnconditional();
92#if LLVM_VERSION_MAJOR >= 23
101#if LLVM_VERSION_MAJOR >= 24
102 return llvm::cast<llvm::CondBrInst>(V)->getCondition();
104 return llvm::cast<llvm::BranchInst>(V)->getCondition();
109#if LLVM_VERSION_MAJOR >= 24
110 llvm::cast<llvm::CondBrInst>(V)->setCondition(Cond);
112 llvm::cast<llvm::BranchInst>(V)->setCondition(Cond);
116#if LLVM_VERSION_MAJOR >= 24
117static inline llvm::Instruction *
119 llvm::InsertPosition InsertBefore) {
120 return llvm::UncondBrInst::Create(Target, InsertBefore);
125template <
typename PosT>
126static inline llvm::Instruction *
128 return llvm::BranchInst::Create(Target, InsertBefore);
132#include "llvm/ADT/SetVector.h"
133#include "llvm/ADT/StringMap.h"
135#include "llvm/IR/Dominators.h"
136#include "llvm/IR/IntrinsicsAMDGPU.h"
137#include "llvm/IR/IntrinsicsNVPTX.h"
142#if LLVM_VERSION_MAJOR >= 16
146#if LLVM_VERSION_MAJOR >= 23
147#define amd_target amdgpu
149#define amd_target amdgcn
152#if LLVM_VERSION_MAJOR >= 16
153#include "llvm/TargetParser/Triple.h"
155#include "llvm/ADT/Triple.h"
158#include "llvm/Support/AMDGPUMetadata.h"
163 auto Arch = TT.getArch();
164 return Arch == llvm::Triple::nvptx || Arch == llvm::Triple::nvptx64 ||
165 Arch == llvm::Triple::amd_target || TT.getArchName() ==
"air64";
170 return TT.getArch() == llvm::Triple::amd_target
171 ? (unsigned)llvm::AMDGPU::HSAMD::AddressSpaceQualifier::Local
175#include "llvm/IR/DiagnosticInfo.h"
177#include "llvm/Analysis/OptimizationRemarkEmitter.h"
184class ScalarEvolution;
215 const void *, LLVMValueRef,
219llvm::SmallVector<llvm::Instruction *, 2>
PostCacheStore(llvm::StoreInst *SI,
220 llvm::IRBuilder<> &B);
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,
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);
241extern llvm::StringMap<std::function<llvm::Value *(
242 llvm::IRBuilder<> &, llvm::CallInst *, llvm::ArrayRef<llvm::Value *>,
248 Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled(
"enzyme");
251template <
typename...
Args>
253 const llvm::DiagnosticLocation &Loc,
254 const llvm::BasicBlock *BB,
const Args &...args) {
256 llvm::LLVMContext &Ctx = BB->getContext();
257 if (Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled(
"enzyme")) {
259 llvm::raw_string_ostream ss(
str);
261 auto R = llvm::OptimizationRemark(
"enzyme", RemarkName, Loc, BB)
267 (llvm::errs() << ... << args) <<
"\n";
270template <
typename...
Args>
271void EmitWarning(llvm::StringRef RemarkName,
const llvm::Instruction &I,
272 const Args &...args) {
273 EmitWarning(RemarkName, I.getDebugLoc(), I.getParent(), args...);
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);
288template <
typename...
Args>
290 const llvm::DiagnosticLocation &Loc,
291 const llvm::Function &F,
const Args &...args) {
294 std::string
str =
"Enzyme: ";
295 llvm::raw_string_ostream ss(
str);
300template <
typename...
Args>
302 const Args &...args) {
307template <
typename...
Args>
309 const Args &...args) {
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.";
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")) {
326 llvm::raw_string_ostream ss(
str);
328 auto R = llvm::OptimizationRemark(
"enzyme", RemarkName, &F) << ss.str();
332 (llvm::errs() << ... << args) <<
"\n";
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);
346template <
typename...
Args>
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);
353 CodeRegion->getContext().diagnose(
357template <
typename...
Args>
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);
364 CodeRegion->getContext().diagnose(
368template <
typename...
Args>
373 EmitFailure(RemarkName, FirstFunc->getSubprogram(), FirstFunc, args...);
376 std::string *
str =
new std::string();
377 llvm::raw_string_ostream ss(*
str);
379 llvm::report_fatal_error(llvm::StringRef(*
str));
384 if (llvm::CallInst *CI = llvm::dyn_cast<llvm::CallInst>(val)) {
385 return CI->getCalledFunction();
393 llvm::Instruction &inst,
395 llvm::Value *condition =
nullptr);
406template <
typename T>
static inline T
max(T a, T b) {
412template <
typename T>
static inline T
min(T a, T b) {
420static inline std::string
to_string(
const std::set<T> &us) {
422 for (
const auto &y : us)
423 s += std::to_string(y) +
",";
429template <
typename T,
typename N>
431 const llvm::ValueMap<T, N> &o,
432 llvm::function_ref<
bool(
const llvm::Value *)> shouldPrint = [](T) {
435 llvm::errs() <<
"<begin dump>\n";
437 if (shouldPrint(a.first))
438 llvm::errs() <<
"key=" << *a.first <<
" val=" << *a.second <<
"\n";
440 llvm::errs() <<
"</end dump>\n";
445static inline void dumpSet(
const llvm::SmallPtrSetImpl<T *> &o) {
446 llvm::errs() <<
"<begin dump>\n";
448 llvm::errs() << *a <<
"\n";
449 llvm::errs() <<
"</end dump>\n";
453static inline void dumpSet(
const llvm::SetVector<T *> &o) {
454 llvm::errs() <<
"<begin dump>\n";
456 llvm::errs() << *a <<
"\n";
457 llvm::errs() <<
"</end dump>\n";
461static inline llvm::Instruction *
463 for (llvm::Instruction *I = Z->getNextNode(); I; I = I->getNextNode())
464 if (!llvm::isa<llvm::DbgInfoIntrinsic>(I))
470static inline llvm::Instruction *
475 llvm::errs() << *Z->getParent() <<
"\n";
476 llvm::errs() << *Z <<
"\n";
477 llvm_unreachable(
"No valid subsequent non debug instruction");
483static inline llvm::MDNode *
hasMetadata(
const llvm::GlobalObject *O,
484 llvm::StringRef kind) {
485 return O->getMetadata(kind);
489static inline llvm::MDNode *
hasMetadata(
const llvm::Instruction *O,
490 llvm::StringRef kind) {
491 return O->getMetadata(kind);
493static inline llvm::MDNode *
hasMetadata(
const llvm::Instruction *O,
495 return O->getMetadata(kind);
566 llvm_unreachable(
"illegal valuetype");
569static inline llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
577 return "ForwardMode";
579 return "ForwardModeError";
581 return "ForwardModeSplit";
583 return "ReverseModePrimal";
585 return "ReverseModeGradient";
587 return "ReverseModeCombined";
589 llvm_unreachable(
"illegal derivative mode");
592static inline llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
609 assert(0 &&
"illegal diffetype");
614static inline llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
623 return "ArgsWithReturn";
625 return "ArgsWithTwoReturns";
629 return "TapeAndReturn";
631 return "TapeAndTwoReturns";
641 llvm_unreachable(
"illegal ReturnType");
644static inline llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
654 bool integersAreConstant,
655 std::set<llvm::Type *> &seen) {
657 if (seen.find(arg) != seen.end())
661 if (arg->isVoidTy() || arg->isEmptyTy()) {
665 if (arg->isPointerTy()) {
666#if LLVM_VERSION_MAJOR >= 17
669#if LLVM_VERSION_MAJOR >= 15
670 if (!arg->getContext().supportsTypedPointers()) {
673#elif LLVM_VERSION_MAJOR >= 13
674 if (arg->isOpaquePointerTy()) {
678 switch (
whatType(arg->getPointerElementType(), mode, integersAreConstant,
687 llvm_unreachable(
"impossible case");
690 llvm::errs() <<
"arg: " << *arg <<
"\n";
691 assert(0 &&
"Cannot handle type0");
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)
703 for (
unsigned i = 0; i < st->getNumElements(); ++i) {
705 whatType(st->getElementType(i), mode, integersAreConstant, seen);
717 llvm_unreachable(
"impossible case");
731 llvm_unreachable(
"impossible case");
737 llvm_unreachable(
"impossible case");
741 }
else if (arg->isIntOrIntVectorTy() || arg->isFunctionTy()) {
743 }
else if (arg->isFPOrFPVectorTy()) {
751 llvm::errs() <<
"arg: " << *arg <<
"\n";
752 assert(0 &&
"Cannot handle type");
757llvm::Value *
get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res);
760 std::set<llvm::Type *> seen;
761 return whatType(arg, mode,
true, seen);
766 for (
const auto a : inst->users()) {
767 if (llvm::isa<llvm::ReturnInst>(a))
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());
782 return llvm::IntegerType::get(T->getContext(), 16);
784 return llvm::IntegerType::get(T->getContext(), 16);
786 return llvm::IntegerType::get(T->getContext(), 32);
788 return llvm::IntegerType::get(T->getContext(), 64);
789 if (T->isX86_FP80Ty())
790 return llvm::IntegerType::get(T->getContext(), 80);
792 return llvm::IntegerType::get(T->getContext(), 128);
793 assert(0 &&
"unknown floating point type");
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());
805 if (
auto ty = llvm::dyn_cast<llvm::IntegerType>(T)) {
806 switch (ty->getBitWidth()) {
808 return llvm::Type::getHalfTy(T->getContext());
811 return llvm::Type::getFloatTy(T->getContext());
813 return llvm::Type::getDoubleTy(T->getContext());
815 return llvm::Type::getX86_FP80Ty(T->getContext());
817 return llvm::Type::getFP128Ty(T->getContext());
820 assert(0 &&
"unknown int to floating point type");
827 if (called->getName() ==
"llvm.enzyme.lifetime_start" ||
828 called->getName() ==
"llvm.enzyme.lifetime_end") {
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:
838 case llvm::Intrinsic::lifetime_start:
839 case llvm::Intrinsic::lifetime_end:
847static inline bool startsWith(llvm::StringRef
string, llvm::StringRef prefix) {
848#if LLVM_VERSION_MAJOR >= 18
849 return string.starts_with(prefix);
851 return string.startswith(prefix);
855static inline bool endsWith(llvm::StringRef
string, llvm::StringRef suffix) {
856#if LLVM_VERSION_MAJOR >= 18
857 return string.ends_with(suffix);
859 return string.endswith(suffix);
864 if (name ==
"printf" || name ==
"puts" || name ==
"fprintf" ||
865 name ==
"putchar" || name ==
"fputc" ||
867 "_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_") ||
871 startsWith(name,
"_ZN3std2io5stdio6_print") ||
885 llvm::Type *
fpType(llvm::LLVMContext &ctx,
bool to_scalar =
false)
const;
886 llvm::IntegerType *
intType(llvm::LLVMContext &ctx)
const;
889#if LLVM_VERSION_MAJOR >= 16
890std::optional<BlasInfo>
extractBLAS(llvm::StringRef in);
892llvm::Optional<BlasInfo>
extractBLAS(llvm::StringRef in);
895std::vector<std::tuple<llvm::Type *, size_t, size_t>>
901llvm::MDNode *
sliceTrueType(
const llvm::MDNode *md,
size_t start,
904bool isAtomic(llvm::Value *origptr,
bool AtomicAdd, llvm::Function *newFunc);
909 llvm::Module &M, llvm::Type *T,
unsigned dstalign,
unsigned srcalign,
910 unsigned dstaddr,
unsigned srcaddr,
unsigned bitwidth,
bool runtimeActivity,
915 llvm::ArrayRef<llvm::Value *> args,
916 llvm::Type *cublas_retty,
917 llvm::ArrayRef<llvm::OperandBundleDef> bundles);
921 BlasInfo blas, llvm::ArrayRef<llvm::Value *> args,
922 llvm::ArrayRef<llvm::OperandBundleDef> bundles);
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);
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);
942 llvm::Type *elementType,
943 llvm::PointerType *T, llvm::Type *IT,
944 unsigned dstalign,
unsigned srcalign);
948 llvm::PointerType *PT,
949 llvm::IntegerType *IT,
unsigned dstalign,
953 llvm::Module &M, llvm::Type *elementType, llvm::PointerType *PT,
954 llvm::IntegerType *IT, llvm::IntegerType *CT,
unsigned dstalign,
955 unsigned srcalign,
bool zeroSrc);
960 llvm::Module &M, llvm::Type *T,
unsigned dstalign,
unsigned srcalign,
961 unsigned dstaddr,
unsigned srcaddr,
unsigned bitwidth,
bool runtimeActivity,
965 llvm::Type *Type,
unsigned width);
969 llvm::ArrayRef<llvm::Type *> T,
971 llvm::StringRef caller);
977template <
typename K,
typename V>
978static inline typename std::map<K, V>::iterator
980 auto found = map.find(key);
981 if (found != map.end()) {
984 return map.emplace(key, val).first;
988template <
typename K,
typename V>
989static inline typename std::map<K, V>::iterator
991 auto found = map.find(key);
992 if (found != map.end()) {
995 return map.emplace(key, val).first;
998template <
typename K,
typename V>
1000 auto found = map.find(key);
1001 if (found == map.end())
1003 V *val = &found->second;
1007#include "llvm/IR/CFG.h"
1009#include <functional>
1014 llvm::function_ref<
bool(llvm::Instruction *)> f) {
1016 for (
auto uinst = inst->getNextNode(); uinst !=
nullptr;
1017 uinst = uinst->getNextNode()) {
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);
1027 while (todo.size()) {
1028 auto BB = todo.front();
1033 for (
auto &ni : *BB) {
1039 for (
auto suc : llvm::successors(BB)) {
1040 todo.push_back(suc);
1049 llvm::function_ref<
bool(llvm::Instruction *)> f) {
1051 for (
auto uinst = inst->getPrevNode(); uinst !=
nullptr;
1052 uinst = uinst->getPrevNode()) {
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);
1062 while (todo.size()) {
1063 auto BB = todo.front();
1069 llvm::BasicBlock::reverse_iterator I = BB->rbegin(), E = BB->rend();
1070 for (; I != E; ++I) {
1076 for (
auto suc : llvm::predecessors(BB)) {
1077 todo.push_back(suc);
1086 llvm::function_ref<
bool(llvm::Instruction *)> f) {
1088 for (
auto uinst = inst->getPrevNode(); uinst !=
nullptr;
1089 uinst = uinst->getPrevNode()) {
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);
1099 while (todo.size()) {
1100 auto BB = todo.front();
1106 if (DT.properlyDominates(BB, inst->getParent())) {
1107 llvm::BasicBlock::reverse_iterator I = BB->rbegin(), E = BB->rend();
1108 for (; I != E; ++I) {
1114 for (
auto suc : llvm::predecessors(BB)) {
1115 todo.push_back(suc);
1125 llvm::function_ref<
bool(llvm::Instruction *)> f,
1126 llvm::function_ref<
void()> preEntry) {
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) {
1134#if LLVM_VERSION_MAJOR > 20
1135 if (II->getIntrinsicID() ==
1136 llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_all) {
1140 if (II->getIntrinsicID() == llvm::Intrinsic::nvvm_barrier0) {
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);
1154 while (todo.size()) {
1155 auto BB = todo.front();
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) {
1169#if LLVM_VERSION_MAJOR > 20
1170 if (II->getIntrinsicID() ==
1171 llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_all) {
1173 if (II->getIntrinsicID() == llvm::Intrinsic::nvvm_barrier0) {
1185 for (
auto suc : llvm::predecessors(BB)) {
1186 todo.push_back(suc);
1188 if (&BB->getParent()->getEntryBlock() == BB) {
1195#include "llvm/Analysis/LoopInfo.h"
1197static inline llvm::Loop *
getAncestor(llvm::Loop *R1, llvm::Loop *R2) {
1200 for (llvm::Loop *L1 = R1; L1; L1 = L1->getParentLoop())
1201 for (llvm::Loop *L2 = R2; L2; L2 = L2->getParentLoop()) {
1212 llvm::Instruction *inst,
1213 const llvm::SetVector<llvm::Instruction *> &stores,
1214 const llvm::Loop *region);
1218 llvm::TargetLibraryInfo &TLI,
1219 llvm::Instruction *maybeReader,
1220 llvm::Instruction *maybeWriter);
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);
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()) {
1255 std::set<llvm::Instruction *> instructions;
1257 llvm::Loop *l1 = LI.getLoopFor(inst1->getParent());
1258 while (l1 && !l1->contains(inst2->getParent()))
1259 l1 = l1->getParentLoop();
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);
1268 while (todo.size()) {
1269 auto BB = todo.front();
1275 for (
auto &ni : *BB) {
1276 instructions.insert(&ni);
1278 for (
auto suc : llvm::successors(BB)) {
1279 if (!l1 || suc != l1->getHeader()) {
1280 todo.push_back(suc);
1287 if (instructions.find(I) == instructions.end())
1315#if LLVM_VERSION_MAJOR >= 17
1317 return llvm::PointerType::get(T->getContext(),
AddressSpace);
1340#if LLVM_VERSION_MAJOR < 17
1341 if (PT->getContext().supportsTypedPointers())
1345 return llvm::PointerType::get(PT->getContext(),
AddressSpace);
1353static inline uint64_t
1355 llvm::ArrayRef<unsigned> Idxs) {
1356 uint64_t Offset = 0;
1357 for (
unsigned Idx : Idxs) {
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);
1366 uint64_t Stride = DL.getTypeAllocSize(ElTy);
1367 Offset += Idx * Stride;
1376 llvm::Type *T, uint64_t Idx) {
1377 unsigned Idxs[1] = {(unsigned)Idx};
1382 using namespace llvm;
1383 auto i64 = Type::getInt64Ty(Context);
1391 Type::getInt8Ty(Context),
1394 return StructType::get(Context, types,
false);
1397template <MPI_Elem E,
bool Po
inter = true>
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);
1406 return B.CreateInBoundsGEP(T, V,
1407 {c0_64, ConstantInt::get(i32, (uint64_t)E)});
1409 return B.CreateExtractValue(V, {(unsigned)E});
1416 llvm::Type *OpPtr, llvm::Type *OpType,
1418 llvm::IRBuilder<> &B2);
1427 assert(0 &&
"attempted to delete value with remaining handle use");
1428 llvm_unreachable(
"attempted to delete value with remaining handle use");
1432 setValPtr(new_value);
1438 const llvm::Function *called =
nullptr;
1439 using namespace llvm;
1440 const llvm::Value *callVal;
1441 callVal = op->getCalledOperand();
1443 if (
auto castinst = dyn_cast<ConstantExpr>(callVal))
1444 if (castinst->isCast()) {
1445 callVal = castinst->getOperand(0);
1448 if (
auto fn = dyn_cast<Function>(callVal)) {
1452 if (
auto alias = dyn_cast<GlobalAlias>(callVal)) {
1453 callVal = alias->getAliasee();
1458 return called ?
const_cast<llvm::Function *
>(called) :
nullptr;
1462 if (called->hasFnAttribute(
"enzyme_math"))
1463 return called->getFnAttribute(
"enzyme_math").getValueAsString();
1464 else if (called->hasFnAttribute(
"enzyme_allocator"))
1465 return "enzyme_allocator";
1467 return called->getName();
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";
1485 using namespace llvm;
1486 if (
auto CB = dyn_cast<CallBase>(op)) {
1488 if (called->hasFnAttribute(
"enzyme_nocache"))
1495 if (
auto I = dyn_cast<Instruction>(op))
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))
1507 if (
auto IT = dyn_cast<IntegerType>(op->getType()))
1514#if LLVM_VERSION_MAJOR >= 16
1515static inline std::optional<size_t>
1518static inline llvm::Optional<size_t>
1523 op->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
1524 if (AttrList.hasAttribute(
"enzyme_allocator")) {
1526 bool b = AttrList.getAttribute(
"enzyme_allocator")
1528 .getAsInteger(10, res);
1531#if LLVM_VERSION_MAJOR >= 16
1532 return std::optional<size_t>(res);
1534 return llvm::Optional<size_t>(res);
1539 if (called->hasFnAttribute(
"enzyme_allocator")) {
1541 bool b = called->getFnAttribute(
"enzyme_allocator")
1543 .getAsInteger(10, res);
1546#if LLVM_VERSION_MAJOR >= 16
1547 return std::optional<size_t>(res);
1549 return llvm::Optional<size_t>(res);
1553#if LLVM_VERSION_MAJOR >= 16
1554 return std::optional<size_t>();
1556 return llvm::Optional<size_t>();
1560template <
typename T>
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());
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());
1576 llvm::errs() <<
"dealloc fn: " << *op->getParent()->getParent()->getParent()
1578 llvm_unreachable(
"Illegal deallocatorfn");
1581template <
typename T>
1583 llvm::StringRef res =
"";
1585 op->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
1586 if (AttrList.hasAttribute(
"enzyme_deallocator"))
1587 res = AttrList.getAttribute(
"enzyme_deaellocator").getValueAsString();
1590 if (called->hasFnAttribute(
"enzyme_deallocator"))
1591 res = called->getFnAttribute(
"enzyme_deallocator").getValueAsString();
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) {
1600 bool b = ind.getAsInteger(10, Result);
1603 vinds.push_back(Result);
1610 llvm::ArrayRef<llvm::Type *> T,
1611 llvm::PointerType *reqType);
1614 llvm::Value *shadow,
const char *Message,
1615 llvm::DebugLoc &&loc, llvm::Instruction *orig);
1622 size_t preOffset = 0);
1638 (F && (F->hasFnAttribute(
"enzyme_preserve_primal") ||
1643 if (funcName ==
"MPI_Wait" || funcName ==
"MPI_Waitall") {
1654 if (
auto II = llvm::dyn_cast<llvm::IntrinsicInst>(val)) {
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)))
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:
1692 if (
auto *
Call = llvm::dyn_cast<llvm::CallInst>(V)) {
1694 if (funcName ==
"julia.pointer_from_objref") {
1697 if (funcName ==
"julia.gc_loaded") {
1700 if (funcName.contains(
"__enzyme_todense")) {
1703 if (funcName.contains(
"__enzyme_ignore_derivatives")) {
1712 bool offsetAllowed =
true) {
1714 if (
auto CI = llvm::dyn_cast<llvm::CastInst>(V)) {
1715 V = CI->getOperand(0);
1717 }
else if (
auto CI = llvm::dyn_cast<llvm::GetElementPtrInst>(V)) {
1718 if (offsetAllowed || CI->hasAllZeroIndices()) {
1719 V = CI->getOperand(0);
1722 }
else if (
auto II = llvm::dyn_cast<llvm::IntrinsicInst>(V);
1724 if (offsetAllowed) {
1725 V = II->getOperand(3);
1728 }
else if (
auto CI = llvm::dyn_cast<llvm::PHINode>(V)) {
1729 if (CI->getNumIncomingValues() == 1) {
1730 V = CI->getOperand(0);
1733 }
else if (
auto *GA = llvm::dyn_cast<llvm::GlobalAlias>(V)) {
1734 if (GA->isInterposable())
1736 V = GA->getAliasee();
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);
1743 }
else if (
auto *
Call = llvm::dyn_cast<llvm::CallInst>(V)) {
1745 auto AttrList =
Call->getAttributes().getAttributes(
1746 llvm::AttributeList::FunctionIndex);
1747 if (AttrList.hasAttribute(
"enzyme_pointermath") && offsetAllowed) {
1749 bool failed = AttrList.getAttribute(
"enzyme_pointermath")
1751 .getAsInteger(10, res);
1754 V =
Call->getArgOperand(res);
1757 if (funcName ==
"julia.pointer_from_objref") {
1758 V =
Call->getArgOperand(0);
1761 if (funcName ==
"julia.gc_loaded") {
1762 V =
Call->getArgOperand(1);
1765 if (funcName ==
"jl_reshape_array" || funcName ==
"ijl_reshape_array") {
1766 V =
Call->getArgOperand(1);
1769 if (funcName.contains(
"__enzyme_ignore_derivatives")) {
1770 V =
Call->getArgOperand(0);
1773 if (funcName.contains(
"__enzyme_todense")) {
1774#if LLVM_VERSION_MAJOR >= 14
1775 size_t numargs =
Call->arg_size();
1777 size_t numargs =
Call->getNumArgOperands();
1780 V =
Call->getArgOperand(2);
1785 auto AttrList = fn->getAttributes().getAttributes(
1786 llvm::AttributeList::FunctionIndex);
1787 if (AttrList.hasAttribute(
"enzyme_pointermath") && offsetAllowed) {
1789 bool failed = AttrList.getAttribute(
"enzyme_pointermath")
1791 .getAsInteger(10, res);
1794 V =
Call->getArgOperand(res);
1798 for (
auto &arg : fn->args()) {
1799 if (arg.hasAttribute(llvm::Attribute::Returned)) {
1801 V =
Call->getArgOperand(arg.getArgNo());
1819 llvm::getArgumentAliasingToReturnedPointer(
Call,
false)) {
1826 if (
auto I = llvm::dyn_cast<llvm::Instruction>(V)) {
1827#if LLVM_VERSION_MAJOR >= 12
1828 auto V2 = llvm::getUnderlyingObject(I, 100);
1830 auto V2 = llvm::GetUnderlyingObject(
1831 I, I->getParent()->getParent()->getParent()->getDataLayout(), 100);
1846static inline llvm::SetVector<llvm::Value *>
1848 llvm::SmallPtrSet<llvm::Value *, 1> seen;
1849 llvm::SetVector<llvm::Value *> results;
1850 llvm::SmallVector<llvm::Value *, 1> todo = {V};
1852 while (todo.size()) {
1853 auto obj = todo.back();
1855 if (seen.contains(obj))
1859 if (
auto PN = llvm::dyn_cast<llvm::PHINode>(obj)) {
1860 for (
auto &x : PN->incoming_values()) {
1866 if (
auto SI = llvm::dyn_cast<llvm::SelectInst>(obj)) {
1867 todo.push_back(SI->getTrueValue());
1868 todo.push_back(SI->getFalseValue());
1874 todo.push_back(cur);
1878 results.insert(obj);
1883static inline bool isReadOnly(
const llvm::Function *F, ssize_t arg = -1) {
1884 if (F->onlyReadsMemory())
1887 if (F->hasFnAttribute(llvm::Attribute::ReadOnly) ||
1888 F->hasFnAttribute(llvm::Attribute::ReadNone))
1891 if (F->hasParamAttribute(arg, llvm::Attribute::ReadOnly) ||
1892 F->hasParamAttribute(arg, llvm::Attribute::ReadNone))
1894#if LLVM_VERSION_MAJOR >= 16
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)))
1915static inline bool isReadOnly(
const llvm::CallBase *call, ssize_t arg = -1) {
1916 if (call->onlyReadsMemory())
1918 if (arg != -1 && call->onlyReadsMemory(arg))
1920#if LLVM_VERSION_MAJOR >= 16
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)))
1941 if (F->getCallingConv() == call->getCallingConv())
1963 if (F->hasFnAttribute(
"enzyme_LocalReadOnlyOrThrow") ||
1964 F->hasFnAttribute(
"enzyme_ReadOnlyOrThrow"))
1974 if (call->hasFnAttr(
"enzyme_LocalReadOnlyOrThrow") ||
1975 call->hasFnAttr(
"enzyme_ReadOnlyOrThrow"))
1983 if (F->getCallingConv() == call->getCallingConv())
2008 if (F->hasFnAttribute(
"enzyme_ReadOnlyOrThrow"))
2018 if (call->hasFnAttr(
"enzyme_ReadOnlyOrThrow"))
2026 if (F->getCallingConv() == call->getCallingConv())
2033static inline bool isWriteOnly(
const llvm::Function *F, ssize_t arg = -1) {
2034#if LLVM_VERSION_MAJOR >= 14
2035 if (F->onlyWritesMemory())
2038 if (F->hasFnAttribute(llvm::Attribute::WriteOnly) ||
2039 F->hasFnAttribute(llvm::Attribute::ReadNone))
2042 if (F->hasParamAttribute(arg, llvm::Attribute::WriteOnly) ||
2043 F->hasParamAttribute(arg, llvm::Attribute::ReadNone))
2045#if LLVM_VERSION_MAJOR >= 16
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)))
2061static inline bool isWriteOnly(
const llvm::CallBase *call, ssize_t arg = -1) {
2062#if LLVM_VERSION_MAJOR >= 14
2063 if (call->onlyWritesMemory())
2065 if (arg != -1 && call->onlyWritesMemory(arg))
2067#if LLVM_VERSION_MAJOR >= 16
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)))
2080 if (call->hasFnAttr(llvm::Attribute::WriteOnly) ||
2081 call->hasFnAttr(llvm::Attribute::ReadNone))
2084 if (call->dataOperandHasImpliedAttr(arg + 1, llvm::Attribute::WriteOnly) ||
2085 call->dataOperandHasImpliedAttr(arg + 1, llvm::Attribute::ReadNone))
2095 if (F->getCallingConv() == call->getCallingConv())
2101static inline bool isReadNone(
const llvm::CallBase *call, ssize_t arg = -1) {
2105static inline bool isReadNone(
const llvm::Function *F, ssize_t arg = -1) {
2109static inline bool isNoCapture(
const llvm::CallBase *call,
size_t idx) {
2110 if (call->doesNotCapture(idx))
2118 if (F->getCallingConv() == call->getCallingConv())
2119 if (idx < F->arg_size() && F->getArg(idx)->hasNoCaptureAttr())
2128 if (call->returnDoesNotAlias())
2132 if (F->returnDoesNotAlias())
2139 if (
auto CB = llvm::dyn_cast<llvm::CallBase>(val))
2141 if (
auto arg = llvm::dyn_cast<llvm::Argument>(val)) {
2142 arg->hasNoAliasAttr();
2144 if (llvm::isa<llvm::AllocaInst>(val))
2157 if (F->hasFnAttribute(
"enzyme_no_escaping_allocation"))
2159 if (F->getName() ==
"llvm.enzyme.lifetime_start" ||
2160 F->getName() ==
"llvm.enzyme.lifetime_end") {
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:
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:
2186 case Intrinsic::nvvm_barrier_cta_sync_aligned_all:
2187 case Intrinsic::nvvm_barrier_cta_sync_aligned_count:
2189#if LLVM_VERSION_MAJOR < 22
2190 case Intrinsic::nvvm_barrier0_popc:
2191 case Intrinsic::nvvm_barrier0_and:
2192 case Intrinsic::nvvm_barrier0_or:
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:
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:
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:
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:
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:
2249 case Intrinsic::copysign:
2250 case Intrinsic::fabs:
2261 call->getAttributes().getAttributes(llvm::AttributeList::FunctionIndex);
2262 if (AttrList.hasAttribute(
"enzyme_no_escaping_allocation"))
2277 bool forceZero =
false);
2280 llvm::IRBuilder<> &BuilderM,
2281 llvm::Value *mask =
nullptr);
2284 llvm::Value *cmp, llvm::Value *tval,
2286 const llvm::Twine &Name =
"") {
2287 if (
auto cmpi = llvm::dyn_cast<llvm::ConstantInt>(cmp)) {
2293 return Builder2.CreateSelect(cmp, tval, fval, Name);
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);
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())
2306 res = Builder2.CreateSelect(Builder2.CreateFCmpOEQ(idiff, zero), zero, res);
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);
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())
2320 res = Builder2.CreateSelect(Builder2.CreateFCmpOEQ(idiff, zero), zero, res);
2327 const llvm::DataLayout &dl,
2328 llvm::Type **vFT =
nullptr) {
2329 using namespace llvm;
2330 if (
auto CI = dyn_cast_or_null<ConstantInt>(V)) {
2336 if (dl.getTypeSizeInBits(FT) == dl.getTypeSizeInBits(CI->getType())) {
2337 if (CI->isNegative() && CI->isMinValue(
true)) {
2344 if (
auto CV = dyn_cast_or_null<ConstantVector>(V)) {
2346 for (
size_t i = 0, end = CV->getNumOperands(); i < end; ++i) {
2350#if LLVM_VERSION_MAJOR >= 12
2351 *vFT = VectorType::get(FT, CV->getType()->getElementCount());
2353 *vFT = VectorType::get(FT, CV->getType()->getNumElements());
2359 if (
auto CV = dyn_cast_or_null<ConstantDataVector>(V)) {
2361 for (
size_t i = 0, end = CV->getNumElements(); i < end; ++i) {
2362 auto CI = CV->getElementAsAPInt(i);
2363#if LLVM_VERSION_MAJOR > 16
2367 if (CI.isNullValue())
2370 if (dl.getTypeSizeInBits(FT) !=
2371 dl.getTypeSizeInBits(CV->getElementType())) {
2375 if (!CI.isMinSignedValue()) {
2381#if LLVM_VERSION_MAJOR >= 12
2382 *vFT = VectorType::get(FT, CV->getType()->getElementCount());
2384 *vFT = VectorType::get(FT, CV->getType()->getNumElements());
2389 if (
auto BO = dyn_cast<BinaryOperator>(V)) {
2390 if (BO->getOpcode() == Instruction::And) {
2391 for (
size_t i = 0; i < 2; i++) {
2402 llvm::SmallVectorImpl<llvm::Value *> &cacheValues,
2403 llvm::IRBuilder<> &BuilderZ,
const llvm::Twine &name =
"");
2405llvm::Value *
load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType,
2406 llvm::Value *V,
bool byRef);
2409 BlasInfo blas,
bool byRef, llvm::Value *layout,
2410 llvm::Value *uplo, llvm::Value *A, llvm::Value *lda,
2416 bool cublas, llvm::IntegerType *julia_decl,
2417 llvm::IRBuilder<> &entryBuilder,
2418 llvm::Twine
const & =
"");
2420 bool byRef, llvm::Type *julia_decl,
2421 llvm::IRBuilder<> &entryBuilder,
2422 llvm::Twine
const & =
"");
2425 llvm::ArrayRef<llvm::Value *> trans,
2426 llvm::Value *arg_ld, llvm::Value *dim_1,
2427 llvm::Value *dim_2,
bool cacheMat,
bool byRef,
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,
2435 vec.append(vals.begin(), vals.end());
2436 append(vec, std::forward<T2>(ts)...);
2438template <
typename... T>
2440 llvm::SmallVector<llvm::Value *, 1> res;
2441 append(res, std::forward<T>(t)...);
2445llvm::Value *
is_normal(llvm::IRBuilder<> &B, llvm::Value *trans,
bool byRef,
2447llvm::Value *
is_left(llvm::IRBuilder<> &B, llvm::Value *side,
bool byRef,
2449llvm::Value *
is_lower(llvm::IRBuilder<> &B, llvm::Value *uplo,
bool byRef,
2451llvm::Value *
is_nonunit(llvm::IRBuilder<> &B, llvm::Value *uplo,
bool byRef,
2455 llvm::Value *layout, llvm::Value *base,
2456 llvm::Value *lda, llvm::Value *row,
2460llvm::Value *
transpose(std::string floatType, llvm::IRBuilder<> &B,
2461 llvm::Value *V,
bool cublas);
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>
2469 llvm::ArrayRef<llvm::Value *> row,
2470 llvm::ArrayRef<llvm::Value *> col,
bool byRef,
bool cublas);
2472llvm::SmallVector<llvm::Value *, 1>
2474 bool byRef,
bool cublas);
2477#pragma clang diagnostic push
2478#pragma clang diagnostic ignored "-Wunused-variable"
2480#pragma GCC diagnostic push
2481#pragma GCC diagnostic ignored "-Wunused-variable"
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,
2496 llvm::Attribute::AttrKind::Preallocated,
2497 llvm::Attribute::AttrKind::InAlloca,
2498#if LLVM_VERSION_MAJOR >= 13
2499 llvm::Attribute::AttrKind::ElementType,
2501#if LLVM_VERSION_MAJOR >= 15
2502 llvm::Attribute::AttrKind::AllocAlign,
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,
2510 llvm::Attribute::AttrKind::NoCapture,
2512 llvm::Attribute::AttrKind::ReadNone
2519 llvm::Attribute::AttrKind::ZExt,
2520 llvm::Attribute::AttrKind::SExt,
2521#if LLVM_VERSION_MAJOR >= 13
2522 llvm::Attribute::AttrKind::ElementType,
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,
2530 llvm::Attribute::AttrKind::NoCapture,
2532 llvm::Attribute::AttrKind::ReadNone,
2535#pragma clang diagnostic pop
2537#pragma GCC diagnostic pop
2540static inline llvm::Function *
2542 llvm::ArrayRef<llvm::Type *> Tys = {}) {
2543#if LLVM_VERSION_MAJOR >= 20
2544 return llvm::Intrinsic::getOrInsertDeclaration(M,
id, Tys);
2546 return llvm::Intrinsic::getDeclaration(M,
id, Tys);
2551#if LLVM_VERSION_MAJOR >= 18
2553 return &*B->getFirstNonPHIIt();
2556 return B->getFirstNonPHI();
2563 llvm::Instruction *Before) {
2564#if LLVM_VERSION_MAJOR >= 24
2565 I->insertBefore(Before->getIterator());
2567 I->insertBefore(Before);
2572 llvm::Instruction *Before) {
2573#if LLVM_VERSION_MAJOR >= 24
2574 I->moveBefore(Before->getIterator());
2576 I->moveBefore(Before);
2581#if LLVM_VERSION_MAJOR >= 20
2583 return &*B->getFirstNonPHIOrDbg();
2586 return B->getFirstNonPHIOrDbg();
2590static inline llvm::Instruction *
2592#if LLVM_VERSION_MAJOR >= 20
2593 return &*B->getFirstNonPHIOrDbgOrLifetime();
2595 return B->getFirstNonPHIOrDbgOrLifetime();
2600#if LLVM_VERSION_MAJOR > 20
2602 idx, llvm::Attribute::get(call->getContext(), llvm::Attribute::Captures,
2603 llvm::CaptureInfo::none().toIntValue()));
2605 call->addParamAttr(idx, llvm::Attribute::NoCapture);
2610#if LLVM_VERSION_MAJOR > 20
2612 idx, llvm::Attribute::get(call->getContext(), llvm::Attribute::Captures,
2613 llvm::CaptureInfo::none().toIntValue()));
2615 call->addParamAttr(idx, llvm::Attribute::NoCapture);
2619[[nodiscard]]
static inline llvm::AttributeList
2622 unsigned idxs = {(unsigned)idx};
2623#if LLVM_VERSION_MAJOR > 20
2624 return list.addParamAttribute(
2626 llvm::Attribute::get(ctx, llvm::Attribute::Captures,
2627 llvm::CaptureInfo::none().toIntValue()));
2629 return list.addParamAttribute(ctx, idxs, llvm::Attribute::NoCapture);
2635template <
typename Arg1,
typename...
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...);
2645 llvm::errs() << *T <<
"\n";
2646 llvm_unreachable(
"unknown subtype");
2665 llvm::PointerType *PTy = llvm::dyn_cast<llvm::PointerType>(Ty);
2668 unsigned AS = PTy->getAddressSpace();
2672#if LLVM_VERSION_MAJOR >= 20
2674 llvm::GEPOperator *gep,
const llvm::DataLayout &DL,
unsigned BitWidth,
2675 llvm::SmallMapVector<llvm::Value *, llvm::APInt, 4> &VariableOffsets,
2676 llvm::APInt &ConstantOffset);
2680 llvm::MapVector<llvm::Value *, llvm::APInt> &VariableOffsets,
2681 llvm::APInt &ConstantOffset);
2685 llvm::Intrinsic::ID ID, llvm::Type *RetTy,
2686 llvm::ArrayRef<llvm::Value *>
Args,
2687 llvm::Instruction *FMFSource =
nullptr,
2688 const llvm::Twine &Name =
"");
2690bool isNVLoad(
const llvm::Value *V);
2697 size_t checkLoadCaptured,
2698 llvm::Instruction *startinst =
nullptr,
2699 llvm::TargetLibraryInfo *TLI =
nullptr);
2702bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI =
nullptr);
2707#if LLVM_VERSION_MAJOR >= 16
2713 llvm::DominatorTree &DT, llvm::LoopInfo &LI,
2714 llvm::Value *op0, llvm::Value *op1,
2715 bool offsetAllowed =
false);
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"},
2781 llvm::StringRef Base = Name;
2782 if (!Base.consume_front_insensitive(
"pmpi_") &&
2783 !Base.consume_front_insensitive(
"mpi_"))
2785 while (Base.consume_back(
"_")) {
2790 if (Base.equals_insensitive(E.first))
2802static inline std::tuple<llvm::StringRef, llvm::StringRef, llvm::StringRef>
2805 return {
"", caller,
""};
2807 auto &&[prefix, todo] = caller.split(
"$");
2808 auto &&[name, postfix] = todo.split(
"$");
2809 return std::make_tuple(prefix, name, postfix);
2813 llvm::StringRef callee) {
2822 return (
"P" + callee).str();
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] ==
'_')
2833 std::string result = profiling ?
"pmpi_" :
"mpi_";
2834 result += callee.drop_front(strlen(
"MPI_")).lower();
2835 result.append(underscores,
'_');
2838 return callee.str();
2849 llvm::Function *templateFn,
2850 llvm::StringRef callee,
2851 llvm::FunctionType *FT);
2854 return std::to_string((
size_t)T);
2857static inline llvm::Type *
2859 if (
str ==
"test_type") {
2861 llvm::SmallVector<llvm::Type *, 1> elts;
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);
2869 if (
str ==
"test_type2") {
2871 return llvm::ArrayType::get(llvm::Type::getInt64Ty(*C), 6);
2873 if (
str ==
"test_type3") {
2875 llvm::SmallVector<llvm::Type *, 1> elts;
2876 elts.push_back(llvm::Type::getDoubleTy(*C));
2877 return llvm::StructType::get(*C, elts);
2879 if (
str ==
"test_type4") {
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);
2887 if (
str ==
"test_type5") {
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);
2895 if (
str ==
"test_type6") {
2897 llvm::SmallVector<llvm::Type *, 2> elts;
2900 elts.push_back(llvm::Type::getInt64Ty(*C));
2901 return llvm::StructType::get(*C, elts);
2904 bool failed =
str.consumeInteger(10, idx);
2907 return (llvm::Type *)idx;
2912 bool failed =
str.consumeInteger(10, idx);
2919 if (CB->hasStructRetAttr())
2921 for (
size_t i = 0; i < CB->arg_size(); i++) {
2922 if (CB->getAttributeAtIndex(llvm::AttributeList::FirstArgIndex + i,
2923 "enzymejl_sret_union_bytes")
2926 if (CB->getAttributeAtIndex(llvm::AttributeList::FirstArgIndex + i,
2927 "enzymejl_returnRoots")
2943 llvm::Value *sret, llvm::Type *root_ty,
2944 llvm::Value *rootRet,
size_t rootOffset,
2948 llvm::Type *dstType, llvm::Value *dst,
2949 llvm::ArrayRef<unsigned> dstPrefix, llvm::Type *srcType,
2950 llvm::Value *src, llvm::ArrayRef<unsigned> srcPrefix,
2956 if (
auto ST = llvm::dyn_cast<llvm::StructType>(T)) {
2957 for (
auto elem : ST->elements()) {
2963 if (
auto AT = llvm::dyn_cast<llvm::ArrayType>(T)) {
2966 if (
auto VT = llvm::dyn_cast<llvm::VectorType>(T)) {
2973 llvm::IRBuilder<> &B);
2978llvm::SmallVector<std::tuple<llvm::Instruction *, llvm::Value *, size_t>, 1>
2982#if LLVM_VERSION_MAJOR >= 23
2983 return BB->hasTerminator();
2985 return BB->getTerminator();
2999 const llvm::SCEV *It,
3000 llvm::ScalarEvolution &SE);
DIFFE_TYPE
Potential differentiable argument classifications.
static std::string str(AugmentedStruct c)
static llvm::Value * checkedMul(bool strongZero, llvm::IRBuilder<> &Builder2, llvm::Value *idiff, llvm::Value *pres, const llvm::Twine &Name="")
llvm::Value * EmitNoDerivativeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &B, llvm::Value *condition=nullptr)
static llvm::StringRef getFuncName(llvm::Function *called)
static bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II)
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,...
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)
bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI=nullptr)
Check if value if b captured.
static llvm::StringRef canonicalizeMPIName(llvm::StringRef Name)
Canonicalize the name of an MPI routine across calling conventions: both the C convention ("MPI_Recv"...
static bool isNoEscapingAllocation(const llvm::Function *F)
llvm::Value * is_nonunit(llvm::IRBuilder<> &B, llvm::Value *uplo, bool byRef, bool cublas)
static bool hasCustomRuleMetadata(const T *V)
static std::string getRenamedPerCallingConv(llvm::StringRef caller, llvm::StringRef callee)
bool isFlangRuntimeNoFree(llvm::StringRef name)
LLVM flang runtime functions that neither free memory of the program nor return or store an allocatio...
bool isAtomic(llvm::Value *origptr, bool AtomicAdd, llvm::Function *newFunc)
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
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)
static llvm::Function * getDeallocatorFnFromCall(T *op)
llvm::SmallVector< llvm::Instruction *, 2 > PostCacheStore(llvm::StoreInst *SI, llvm::IRBuilder<> &B)
static llvm::Instruction * getNextNonDebugInstructionOrNull(llvm::Instruction *Z)
Get the next non-debug instruction, if one exists.
static T max(T a, T b)
Pick the maximum value.
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
llvm::PointerType * getDefaultAnonymousTapeType(llvm::LLVMContext &C)
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.
bool attributeKnownFunctions(llvm::Function &F)
static bool anyJuliaObjects(llvm::Type *T)
ReturnType
Potential return type of generated functions.
@ 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)
llvm::Value * is_left(llvm::IRBuilder<> &B, llvm::Value *side, bool byRef, bool cublas)
static bool isNoAlias(const llvm::CallBase *call)
static llvm::Type * FloatToIntTy(llvm::Type *T)
Convert a floating point type to an integer type of the same size.
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
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)
static unsigned getGPUSharedAddrSpace(const llvm::Triple &TT)
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)
static size_t convertRRootCountFromString(llvm::StringRef str)
static bool isGPUArch(const llvm::Triple &TT)
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,...
llvm::Value * get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res)
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.
static llvm::PointerType * getUnqual(llvm::Type *T)
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="")
llvm::Value * nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V)
Create function to computer nearest power of two.
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)
static bool isCertainPrint(const llvm::StringRef name)
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask=nullptr)
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)
llvm::Optional< BlasInfo > extractBLAS(llvm::StringRef in)
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
static std::map< K, V >::iterator insert_or_assign2(std::map< K, V > &map, K key, V val)
Insert into a map.
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,...
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
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)
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)
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
static llvm::Type * convertSRetTypeFromString(llvm::StringRef str, llvm::LLVMContext *C=nullptr)
static bool isDebugFunction(llvm::Function *called)
static void insertBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
static bool isReturned(llvm::Instruction *inst)
Check whether this instruction is returned.
std::vector< std::tuple< llvm::Type *, size_t, size_t > > parseTrueType(const llvm::MDNode *, DerivativeMode, bool const_src)
static bool isPointerArithmeticInst(const llvm::Value *V, bool includephi=true, bool includebin=true)
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
bool isNVLoad(const llvm::Value *V)
static llvm::Function * isCalledFunction(llvm::Value *val)
static bool isFortranMPICall(llvm::StringRef Name)
True if Name uses a Fortran ABI mangling of an MPI routine (trailing underscore(s),...
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
static bool containsOnlyAtMostTopBit(const llvm::Value *V, llvm::Type *FT, const llvm::DataLayout &dl, llvm::Type **vFT=nullptr)
static llvm::PointerType * changePointerAddrSpace(llvm::PointerType *PT, unsigned AddressSpace)
The same pointer type, in a different address space.
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.
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)
static llvm::SetVector< llvm::Value * > getBaseObjects(llvm::Value *V, bool offsetAllowed=true)
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)
void EmitWarning(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::BasicBlock *BB, const Args &...args)
static bool isReadOnlyOrThrow(const llvm::Function *F)
static bool isSpecialPtr(llvm::Type *Ty)
static llvm::Instruction * getFirstNonPHI(llvm::BasicBlock *B)
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.
static bool hasNoCache(llvm::Value *op)
static llvm::StructType * getMPIHelper(llvm::LLVMContext &Context)
llvm::CallInst * CreateDealloc(llvm::IRBuilder<> &B, llvm::Value *ToFree)
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)
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,...
static llvm::Type * getSubType(llvm::Type *T)
static llvm::Instruction * getNextNonDebugInstruction(llvm::Instruction *Z)
Get the next non-debug instruction, erring if none exists.
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)
llvm::Value * moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype, llvm::Value *sret, llvm::Type *root_ty, llvm::Value *rootRet, size_t rootOffset, SRetRootMovement direction)
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,...
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 ...
void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal, llvm::Value *shadow, const char *Message, llvm::DebugLoc &&loc, llvm::Instruction *orig)
llvm::cl::opt< bool > EnzymeJuliaAddrLoad
static void dumpSet(const llvm::SmallPtrSetImpl< T * > &o)
Print a set.
static bool isConditionalBranch(const llvm::Value *V)
static llvm::Function * getFunctionFromCall(T *op)
static llvm::Value * getBranchCondition(llvm::Value *V)
The condition of a branch isConditionalBranch says yes to.
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)
@ IllegalReplaceFicticiousPHIs
static llvm::Value * checkedDiv(bool strongZero, llvm::IRBuilder<> &Builder2, llvm::Value *idiff, llvm::Value *pres, const llvm::Twine &Name="")
static T min(T a, T b)
Pick the maximum value.
static bool hasSRetRRootsOrUnionSRet(llvm::CallBase *CB)
static std::tuple< llvm::StringRef, llvm::StringRef, llvm::StringRef > tripleSplitDollar(llvm::StringRef caller)
static bool isUnconditionalBranch(const llvm::Value *V)
llvm::SmallVector< llvm::Value *, 1 > getJuliaObjects(llvm::Value *v, llvm::IRBuilder<> &B)
@ RootPointerToSRetPointer
@ SRetPointerToRootPointer
static void addCallSiteNoCapture(llvm::CallBase *call, size_t idx)
static llvm::Type * IntToFloatTy(llvm::Type *T)
Convert a integer type to a floating point type of the same size.
static void resetClonedGUID(llvm::Function *F)
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...
static llvm::Instruction * getFirstNonPHIOrDbgOrLifetime(llvm::BasicBlock *B)
llvm::cl::opt< bool > EnzymePrintPerf
Print additional debug info relevant to performance.
static bool isAnyBranch(const llvm::Value *V)
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)
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 ...
static bool endsWith(llvm::StringRef string, llvm::StringRef suffix)
static llvm::Value * CreateSelect(llvm::IRBuilder<> &Builder2, llvm::Value *cmp, llvm::Value *tval, llvm::Value *fval, const llvm::Twine &Name="")
static void setBranchCondition(llvm::Value *V, llvm::Value *Cond)
static std::string convertSRetTypeToString(llvm::Type *T)
static llvm::SmallVector< llvm::Value *, 1 > concat_values(T &&...t)
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
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)
static constexpr const char * MixedActivityHint
Remedy appended to mismatched-activity warnings, which are emitted when a value Enzyme proved inactiv...
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
static bool isLocalReadOnlyOrThrow(const llvm::Function *F)
llvm::cl::opt< bool > EnzymeLapackCopy
ValueType
Classification of value as an original program variable, a derivative variable, neither,...
static llvm::Instruction * getFirstNonPHIOrDbg(llvm::BasicBlock *B)
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
static std::string to_string(const std::set< T > &us)
Output a set as a string.
static llvm::Attribute::AttrKind PrimalParamAttrsToPreserve[]
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)
llvm::cl::opt< bool > EnzymeNonPower2Cache
static llvm::Attribute::AttrKind ShadowParamAttrsToPreserve[]
llvm::SmallVector< std::tuple< llvm::Instruction *, llvm::Value *, size_t >, 1 > findAllUsersOf(llvm::Value *AI)
static void moveBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
static llvm::raw_ostream & operator<<(llvm::raw_ostream &os, ValueType mode)
static bool hasTerminator(llvm::BasicBlock *BB)
static std::map< K, V >::iterator insert_or_assign(std::map< K, V > &map, K &key, V &&val)
Insert into a map.
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)
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,...
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)
static void append(llvm::SmallVectorImpl< T > &vec)
static llvm::Optional< size_t > getAllocationIndexFromCall(const llvm::CallBase *op)
static llvm::Instruction * createUnconditionalBranch(llvm::BasicBlock *Target, PosT InsertBefore)
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.
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
void allUsesReplacedWith(llvm::Value *new_value) override final
virtual ~AssertingReplacingVH()
AssertingReplacingVH()=default
AssertingReplacingVH(llvm::Value *new_value)
Concrete SubType of a given value.
EnzymeFailure(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
EnzymeWarning(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
A holder class representing the results of running TypeAnalysis on a given function.
llvm::Type * fpType(llvm::LLVMContext &ctx, bool to_scalar=false) const
llvm::IntegerType * intType(llvm::LLVMContext &ctx) const
CountTrackedPointers(llvm::Type *T)