26#include "GradientUtils.h"
29#if LLVM_VERSION_MAJOR >= 16
30#include "llvm/Analysis/ScalarEvolution.h"
31#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
33#include "SCEV/ScalarEvolution.h"
34#include "SCEV/ScalarEvolutionExpander.h"
38#include "llvm/IR/BasicBlock.h"
39#include "llvm/IR/DerivedTypes.h"
40#include "llvm/IR/Function.h"
41#include "llvm/IR/GetElementPtrTypeIterator.h"
42#include "llvm/IR/IRBuilder.h"
43#include "llvm/IR/InlineAsm.h"
44#include "llvm/IR/Module.h"
45#include "llvm/IR/Type.h"
46#include "llvm/IR/Verifier.h"
48#if LLVM_VERSION_MAJOR >= 16
49#include "llvm/TargetParser/Triple.h"
51#include "llvm/ADT/Triple.h"
54#include "llvm-c/Core.h"
56#include "BlasAttributor.inc"
63 const void *, LLVMValueRef,
64 LLVMBuilderRef) =
nullptr;
67 LLVMValueRef, uint8_t,
68 LLVMValueRef *) =
nullptr;
70 LLVMValueRef, uint8_t) =
nullptr;
73 LLVMValueRef) =
nullptr;
74LLVMValueRef *(*EnzymePostCacheStore)(LLVMValueRef, LLVMBuilderRef,
75 uint64_t *size) =
nullptr;
82 LLVMValueRef) =
nullptr;
89 cl::desc(
"Use blas copy calls to cache matrices"));
92 cl::desc(
"Use blas copy calls to cache vectors"));
95 cl::desc(
"Use fast math on derivative compuation"));
97 "enzyme-memmove-warning", cl::init(
true), cl::Hidden,
98 cl::desc(
"Warn if using memmove implementation as a fallback for memmove"));
100 "enzyme-runtime-error", cl::init(
false), cl::Hidden,
101 cl::desc(
"Emit Runtime errors instead of compile time ones"));
104 "enzyme-check-nan", cl::init(
false), cl::Hidden,
105 cl::desc(
"Add NaN checks to all derivative intermediate values"));
108 "enzyme-non-power2-cache", cl::init(
false), cl::Hidden,
109 cl::desc(
"Disable caching of integers which are not a power of 2"));
112#define addAttribute addAttributeAtIndex
113#define getAttribute getAttributeAtIndex
116 for (
auto prefix : {
"_FortranAio",
"_FortranAModInteger",
"_FortranAModReal",
117 "_FortranAModuloInteger",
"_FortranAModuloReal"})
120 static const char *Names[] = {
131 "_FortranACharacterCompareScalar",
132 "_FortranACharacterCompareScalar1",
133 "_FortranACharacterCompareScalar2",
134 "_FortranACharacterCompareScalar4",
136 return llvm::is_contained(Names, name);
140 bool changed =
false;
141 if (F.getName() ==
"fprintf") {
142 for (
auto &arg : F.args()) {
143 if (arg.getType()->isPointerTy()) {
149 if (F.getName().contains(
"__enzyme_float") ||
150 F.getName().contains(
"__enzyme_double") ||
151 F.getName().contains(
"__enzyme_integer") ||
152 F.getName().contains(
"__enzyme_pointer") ||
153 F.getName().contains(
"__enzyme_todense") ||
154 F.getName().contains(
"__enzyme_ignore_derivatives") ||
155 F.getName().contains(
"__enzyme_iter") ||
156 F.getName().contains(
"__enzyme_virtualreverse")) {
158#if LLVM_VERSION_MAJOR >= 16
159 F.setOnlyReadsMemory();
160 F.setOnlyWritesMemory();
162 F.addFnAttr(Attribute::ReadNone);
164 if (!(F.getName().contains(
"__enzyme_todense") ||
165 F.getName().contains(
"__enzyme_ignore_derivatives"))) {
166 for (
auto &arg : F.args()) {
167 if (arg.getType()->isPointerTy()) {
168 arg.addAttr(Attribute::ReadNone);
174 if (F.getName() ==
"memcmp") {
176#if LLVM_VERSION_MAJOR >= 16
177 F.setOnlyAccessesArgMemory();
178 F.setOnlyReadsMemory();
180 F.addFnAttr(Attribute::ArgMemOnly);
181 F.addFnAttr(Attribute::ReadOnly);
183 F.addFnAttr(Attribute::NoUnwind);
184 F.addFnAttr(Attribute::NoRecurse);
185 F.addFnAttr(Attribute::WillReturn);
186 F.addFnAttr(Attribute::NoFree);
187 F.addFnAttr(Attribute::NoSync);
188 for (
int i = 0; i < 2; i++)
189 if (F.getFunctionType()->getParamType(i)->isPointerTy()) {
191 F.addParamAttr(i, Attribute::ReadOnly);
196 "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm") {
198 F.addFnAttr(Attribute::NoFree);
204 if (canonMPIName ==
"MPI_Irecv") {
205 auto FT = F.getFunctionType();
206 bool PointerABI =
true;
208 F.addFnAttr(Attribute::NoUnwind);
209 F.addFnAttr(Attribute::NoRecurse);
210 F.addFnAttr(Attribute::WillReturn);
211 F.addFnAttr(Attribute::NoFree);
212 F.addFnAttr(Attribute::NoSync);
213 if (FT->getParamType(0)->isPointerTy()) {
214 F.addParamAttr(0, Attribute::WriteOnly);
219 if (FT->getParamType(2)->isPointerTy()) {
221 F.addParamAttr(2, Attribute::ReadOnly);
223 if (FT->getParamType(6)->isPointerTy()) {
224 F.addParamAttr(6, Attribute::WriteOnly);
229#if LLVM_VERSION_MAJOR >= 16
230 F.setOnlyAccessesInaccessibleMemOrArgMem();
232 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
237 if (canonMPIName ==
"MPI_Isend") {
238 auto FT = F.getFunctionType();
239 bool PointerABI =
true;
241 F.addFnAttr(Attribute::NoUnwind);
242 F.addFnAttr(Attribute::NoRecurse);
243 F.addFnAttr(Attribute::WillReturn);
244 F.addFnAttr(Attribute::NoFree);
245 F.addFnAttr(Attribute::NoSync);
246 if (FT->getParamType(0)->isPointerTy()) {
247 F.addParamAttr(0, Attribute::ReadOnly);
252 if (FT->getParamType(2)->isPointerTy()) {
254 F.addParamAttr(2, Attribute::ReadOnly);
256 if (FT->getParamType(6)->isPointerTy()) {
257 F.addParamAttr(6, Attribute::WriteOnly);
262#if LLVM_VERSION_MAJOR >= 16
263 F.setOnlyAccessesInaccessibleMemOrArgMem();
265 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
269 if (canonMPIName ==
"MPI_Comm_rank" || canonMPIName ==
"MPI_Comm_size") {
270 auto FT = F.getFunctionType();
271 bool PointerABI =
true;
273 F.addFnAttr(Attribute::NoUnwind);
274 F.addFnAttr(Attribute::NoRecurse);
275 F.addFnAttr(Attribute::WillReturn);
276 F.addFnAttr(Attribute::NoFree);
277 F.addFnAttr(Attribute::NoSync);
280 if (FT->getParamType(0)->isPointerTy()) {
282 F.addParamAttr(0, Attribute::ReadOnly);
284 if (FT->getParamType(1)->isPointerTy()) {
285 F.addParamAttr(1, Attribute::WriteOnly);
291#if LLVM_VERSION_MAJOR >= 16
292 F.setOnlyAccessesInaccessibleMemOrArgMem();
294 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
298 if (canonMPIName ==
"MPI_Wait") {
300 F.addFnAttr(Attribute::NoUnwind);
301 F.addFnAttr(Attribute::NoRecurse);
302 F.addFnAttr(Attribute::WillReturn);
303 F.addFnAttr(Attribute::NoFree);
304 F.addFnAttr(Attribute::NoSync);
305 if (F.getFunctionType()->getParamType(0)->isPointerTy()) {
308 if (F.getFunctionType()->getParamType(1)->isPointerTy()) {
309 F.addParamAttr(1, Attribute::WriteOnly);
313 if (canonMPIName ==
"MPI_Waitall") {
315 F.addFnAttr(Attribute::NoUnwind);
316 F.addFnAttr(Attribute::NoRecurse);
317 F.addFnAttr(Attribute::WillReturn);
318 F.addFnAttr(Attribute::NoFree);
319 F.addFnAttr(Attribute::NoSync);
320 if (F.getFunctionType()->getParamType(1)->isPointerTy()) {
323 if (F.getFunctionType()->getParamType(2)->isPointerTy()) {
324 F.addParamAttr(2, Attribute::WriteOnly);
329 std::map<std::string, int> MPI_TYPE_ARGS = {
330 {
"MPI_Send", 2}, {
"MPI_Ssend", 2}, {
"MPI_Bsend", 2},
331 {
"MPI_Recv", 2}, {
"MPI_Brecv", 2}, {
"PMPI_Send", 2},
332 {
"PMPI_Ssend", 2}, {
"PMPI_Bsend", 2}, {
"PMPI_Recv", 2},
335 {
"MPI_Isend", 2}, {
"MPI_Irecv", 2}, {
"PMPI_Isend", 2},
338 {
"MPI_Reduce", 3}, {
"PMPI_Reduce", 3},
340 {
"MPI_Allreduce", 3}, {
"PMPI_Allreduce", 3}};
342 auto found = MPI_TYPE_ARGS.find(canonMPIName.str());
343 if (found != MPI_TYPE_ARGS.end()) {
344 for (
auto user : F.users()) {
345 if (
auto CI = dyn_cast<CallBase>(user))
346 if (CI->getCalledFunction() == &F) {
348 dyn_cast<Constant>(CI->getArgOperand(found->second))) {
349 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
350 C = CE->getOperand(0);
352 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
353 if (GV->getName() ==
"ompi_mpi_cxx_bool") {
356 AttributeList::FunctionIndex,
357 Attribute::get(CI->getContext(),
"enzyme_inactive"));
366 if (F.getName() ==
"omp_get_max_threads" ||
367 F.getName() ==
"omp_get_thread_num") {
369#if LLVM_VERSION_MAJOR >= 16
370 F.setOnlyAccessesInaccessibleMemory();
371 F.setOnlyReadsMemory();
373 F.addFnAttr(Attribute::InaccessibleMemOnly);
374 F.addFnAttr(Attribute::ReadOnly);
377 if (F.getName() ==
"frexp" || F.getName() ==
"frexpf" ||
378 F.getName() ==
"frexpl") {
380#if LLVM_VERSION_MAJOR >= 16
381 F.setOnlyAccessesArgMemory();
383 F.addFnAttr(Attribute::ArgMemOnly);
385 F.addParamAttr(1, Attribute::WriteOnly);
387 if (F.getName() ==
"__fd_sincos_1" || F.getName() ==
"__fd_cos_1" ||
388 F.getName() ==
"__mth_i_ipowi") {
390#if LLVM_VERSION_MAJOR >= 16
391 F.setOnlyReadsMemory();
392 F.setOnlyWritesMemory();
394 F.addFnAttr(Attribute::ReadNone);
398 const char *NonEscapingFns[] = {
400 "julia.get_pgcstack",
403 "_ZNSt6chrono3_V212steady_clock3nowEv",
404 "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_"
406 "_ZNKSt8__detail20_Prime_rehash_policy14_M_need_rehashEmmm",
414 "cublasSetStream_v2",
416 "cuDeviceGetMemPool",
417 "cuStreamSynchronize",
423 "cuDriverGetVersion",
424 "cudaRuntimeGetVersion",
426 "cuMemPoolGetAttribute",
428 "cuDeviceGetAttribute",
429 "cuDevicePrimaryCtxRetain",
431 for (
auto fname : NonEscapingFns)
435 AttributeList::FunctionIndex,
436 Attribute::get(F.getContext(),
"enzyme_no_escaping_allocation"));
442 const char *FlangRuntimeQueries[] = {
445 "_FortranADateAndTime",
447 "_FortranASystemClockCount",
448 "_FortranASystemClockCountRate",
449 "_FortranASystemClockCountMax",
452 "_FortranAArgumentCount",
453 "_FortranAGetCommand",
454 "_FortranAGetCommandArgument",
455 "_FortranAGetEnvVariable",
462 if (llvm::is_contained(FlangRuntimeQueries, name)) {
464 F.addFnAttr(Attribute::NoFree);
466 AttributeList::FunctionIndex,
467 Attribute::get(F.getContext(),
"enzyme_no_escaping_allocation"));
473 name ==
"_FortranAStopStatementText" ||
474 name ==
"_FortranAReportFatalUserError" || name ==
"_FortranAExit" ||
475 name ==
"_FortranAAbort") {
478 AttributeList::FunctionIndex,
479 Attribute::get(F.getContext(),
"enzyme_no_escaping_allocation"));
483 const char *OpenMPRuntimeCalls[] = {
484 "__kmpc_global_thread_num",
485 "__kmpc_for_static_init_4",
486 "__kmpc_for_static_init_4u",
487 "__kmpc_for_static_init_8",
488 "__kmpc_for_static_init_8u",
489 "__kmpc_for_static_fini",
492 "__kmpc_end_critical",
493 "omp_get_thread_num",
494 "omp_get_num_threads",
495 "omp_get_max_threads",
497 if (llvm::is_contained(OpenMPRuntimeCalls, name)) {
499 F.addFnAttr(Attribute::NoFree);
501 AttributeList::FunctionIndex,
502 Attribute::get(F.getContext(),
"enzyme_no_escaping_allocation"));
504 changed |= attributeTablegen(F);
508void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj,
511 CustomZero(wrap(&Builder), wrap(T), wrap(obj), isTape);
513 Builder.CreateStore(Constant::getNullValue(T), obj);
518 llvm::IRBuilder<> &B) {
519 SmallVector<llvm::Instruction *, 2> res;
523 for (
size_t i = 0; i < size; i++) {
524 res.push_back(cast<Instruction>(unwrap(ptr[i])));
538 bool ZeroInit, llvm::Type *RT) {
540 llvm::PointerType *allocType;
542 auto i64 = Type::getInt64Ty(newFunc->getContext());
543 BasicBlock *BB = BasicBlock::Create(M.getContext(),
"entry", newFunc);
545 auto P = B.CreatePHI(i64, 1);
546 CallInst *malloccall;
547 Instruction *SubZero =
nullptr;
550 custom = F->getName() !=
"malloc";
552 allocType = cast<PointerType>(malloccall->getType());
553 if (ZeroInit && !SubZero)
555 BB->eraseFromParent();
558 Type *types[] = {allocType, Type::getInt64Ty(M.getContext()),
559 Type::getInt64Ty(M.getContext())};
560 std::string name =
"__enzyme_exponentialallocation";
564 name +=
".custom@" + std::to_string((
size_t)RT);
566 FunctionType *FT = FunctionType::get(allocType, types,
false);
568 if (newFunc->hasFnAttribute(
"enzymejl_world")) {
569 AL = AL.addFnAttribute(newFunc->getContext(),
570 newFunc->getFnAttribute(
"enzymejl_world"));
572 Function *F = cast<Function>(M.getOrInsertFunction(name, FT, AL).getCallee());
577 F->setLinkage(Function::LinkageTypes::InternalLinkage);
578 F->addFnAttr(Attribute::AlwaysInline);
579 F->addFnAttr(Attribute::NoUnwind);
580 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
581 BasicBlock *grow = BasicBlock::Create(M.getContext(),
"grow", F);
582 BasicBlock *ok = BasicBlock::Create(M.getContext(),
"ok", F);
584 IRBuilder<> B(entry);
586 Argument *ptr = F->arg_begin();
588 Argument *size = ptr + 1;
589 size->setName(
"size");
590 Argument *tsize = size + 1;
591 tsize->setName(
"tsize");
593 Value *hasOne = B.CreateICmpNE(
594 B.CreateAnd(size, ConstantInt::get(size->getType(), 1,
false)),
595 ConstantInt::get(size->getType(), 0,
false));
599 B.CreateAnd(B.CreateICmpULT(B.CreateCall(popCnt, {size}),
600 ConstantInt::get(types[1], 3,
false)),
604 B.SetInsertPoint(grow);
608 {size, ConstantInt::getTrue(M.getContext())});
610 B.CreateShl(tsize, B.CreateSub(ConstantInt::get(types[1], 64,
false), lz,
615 Value *halfNext = B.CreateLShr(next, ConstantInt::get(next->getType(), 1));
617 B.CreateICmpEQ(size, ConstantInt::get(size->getType(), 1));
618 Value *prevSize = B.CreateSelect(
619 isFirstSize, ConstantInt::get(next->getType(), 0), halfNext);
621 auto Arch = llvm::Triple(M.getTargetTriple()).getArch();
622 bool forceMalloc = Arch == Triple::nvptx || Arch == Triple::nvptx64;
624 if (!custom && !forceMalloc) {
625 auto reallocF = M.getOrInsertFunction(
"realloc", allocType, allocType,
626 Type::getInt64Ty(M.getContext()));
628 Value *args[] = {B.CreatePointerCast(ptr, allocType), next};
629 gVal = B.CreateCall(reallocF, args);
631 Value *tsize = ConstantInt::get(
633 newFunc->getParent()->getDataLayout().getTypeAllocSizeInBits(RT) / 8);
634 auto elSize = B.CreateUDiv(next, tsize,
"",
true);
635 Instruction *SubZero =
nullptr;
640 cast<PointerType>(gVal->getType())->getAddressSpace());
641 gVal = B.CreatePointerCast(gVal, bTy);
642 auto pVal = B.CreatePointerCast(ptr, gVal->getType());
644 Value *margs[] = {gVal, pVal, prevSize,
645 ConstantInt::getFalse(M.getContext())};
646 Type *tys[] = {margs[0]->getType(), margs[1]->getType(),
647 margs[2]->getType()};
649 B.CreateCall(memsetF, margs);
652 IRBuilder<> BB(SubZero);
653 Value *zeroSize = BB.CreateSub(next, prevSize);
654 Value *tmp = SubZero->getOperand(0);
655 Type *tmpT = tmp->getType();
656 tmp = BB.CreatePointerCast(tmp, bTy);
657 tmp = BB.CreateInBoundsGEP(Type::getInt8Ty(tmp->getContext()), tmp,
659 tmp = BB.CreatePointerCast(tmp, tmpT);
660 SubZero->setOperand(0, tmp);
661 SubZero->setOperand(2, zeroSize);
666 Value *zeroSize = B.CreateSub(next, prevSize);
668 Value *margs[] = {B.CreateInBoundsGEP(B.getInt8Ty(), gVal, prevSize),
669 B.getInt8(0), zeroSize, B.getFalse()};
670 Type *tys[] = {margs[0]->getType(), margs[2]->getType()};
672 B.CreateCall(memsetF, margs);
674 gVal = B.CreatePointerCast(gVal, ptr->getType());
677 B.SetInsertPoint(ok);
678 auto phi = B.CreatePHI(ptr->getType(), 2);
679 phi->addIncoming(gVal, grow);
680 phi->addIncoming(ptr, entry);
686 llvm::Type *T, llvm::Value *OuterCount,
687 llvm::Value *InnerCount,
688 const llvm::Twine &Name,
689 llvm::CallInst **caller,
bool ZeroMem) {
690 auto newFunc = B.GetInsertBlock()->getParent();
692 Value *tsize = ConstantInt::get(
693 InnerCount->getType(),
694 newFunc->getParent()->getDataLayout().getTypeAllocSizeInBits(T) / 8);
702 B.CreateMul(tsize, InnerCount,
"",
true,
707 newFunc, ZeroMem, T),
710 *caller = realloccall;
715 const Twine &Name, CallInst **caller,
716 Instruction **ZeroMem,
bool isDefault) {
718 auto &M = *Builder.GetInsertBlock()->getParent()->getParent();
719 auto AlignI = M.getDataLayout().getTypeAllocSizeInBits(T) / 8;
733 Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext(), 0);
734 Value *AllocCount =
Count;
735 Type *AllocSizeTy =
Count->getType();
737 IntPtrTy->isIntegerTy() &&
738 AllocSizeTy->getIntegerBitWidth() > IntPtrTy->getIntegerBitWidth()) {
739 AllocCount = Builder.CreateTrunc(
Count, IntPtrTy, Name +
".size");
740 AllocSizeTy = IntPtrTy;
742 auto Align = ConstantInt::get(AllocSizeTy, AlignI);
744 CallInst *malloccall =
nullptr;
746 LLVMValueRef wzeromem =
nullptr;
748 wrap(Align), isDefault,
749 ZeroMem ? &wzeromem :
nullptr));
750 if (isa<UndefValue>(res))
752 if (isa<Constant>(res))
754 if (
auto I = dyn_cast<Instruction>(res))
757 malloccall = dyn_cast<CallInst>(res);
758 if (malloccall ==
nullptr) {
759 malloccall = cast<CallInst>(cast<Instruction>(res)->getOperand(0));
762 *ZeroMem = cast_or_null<Instruction>(unwrap(wzeromem));
766#if LLVM_VERSION_MAJOR >= 24
767 res = Builder.CreateMalloc(AllocSizeTy, Align, AllocCount,
nullptr, Name);
768#elif LLVM_VERSION_MAJOR > 17
770 Builder.CreateMalloc(AllocSizeTy, T, Align, AllocCount,
nullptr, Name);
772 if (Builder.GetInsertPoint() == Builder.GetInsertBlock()->end()) {
773 res = CallInst::CreateMalloc(Builder.GetInsertBlock(), AllocSizeTy, T,
774 Align, AllocCount,
nullptr, Name);
775 Builder.SetInsertPoint(Builder.GetInsertBlock());
777 res = CallInst::CreateMalloc(&*Builder.GetInsertPoint(), AllocSizeTy, T,
778 Align, AllocCount,
nullptr, Name);
780 if (!cast<Instruction>(res)->getParent())
781 Builder.Insert(cast<Instruction>(res));
784 malloccall = dyn_cast<CallInst>(res);
785 if (malloccall ==
nullptr) {
786 malloccall = cast<CallInst>(cast<Instruction>(res)->getOperand(0));
793 if (
auto BI = dyn_cast<BinaryOperator>(malloccall->getArgOperand(0))) {
794 if (BI->getOpcode() == BinaryOperator::Mul) {
795 if ((BI->getOperand(0) == Align && BI->getOperand(1) == AllocCount) ||
796 (BI->getOperand(1) == Align && BI->getOperand(0) == AllocCount))
797 BI->setHasNoSignedWrap(
true);
798 BI->setHasNoUnsignedWrap(
true);
802 if (
auto ci = dyn_cast<ConstantInt>(
Count)) {
803#if LLVM_VERSION_MAJOR >= 14
804 malloccall->addDereferenceableRetAttr(ci->getLimitedValue() * AlignI);
805#if !defined(FLANG) && !defined(ROCM)
806 AttrBuilder B(ci->getContext());
810 B.addDereferenceableOrNullAttr(ci->getLimitedValue() * AlignI);
811 malloccall->setAttributes(malloccall->getAttributes().addRetAttributes(
812 malloccall->getContext(), B));
814 malloccall->addDereferenceableAttr(llvm::AttributeList::ReturnIndex,
815 ci->getLimitedValue() * AlignI);
816 malloccall->addDereferenceableOrNullAttr(llvm::AttributeList::ReturnIndex,
817 ci->getLimitedValue() * AlignI);
822#if LLVM_VERSION_MAJOR >= 14
823 malloccall->addAttributeAtIndex(AttributeList::ReturnIndex,
825 malloccall->addAttributeAtIndex(AttributeList::ReturnIndex,
828 malloccall->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias);
829 malloccall->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
833 *caller = malloccall;
836 auto PT = cast<PointerType>(malloccall->getType());
837 Value *tozero = malloccall;
839 bool needsCast =
false;
840#if LLVM_VERSION_MAJOR < 17
841#if LLVM_VERSION_MAJOR >= 15
842 if (PT->getContext().supportsTypedPointers()) {
844 needsCast = !PT->getPointerElementType()->isIntegerTy(8);
845#if LLVM_VERSION_MAJOR >= 15
850 tozero = Builder.CreatePointerCast(
852 PT->getAddressSpace()));
860 tozero, ConstantInt::get(Type::getInt8Ty(malloccall->getContext()), 0),
861 Builder.CreateMul(Align, AllocCount,
"",
true,
true),
862 ConstantInt::getFalse(malloccall->getContext())};
863 Type *tys[] = {args[0]->getType(), args[2]->getType()};
865 *ZeroMem = Builder.CreateCall(
872 CallInst *res =
nullptr;
875 res = dyn_cast_or_null<CallInst>(
880 Builder.CreatePointerCast(ToFree,
getInt8PtrTy(ToFree->getContext()));
881#if LLVM_VERSION_MAJOR > 17
882 res = cast<CallInst>(Builder.CreateFree(ToFree));
884 if (Builder.GetInsertPoint() == Builder.GetInsertBlock()->end()) {
885 res = cast<CallInst>(
886 CallInst::CreateFree(ToFree, Builder.GetInsertBlock()));
887 Builder.SetInsertPoint(Builder.GetInsertBlock());
889 res = cast<CallInst>(
890 CallInst::CreateFree(ToFree, &*Builder.GetInsertPoint()));
892 if (!cast<Instruction>(res)->getParent())
893 Builder.Insert(cast<Instruction>(res));
895#if LLVM_VERSION_MAJOR >= 14
896 res->addAttributeAtIndex(AttributeList::FirstArgIndex, Attribute::NonNull);
898 res->addAttribute(AttributeList::FirstArgIndex, Attribute::NonNull);
905 const llvm::DiagnosticLocation &Loc,
906 const llvm::Instruction *CodeRegion)
907 :
EnzymeWarning(RemarkName, Loc, CodeRegion->getParent()->getParent()) {}
910 const llvm::DiagnosticLocation &Loc,
911 const llvm::Function *CodeRegion)
912 : DiagnosticInfoUnsupported(*CodeRegion, RemarkName, Loc, DS_Warning) {}
915 const llvm::DiagnosticLocation &Loc,
916 const llvm::Instruction *CodeRegion)
917 :
EnzymeFailure(RemarkName, Loc, CodeRegion->getParent()->getParent()) {}
920 const llvm::DiagnosticLocation &Loc,
921 const llvm::Function *CodeRegion)
922 : DiagnosticInfoUnsupported(*CodeRegion, RemarkName, Loc) {}
926 if (
auto VT = dyn_cast<VectorType>(T)) {
927#if LLVM_VERSION_MAJOR >= 12
928 auto len = VT->getElementCount().getFixedValue();
930 auto len = VT->getNumElements();
932 return "vec" + std::to_string(len) +
tofltstr(VT->getElementType());
934 switch (T->getTypeID()) {
937 case Type::FloatTyID:
939 case Type::DoubleTyID:
941 case Type::X86_FP80TyID:
943 case Type::BFloatTyID:
945 case Type::FP128TyID:
947 case Type::PPC_FP128TyID:
950 llvm_unreachable(
"Invalid floating type");
955 llvm::Constant *s = llvm::ConstantDataArray::getString(M.getContext(), Str);
956 auto *gv =
new llvm::GlobalVariable(
957 M, s->getType(),
true, llvm::GlobalValue::PrivateLinkage, s,
".str");
958 gv->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
959 Value *Idxs[2] = {ConstantInt::get(Type::getInt32Ty(M.getContext()), 0),
960 ConstantInt::get(Type::getInt32Ty(M.getContext()), 0)};
961 return ConstantExpr::getInBoundsGetElementPtr(s->getType(), gv, Idxs);
965 SmallPtrSet<llvm::Instruction *, 8> visited;
967 if (visited.contains(inst))
969 visited.insert(inst);
972 if (
auto dbgLoc = inst->getDebugLoc()) {
973 auto *loc = dbgLoc.get();
975 if (
auto *scope = loc->getScope()) {
976 StringRef name = scope->getName();
978 while (!name.empty() && name.back() ==
';')
979 name = name.drop_back();
980 if (
auto *file = scope->getFile()) {
981 StringRef dir = file->getDirectory();
982 StringRef fn = file->getFilename();
983 ss <<
" in '" << name <<
"' at ";
986 ss << fn <<
":" << loc->getLine() <<
"\n";
988 ss <<
" in '" << name <<
"' at unknown:" << loc->getLine() <<
"\n";
991 loc = loc->getInlinedAt();
996 Function *f = inst->getParent()->getParent();
999 SmallVector<CallInst *, 4> callersWithDbg;
1000 for (
auto *U : f->users()) {
1001 auto *CI = dyn_cast<CallInst>(U);
1004 if (!CI->getDebugLoc())
1006 callersWithDbg.push_back(CI);
1009 if (callersWithDbg.empty())
1013 SmallVector<CallInst *, 4> uniqueCallSites;
1014 SmallPtrSet<const MDNode *, 4> seenMD;
1015 for (
auto *CI : callersWithDbg) {
1016 if (seenMD.insert(CI->getDebugLoc().getAsMDNode()).second)
1017 uniqueCallSites.push_back(CI);
1020 if (uniqueCallSites.size() > 1) {
1021 ss <<
" (multiple call sites)\n";
1023 }
else if (uniqueCallSites.size() == 1) {
1024 inst = uniqueCallSites[0];
1032 llvm::Value *shadow,
const char *Message,
1033 llvm::DebugLoc &&loc, llvm::Instruction *orig) {
1034 Module &M = *B.GetInsertBlock()->getParent()->getParent();
1035 std::string name =
"__enzyme_runtimeinactiveerr";
1037 static int count = 0;
1038 name += std::to_string(count);
1041 FunctionType *FT = FunctionType::get(Type::getVoidTy(M.getContext()),
1042 {getInt8PtrTy(M.getContext()),
1043 getInt8PtrTy(M.getContext()),
1044 getInt8PtrTy(M.getContext())},
1047 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1050 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1051 F->addFnAttr(Attribute::AlwaysInline);
1055 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1056 BasicBlock *error = BasicBlock::Create(M.getContext(),
"error", F);
1057 BasicBlock *end = BasicBlock::Create(M.getContext(),
"end", F);
1059 auto prim = F->arg_begin();
1060 prim->setName(
"primal");
1061 auto shadow = prim + 1;
1062 shadow->setName(
"shadow");
1063 auto msg = prim + 2;
1064 msg->setName(
"msg");
1066 IRBuilder<> EB(entry);
1067 EB.CreateCondBr(EB.CreateICmpEQ(prim, shadow), error, end);
1069 EB.SetInsertPoint(error);
1075 FunctionType::get(Type::getInt32Ty(M.getContext()),
1076 {getInt8PtrTy(M.getContext())},
false);
1078 auto PutsF = M.getOrInsertFunction(
"puts", FT);
1079 EB.CreateCall(PutsF, msg);
1082 FunctionType::get(Type::getVoidTy(M.getContext()),
1083 {Type::getInt32Ty(M.getContext())},
false);
1085 auto ExitF = M.getOrInsertFunction(
"exit", FT2);
1086 EB.CreateCall(ExitF,
1087 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
1089 EB.CreateUnreachable();
1091 EB.SetInsertPoint(end);
1095 std::string Message2 = Message;
1098 raw_string_ostream ss(
str);
1099 ss << Message <<
"\n";
1101 Message2 = ss.str();
1103 Value *args[] = {B.CreatePointerCast(primal,
getInt8PtrTy(M.getContext())),
1104 B.CreatePointerCast(shadow,
getInt8PtrTy(M.getContext())),
1106 auto call = B.CreateCall(F, args);
1107 call->setDebugLoc(loc);
1112 return Type::getDoubleTy(ctx);
1114 return Type::getFloatTy(ctx);
1117 return Type::getFloatTy(ctx);
1118 return VectorType::get(Type::getFloatTy(ctx), 2,
false);
1121 return Type::getDoubleTy(ctx);
1122 return VectorType::get(Type::getDoubleTy(ctx), 2,
false);
1124 assert(
false &&
"Unreachable");
1131 return IntegerType::get(ctx, 64);
1133 return IntegerType::get(ctx, 32);
1136bool isAtomic(Value *origptr,
bool AtomicAdd, Function *newFunc) {
1139 if (!origptr || !newFunc)
1145 bool Atomic = AtomicAdd;
1146 auto Arch = llvm::Triple(newFunc->getParent()->getTargetTriple()).getArch();
1150 if (isa<AllocaInst>(TmpOrig) &&
1151 (Arch == Triple::nvptx || Arch == Triple::nvptx64 ||
1152 Arch == Triple::amd_target)) {
1161 Module &M, Type *elementType,
unsigned dstalign,
unsigned srcalign,
1162 unsigned dstaddr,
unsigned srcaddr,
unsigned bitwidth,
bool runtimeActivity,
1164 assert(elementType->isFloatingPointTy());
1165 std::string name =
"__enzyme_memcpy";
1167 name += std::to_string(bitwidth);
1168 name +=
"add_" +
tofltstr(elementType) +
"da" + std::to_string(dstalign) +
1169 "sa" + std::to_string(srcalign);
1171 name +=
"dadd" + std::to_string(dstaddr);
1173 name +=
"sadd" + std::to_string(srcaddr);
1174 if (runtimeActivity)
1175 name +=
"_runtime_activity";
1178 std::vector<Type *> argTys = {
getPointerType(elementType, dstaddr),
1180 IntegerType::get(M.getContext(), bitwidth)};
1181 if (runtimeActivity) {
1182 argTys.push_back(Type::getInt1Ty(M.getContext()));
1183 argTys.push_back(Type::getInt1Ty(M.getContext()));
1186 FunctionType::get(Type::getVoidTy(M.getContext()), argTys,
false);
1188 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1193 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1194#if LLVM_VERSION_MAJOR >= 16
1195 F->setOnlyAccessesArgMemory();
1197 F->addFnAttr(Attribute::ArgMemOnly);
1199 F->addFnAttr(Attribute::NoUnwind);
1200 F->addFnAttr(Attribute::AlwaysInline);
1204 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1205 BasicBlock *body = BasicBlock::Create(M.getContext(),
"for.body", F);
1206 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
1208 Argument *dst = &*F->arg_begin();
1209 dst->setName(
"dst");
1210 Argument *src = dst + 1;
1211 src->setName(
"src");
1212 Argument *num = src + 1;
1213 num->setName(
"num");
1215 Argument *dst_inactive =
nullptr;
1216 Argument *src_inactive =
nullptr;
1217 if (runtimeActivity) {
1218 dst_inactive = num + 1;
1219 dst_inactive->setName(
"dst_inactive");
1220 src_inactive = dst_inactive + 1;
1221 src_inactive->setName(
"src_inactive");
1224 BasicBlock *checkSrc =
nullptr;
1225 BasicBlock *memsetDst =
nullptr;
1226 if (runtimeActivity) {
1227 checkSrc = BasicBlock::Create(M.getContext(),
"check_src", F, body);
1228 memsetDst = BasicBlock::Create(M.getContext(),
"memset_dst", F, body);
1232 IRBuilder<> B(entry);
1233 Value *cond = B.CreateICmpEQ(num, ConstantInt::get(num->getType(), 0));
1234 if (runtimeActivity) {
1235 cond = B.CreateOr(cond, dst_inactive);
1236 B.CreateCondBr(cond, end, checkSrc);
1238 B.SetInsertPoint(checkSrc);
1239 B.CreateCondBr(src_inactive, memsetDst, body);
1241 B.SetInsertPoint(memsetDst);
1242 auto elSize = (M.getDataLayout().getTypeSizeInBits(elementType) + 7) / 8;
1243 Value *dst_i8 = B.CreatePointerCast(
1245 B.CreateMemSet(dst_i8, B.getInt8(0),
1246 B.CreateMul(num, ConstantInt::get(num->getType(), elSize),
1248 MaybeAlign(dstalign));
1251 B.CreateCondBr(cond, end, body);
1255 auto elSize = (M.getDataLayout().getTypeSizeInBits(elementType) + 7) / 8;
1257 IRBuilder<> B(body);
1258 B.setFastMathFlags(
getFast());
1259 PHINode *idx = B.CreatePHI(num->getType(), 2,
"idx");
1260 idx->addIncoming(ConstantInt::get(num->getType(), 0),
1261 runtimeActivity ? checkSrc : entry);
1263 Value *dsti = B.CreateInBoundsGEP(elementType, dst, idx,
"dst.i");
1264 LoadInst *dstl = B.CreateLoad(elementType, dsti,
"dst.i.l");
1265 StoreInst *dsts = B.CreateStore(Constant::getNullValue(elementType), dsti);
1270 if (elSize % dstalign == 0) {
1272 }
else if (dstalign % elSize == 0) {
1287 if (elSize % srcalign == 0) {
1289 }
else if (srcalign % elSize == 0) {
1302 dstl->setAlignment(Align(dstalign));
1303 dsts->setAlignment(Align(dstalign));
1306 Value *srci = B.CreateInBoundsGEP(elementType, src, idx,
"src.i");
1308 B.CreateAtomicRMW(AtomicRMWInst::BinOp::FAdd, srci, dstl,
1309 MaybeAlign(srcalign), AtomicOrdering::Monotonic,
1312 LoadInst *srcl = B.CreateLoad(elementType, srci,
"src.i.l");
1313 StoreInst *srcs = B.CreateStore(B.CreateFAdd(srcl, dstl), srci);
1315 srcl->setAlignment(Align(srcalign));
1316 srcs->setAlignment(Align(srcalign));
1321 B.CreateNUWAdd(idx, ConstantInt::get(num->getType(), 1),
"idx.next");
1322 idx->addIncoming(next, body);
1323 B.CreateCondBr(B.CreateICmpEQ(num, next), end, body);
1334 Value *
const base, Value *lda, Value *row,
1336 Type *intType = row->getType();
1338 layout ? B.CreateICmpEQ(layout, ConstantInt::get(layout->getType(), 101))
1340 Value *offset =
nullptr;
1342 offset = B.CreateMul(
1343 row,
CreateSelect(B, is_row_maj, lda, ConstantInt::get(intType, 1)));
1344 offset = B.CreateAdd(
1347 ConstantInt::get(intType, 1), lda)));
1349 offset = B.CreateMul(row, lda);
1355 if (base->getType()->isIntegerTy())
1356 ptr = B.CreateIntToPtr(ptr,
getUnqual(fpType));
1358#if LLVM_VERSION_MAJOR < 17
1359#if LLVM_VERSION_MAJOR >= 15
1360 if (ptr->getContext().supportsTypedPointers()) {
1362 if (fpType != ptr->getType()->getPointerElementType()) {
1363 ptr = B.CreatePointerCast(
1366 cast<PointerType>(ptr->getType())->getAddressSpace()));
1368#if LLVM_VERSION_MAJOR >= 15
1372 ptr = B.CreateGEP(fpType, ptr, offset);
1374 if (base->getType()->isIntegerTy()) {
1375 ptr = B.CreatePtrToInt(ptr, base->getType());
1376 }
else if (ptr->getType() != base->getType()) {
1377 ptr = B.CreatePointerCast(ptr, base->getType());
1383 BlasInfo blas,
bool byRef, llvm::Value *layout,
1384 llvm::Value *islower, llvm::Value *A, llvm::Value *lda,
1387 const bool cublasv2 =
1388 blas.
prefix ==
"cublas" && StringRef(blas.
suffix).contains(
"v2");
1390 const bool cublas = blas.
prefix ==
"cublas";
1391 auto &M = *B.GetInsertBlock()->getParent()->getParent();
1393 llvm::Type *intType = N->getType();
1395 auto fnc_name =
"__enzyme_copy_lower_to_upper" + blas.
floatType +
1398 SmallVector<Type *, 1> tys = {islower->getType(), A->getType(),
1399 lda->getType(), N->getType()};
1401 tys.insert(tys.begin(), layout->getType());
1402 auto ltuFT = FunctionType::get(B.getVoidTy(), tys,
false);
1404 auto F0 = M.getOrInsertFunction(fnc_name, ltuFT);
1406 SmallVector<Value *, 1> args = {islower, A, lda, N};
1408 args.insert(args.begin(), layout);
1409 auto C = B.CreateCall(F0, args);
1417 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1418#if LLVM_VERSION_MAJOR >= 16
1419 F->setOnlyAccessesArgMemory();
1421 F->addFnAttr(Attribute::ArgMemOnly);
1423 F->addFnAttr(Attribute::NoUnwind);
1424 F->addFnAttr(Attribute::AlwaysInline);
1425 if (A->getType()->isPointerTy())
1428 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1429 BasicBlock *loop = BasicBlock::Create(M.getContext(),
"loop", F);
1430 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
1432 auto arg = F->arg_begin();
1433 Argument *layoutarg =
nullptr;
1436 layoutarg->setName(
"layout");
1439 auto islowerarg = arg;
1440 islowerarg->setName(
"islower");
1446 ldaarg->setName(
"lda");
1451 IRBuilder<> EB(entry);
1453 auto one = ConstantInt::get(intType, 1);
1454 auto zero = ConstantInt::get(intType, 0);
1456 Value *N_minus_1 = EB.CreateSub(Narg, one);
1458 IRBuilder<> LB(loop);
1460 auto i = LB.CreatePHI(intType, 2);
1461 i->addIncoming(zero, entry);
1462 auto i_plus_one = LB.CreateAdd(i, one,
"",
true,
true);
1463 i->addIncoming(i_plus_one, loop);
1469 cublas,
nullptr, EB);
1471 Value *aCol =
CreateSelect(LB, islowerarg, i, i_plus_one);
1472 Value *aRow =
CreateSelect(LB, islowerarg, i_plus_one, i);
1476 Value *incACol =
CreateSelect(LB, islowerarg, zero, one);
1477 Value *incARow =
CreateSelect(LB, islowerarg, one, zero);
1481 ldaarg, incARow, incACol),
1482 byRef, cublas,
nullptr, EB);
1484 Value *bCol =
CreateSelect(LB, islowerarg, i_plus_one, i);
1485 Value *bRow =
CreateSelect(LB, islowerarg, i, i_plus_one);
1489 Value *incBCol =
CreateSelect(LB, islowerarg, one, zero);
1490 Value *incBRow =
CreateSelect(LB, islowerarg, zero, one);
1494 ldaarg, incBRow, incBCol),
1495 byRef, cublas,
nullptr, EB);
1497 Value *copyArgs[] = {countArg, aArg, incAArg, bArg, incBArg};
1499 Type *copyTys[] = {copyArgs[0]->getType(), copyArgs[1]->getType(),
1500 copyArgs[2]->getType(), copyArgs[3]->getType(),
1501 copyArgs[4]->getType()};
1503 FunctionType *FT = FunctionType::get(B.getVoidTy(), copyTys,
false);
1506 (cublasv2 ?
"" : blas.
suffix);
1508 auto copyfn = M.getOrInsertFunction(copy_name, FT);
1509 LB.CreateCall(copyfn, copyArgs);
1512 LB.CreateCondBr(LB.CreateICmpEQ(i_plus_one, N_minus_1), end, loop);
1514 EB.CreateCondBr(EB.CreateICmpSLE(N_minus_1, zero), end, loop);
1520 if (llvm::verifyFunction(*F, &llvm::errs())) {
1521 llvm::errs() << *F <<
"\n";
1522 report_fatal_error(
"helper function failed verification");
1527 llvm::ArrayRef<llvm::Value *> args,
1528 llvm::Type *copy_retty,
1529 llvm::ArrayRef<llvm::OperandBundleDef> bundles) {
1530 const bool cublasv2 =
1531 blas.
prefix ==
"cublas" && StringRef(blas.
suffix).contains(
"v2");
1533 (cublasv2 ?
"" : blas.
suffix);
1535 SmallVector<Type *, 1> tys;
1536 for (
auto arg : args)
1537 tys.push_back(arg->getType());
1539 FunctionType *FT = FunctionType::get(copy_retty, tys,
false);
1540 auto fn = M.getOrInsertFunction(copy_name, FT);
1541 B.CreateCall(fn, args, bundles);
1547 BlasInfo blas, llvm::ArrayRef<llvm::Value *> args,
1548 llvm::ArrayRef<llvm::OperandBundleDef> bundles) {
1552 SmallVector<Type *, 1> tys;
1553 for (
auto arg : args)
1554 tys.push_back(arg->getType());
1556 auto FT = FunctionType::get(Type::getVoidTy(M.getContext()), tys,
false);
1557 auto fn = M.getOrInsertFunction(copy_name, FT);
1558 B.CreateCall(fn, args, bundles);
1565 IntegerType *IT, Type *BlasCT, Type *BlasFPT,
1566 Type *BlasPT, Type *BlasIT, Type *fpTy,
1567 ArrayRef<Value *> args,
1568 ArrayRef<OperandBundleDef> bundles,
bool byRef,
1574 auto FDiagUpdateT = FunctionType::get(
1576 {BlasCT, BlasIT, BlasFPT, BlasPT, BlasIT, BlasPT, BlasIT, BlasPT},
false);
1578 cast<Function>(M.getOrInsertFunction(fnc_name, FDiagUpdateT).getCallee());
1581 B.CreateCall(F, args, bundles);
1586 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1587#if LLVM_VERSION_MAJOR >= 16
1588 F->setOnlyAccessesArgMemory();
1590 F->addFnAttr(Attribute::ArgMemOnly);
1592 F->addFnAttr(Attribute::NoUnwind);
1593 F->addFnAttr(Attribute::AlwaysInline);
1598 F->addParamAttr(3, Attribute::NoAlias);
1599 F->addParamAttr(5, Attribute::NoAlias);
1600 F->addParamAttr(7, Attribute::NoAlias);
1601 F->addParamAttr(3, Attribute::ReadOnly);
1602 F->addParamAttr(5, Attribute::ReadOnly);
1605 F->addParamAttr(2, Attribute::NoAlias);
1606 F->addParamAttr(2, Attribute::ReadOnly);
1610 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1611 BasicBlock *init = BasicBlock::Create(M.getContext(),
"init", F);
1612 BasicBlock *uper_code = BasicBlock::Create(M.getContext(),
"uper", F);
1613 BasicBlock *lower_code = BasicBlock::Create(M.getContext(),
"lower", F);
1614 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
1617 auto blasuplo = F->arg_begin();
1618 blasuplo->setName(
"blasuplo");
1619 auto blasn = blasuplo + 1;
1620 blasn->setName(
"blasn");
1621 auto blasalpha = blasn + 1;
1622 blasalpha->setName(
"blasalpha");
1623 auto blasx = blasalpha + 1;
1624 blasx->setName(
"blasx");
1625 auto blasincx = blasx + 1;
1626 blasincx->setName(
"blasincx");
1627 auto blasdy = blasx + 1;
1628 blasdy->setName(
"blasdy");
1629 auto blasincy = blasdy + 1;
1630 blasincy->setName(
"blasincy");
1631 auto blasdAP = blasincy + 1;
1632 blasdAP->setName(
"blasdAP");
1652 IRBuilder<> B1(entry);
1654 Value *incx =
load_if_ref(B1, IT, blasincx, byRef);
1655 Value *incy =
load_if_ref(B1, IT, blasincy, byRef);
1656 Value *alpha = blasalpha;
1658 auto VP = B1.CreatePointerCast(
1662 cast<PointerType>(blasalpha->getType())->getAddressSpace()));
1663 alpha = B1.CreateLoad(fpTy, VP);
1665 Value *is_l =
is_lower(B1, blasuplo, byRef,
false);
1666 B1.CreateCondBr(B1.CreateICmpEQ(n, ConstantInt::get(IT, 0)), end, init);
1668 IRBuilder<> B2(init);
1669 Value *xfloat = B2.CreatePointerCast(
1672 cast<PointerType>(blasx->getType())->getAddressSpace()));
1673 Value *dyfloat = B2.CreatePointerCast(
1676 fpTy, cast<PointerType>(blasdy->getType())->getAddressSpace()));
1677 Value *dAPfloat = B2.CreatePointerCast(
1680 fpTy, cast<PointerType>(blasdAP->getType())->getAddressSpace()));
1681 B2.CreateCondBr(is_l, lower_code, uper_code);
1683 IRBuilder<> B3(uper_code);
1684 B3.setFastMathFlags(
getFast());
1686 PHINode *iter = B3.CreatePHI(IT, 2,
"iteration");
1687 PHINode *kval = B3.CreatePHI(IT, 2,
"k");
1688 iter->addIncoming(ConstantInt::get(IT, 0), init);
1689 kval->addIncoming(ConstantInt::get(IT, 0), init);
1691 B3.CreateAdd(iter, ConstantInt::get(IT, 1),
"iter.next");
1693 Value *kvalnext = B3.CreateAdd(kval, iternext,
"k.next");
1694 iter->addIncoming(iternext, uper_code);
1695 kval->addIncoming(kvalnext, uper_code);
1697 Value *xidx = B3.CreateNUWMul(iter, incx,
"x.idx");
1698 Value *yidx = B3.CreateNUWMul(iter, incy,
"y.idx");
1699 Value *x = B3.CreateInBoundsGEP(fpTy, xfloat, xidx,
"x.ptr");
1700 Value *y = B3.CreateInBoundsGEP(fpTy, dyfloat, yidx,
"y.ptr");
1701 Value *xval = B3.CreateLoad(fpTy, x,
"x.val");
1702 Value *yval = B3.CreateLoad(fpTy, y,
"y.val");
1703 Value *xy = B3.CreateFMul(xval, yval,
"xy");
1704 Value *xyalpha = B3.CreateFMul(xy, alpha,
"xy.alpha");
1705 Value *kptr = B3.CreateInBoundsGEP(fpTy, dAPfloat, kval,
"k.ptr");
1706 Value *kvalloaded = B3.CreateLoad(fpTy, kptr,
"k.val");
1707 Value *kvalnew = B3.CreateFSub(kvalloaded, xyalpha,
"k.val.new");
1708 B3.CreateStore(kvalnew, kptr);
1710 B3.CreateCondBr(B3.CreateICmpEQ(iternext, n), end, uper_code);
1713 IRBuilder<> B4(lower_code);
1714 B4.setFastMathFlags(
getFast());
1716 PHINode *iter = B4.CreatePHI(IT, 2,
"iteration");
1717 PHINode *kval = B4.CreatePHI(IT, 2,
"k");
1718 iter->addIncoming(ConstantInt::get(IT, 0), init);
1719 kval->addIncoming(ConstantInt::get(IT, 0), init);
1721 B4.CreateAdd(iter, ConstantInt::get(IT, 1),
"iter.next");
1722 Value *ktmp = B4.CreateAdd(n, ConstantInt::get(IT, 1),
"tmp.val");
1723 Value *ktmp2 = B4.CreateSub(ktmp, iternext,
"tmp.val.other");
1724 Value *kvalnext = B4.CreateAdd(kval, ktmp2,
"k.next");
1725 iter->addIncoming(iternext, lower_code);
1726 kval->addIncoming(kvalnext, lower_code);
1728 Value *xidx = B4.CreateNUWMul(iter, incx,
"x.idx");
1729 Value *yidx = B4.CreateNUWMul(iter, incy,
"y.idx");
1730 Value *x = B4.CreateInBoundsGEP(fpTy, xfloat, xidx,
"x.ptr");
1731 Value *y = B4.CreateInBoundsGEP(fpTy, dyfloat, yidx,
"y.ptr");
1732 Value *xval = B4.CreateLoad(fpTy, x,
"x.val");
1733 Value *yval = B4.CreateLoad(fpTy, y,
"y.val");
1734 Value *xy = B4.CreateFMul(xval, yval,
"xy");
1735 Value *xyalpha = B4.CreateFMul(xy, alpha,
"xy.alpha");
1736 Value *kptr = B4.CreateInBoundsGEP(fpTy, dAPfloat, kval,
"k.ptr");
1737 Value *kvalloaded = B4.CreateLoad(fpTy, kptr,
"k.val");
1738 Value *kvalnew = B4.CreateFSub(kvalloaded, xyalpha,
"k.val.new");
1739 B4.CreateStore(kvalnew, kptr);
1741 B4.CreateCondBr(B4.CreateICmpEQ(iternext, n), end, lower_code);
1744 IRBuilder<> B5(end);
1747 B.CreateCall(F, args, bundles);
1753 IntegerType *IT, Type *BlasPT, Type *BlasIT, Type *fpTy,
1754 llvm::ArrayRef<llvm::Value *> args,
1755 const llvm::ArrayRef<llvm::OperandBundleDef> bundles,
1756 bool byRef,
bool cublas,
bool julia_decl) {
1757 assert(fpTy->isFloatingPointTy());
1760 std::string prod_name =
"__enzyme_inner_prod" + blas.
floatType + blas.
suffix;
1762 FunctionType::get(fpTy, {BlasIT, BlasIT, BlasPT, BlasIT, BlasPT},
false);
1764 cast<Function>(M.getOrInsertFunction(prod_name, FInnerProdT).getCallee());
1767 return B.CreateCall(F, args, bundles);
1772 FunctionType::get(fpTy, {BlasIT, BlasPT, BlasIT, BlasPT, BlasIT},
false);
1773 auto FDot = M.getOrInsertFunction(dot_name, FDotT);
1776 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1777#if LLVM_VERSION_MAJOR >= 16
1778 F->setOnlyAccessesArgMemory();
1779 F->setOnlyReadsMemory();
1781 F->addFnAttr(Attribute::ArgMemOnly);
1782 F->addFnAttr(Attribute::ReadOnly);
1784 F->addFnAttr(Attribute::NoUnwind);
1785 F->addFnAttr(Attribute::AlwaysInline);
1789 F->addParamAttr(2, Attribute::NoAlias);
1790 F->addParamAttr(4, Attribute::NoAlias);
1791 F->addParamAttr(2, Attribute::ReadOnly);
1792 F->addParamAttr(4, Attribute::ReadOnly);
1795 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1796 BasicBlock *init = BasicBlock::Create(M.getContext(),
"init.idx", F);
1797 BasicBlock *fastPath = BasicBlock::Create(M.getContext(),
"fast.path", F);
1798 BasicBlock *body = BasicBlock::Create(M.getContext(),
"for.body", F);
1799 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
1806 auto blasm = F->arg_begin();
1807 blasm->setName(
"blasm");
1808 auto blasn = blasm + 1;
1809 blasn->setName(
"blasn");
1810 auto matA = blasn + 1;
1812 auto blaslda = matA + 1;
1813 blaslda->setName(
"lda");
1814 auto matB = blaslda + 1;
1818 IRBuilder<> B1(entry);
1820 cublas,
nullptr, B1,
"constant.one");
1822 if (blasOne->getType() != BlasIT)
1823 blasOne = B1.CreatePointerCast(blasOne, BlasIT,
"intcast.constant.one");
1827 Value *size = B1.CreateNUWMul(m, n,
"mat.size");
1829 B1, size, byRef, cublas, julia_decl ? IT :
nullptr, B1,
"mat.size");
1831 if (blasSize->getType() != BlasIT)
1832 blasSize = B1.CreatePointerCast(blasSize, BlasIT,
"intcast.mat.size");
1833 B1.CreateCondBr(B1.CreateICmpEQ(size, ConstantInt::get(IT, 0)), end, init);
1835 IRBuilder<> B2(init);
1836 B2.setFastMathFlags(
getFast());
1838 Value *Afloat = B2.CreatePointerCast(
1840 fpTy, cast<PointerType>(matA->getType())->getAddressSpace()));
1841 Value *Bfloat = B2.CreatePointerCast(
1843 fpTy, cast<PointerType>(matB->getType())->getAddressSpace()));
1844 B2.CreateCondBr(B2.CreateICmpEQ(m, lda), fastPath, body);
1849 IRBuilder<> B3(fastPath);
1850 B3.setFastMathFlags(
getFast());
1851 Value *blasA = B3.CreatePointerCast(matA, BlasPT);
1852 Value *blasB = B3.CreatePointerCast(matB, BlasPT);
1854 B3.CreateCall(FDot, {blasSize, blasA, blasOne, blasB, blasOne});
1857 IRBuilder<> B4(body);
1858 B4.setFastMathFlags(
getFast());
1859 PHINode *Aidx = B4.CreatePHI(IT, 2,
"Aidx");
1860 PHINode *Bidx = B4.CreatePHI(IT, 2,
"Bidx");
1861 PHINode *iter = B4.CreatePHI(IT, 2,
"iteration");
1862 PHINode *sum = B4.CreatePHI(fpTy, 2,
"sum");
1863 Aidx->addIncoming(ConstantInt::get(IT, 0), init);
1864 Bidx->addIncoming(ConstantInt::get(IT, 0), init);
1865 iter->addIncoming(ConstantInt::get(IT, 0), init);
1866 sum->addIncoming(ConstantFP::get(fpTy, 0.0), init);
1868 Value *Ai = B4.CreateInBoundsGEP(fpTy, Afloat, Aidx,
"A.i");
1869 Value *Bi = B4.CreateInBoundsGEP(fpTy, Bfloat, Bidx,
"B.i");
1870 Value *AiDot = B4.CreatePointerCast(Ai, BlasPT);
1871 Value *BiDot = B4.CreatePointerCast(Bi, BlasPT);
1873 B4.CreateCall(FDot, {blasm, AiDot, blasOne, BiDot, blasOne});
1875 Value *Anext = B4.CreateNUWAdd(Aidx, lda,
"Aidx.next");
1876 Value *Bnext = B4.CreateNUWAdd(Aidx, m,
"Bidx.next");
1877 Value *iternext = B4.CreateAdd(iter, ConstantInt::get(IT, 1),
"iter.next");
1878 Value *sumnext = B4.CreateFAdd(sum, newDot);
1880 iter->addIncoming(iternext, body);
1881 Aidx->addIncoming(Anext, body);
1882 Bidx->addIncoming(Bnext, body);
1883 sum->addIncoming(sumnext, body);
1885 B4.CreateCondBr(B4.CreateICmpEQ(iter, n), end, body);
1887 IRBuilder<> B5(end);
1888 PHINode *res = B5.CreatePHI(fpTy, 3,
"res");
1889 res->addIncoming(ConstantFP::get(fpTy, 0.0), entry);
1890 res->addIncoming(sum, body);
1891 res->addIncoming(fastSum, fastPath);
1895 auto res = B.CreateCall(F, args, bundles);
1902 Type *IT,
unsigned dstalign,
1903 unsigned srcalign) {
1904 assert(elementType->isFloatingPointTy());
1905 std::string name =
"__enzyme_memcpy_" +
tofltstr(elementType) +
"_" +
1906 std::to_string(cast<IntegerType>(IT)->getBitWidth()) +
1907 "_da" + std::to_string(dstalign) +
"sa" +
1908 std::to_string(srcalign) +
"stride";
1910 FunctionType::get(Type::getVoidTy(M.getContext()), {T, T, IT, IT},
false);
1912 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1917 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1918#if LLVM_VERSION_MAJOR >= 16
1919 F->setOnlyAccessesArgMemory();
1921 F->addFnAttr(Attribute::ArgMemOnly);
1923 F->addFnAttr(Attribute::NoUnwind);
1924 F->addFnAttr(Attribute::AlwaysInline);
1926 F->addParamAttr(0, Attribute::NoAlias);
1928 F->addParamAttr(1, Attribute::NoAlias);
1929 F->addParamAttr(0, Attribute::WriteOnly);
1930 F->addParamAttr(1, Attribute::ReadOnly);
1932 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
1933 BasicBlock *init = BasicBlock::Create(M.getContext(),
"init.idx", F);
1934 BasicBlock *body = BasicBlock::Create(M.getContext(),
"for.body", F);
1935 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
1937 auto dst = F->arg_begin();
1938 dst->setName(
"dst");
1940 src->setName(
"src");
1942 num->setName(
"num");
1943 auto stride = num + 1;
1944 stride->setName(
"stride");
1947 IRBuilder<> B(entry);
1948 B.CreateCondBr(B.CreateICmpEQ(num, ConstantInt::get(num->getType(), 0)),
1953 IRBuilder<> B2(init);
1954 B2.setFastMathFlags(
getFast());
1955 Value *a = B2.CreateNSWSub(ConstantInt::get(num->getType(), 1), num,
"a");
1956 Value *negidx = B2.CreateNSWMul(a, stride,
"negidx");
1961 B2.CreateICmpSLT(stride, ConstantInt::get(num->getType(), 0),
"is.neg");
1962 Value *startidx = B2.CreateSelect(
1963 isneg, negidx, ConstantInt::get(num->getType(), 0),
"startidx");
1968 IRBuilder<> B(body);
1969 B.setFastMathFlags(
getFast());
1970 PHINode *idx = B.CreatePHI(num->getType(), 2,
"idx");
1971 PHINode *sidx = B.CreatePHI(num->getType(), 2,
"sidx");
1972 idx->addIncoming(ConstantInt::get(num->getType(), 0), init);
1973 sidx->addIncoming(startidx, init);
1975 Value *dsti = B.CreateInBoundsGEP(elementType, dst, idx,
"dst.i");
1976 Value *srci = B.CreateInBoundsGEP(elementType, src, sidx,
"src.i");
1977 LoadInst *srcl = B.CreateLoad(elementType, srci,
"src.i.l");
1978 StoreInst *dsts = B.CreateStore(srcl, dsti);
1981 dsts->setAlignment(Align(dstalign));
1984 srcl->setAlignment(Align(srcalign));
1988 B.CreateNSWAdd(idx, ConstantInt::get(num->getType(), 1),
"idx.next");
1989 Value *snext = B.CreateNSWAdd(sidx, stride,
"sidx.next");
1990 idx->addIncoming(next, body);
1991 sidx->addIncoming(snext, body);
1992 B.CreateCondBr(B.CreateICmpEQ(num, next), end, body);
2004 IntegerType *IT,
unsigned dstalign,
2005 unsigned srcalign) {
2006 assert(elementType->isFPOrFPVectorTy());
2007#if LLVM_VERSION_MAJOR < 17
2008#if LLVM_VERSION_MAJOR >= 15
2009 if (Mod.getContext().supportsTypedPointers()) {
2011#if LLVM_VERSION_MAJOR >= 13
2012 if (!PT->isOpaquePointerTy())
2014 assert(PT->getPointerElementType() == elementType);
2015#if LLVM_VERSION_MAJOR >= 15
2019 std::string name =
"__enzyme_memcpy_" +
tofltstr(elementType) +
"_mat_" +
2020 std::to_string(cast<IntegerType>(IT)->getBitWidth());
2021 FunctionType *FT = FunctionType::get(Type::getVoidTy(Mod.getContext()),
2022 {PT, PT, IT, IT, IT},
false);
2024 Function *F = cast<Function>(Mod.getOrInsertFunction(name, FT).getCallee());
2029 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2030#if LLVM_VERSION_MAJOR >= 16
2031 F->setOnlyAccessesArgMemory();
2033 F->addFnAttr(Attribute::ArgMemOnly);
2035 F->addFnAttr(Attribute::NoUnwind);
2036 F->addFnAttr(Attribute::AlwaysInline);
2038 F->addParamAttr(0, Attribute::NoAlias);
2040 F->addParamAttr(1, Attribute::NoAlias);
2041 F->addParamAttr(0, Attribute::WriteOnly);
2042 F->addParamAttr(1, Attribute::ReadOnly);
2044 BasicBlock *entry = BasicBlock::Create(F->getContext(),
"entry", F);
2045 BasicBlock *init = BasicBlock::Create(F->getContext(),
"init.idx", F);
2046 BasicBlock *body = BasicBlock::Create(F->getContext(),
"for.body", F);
2047 BasicBlock *initend = BasicBlock::Create(F->getContext(),
"init.end", F);
2048 BasicBlock *end = BasicBlock::Create(F->getContext(),
"for.end", F);
2050 auto dst = F->arg_begin();
2051 dst->setName(
"dst");
2053 src->setName(
"src");
2059 LDA->setName(
"LDA");
2062 IRBuilder<> B(entry);
2063 Value *l0 = B.CreateICmpEQ(M, ConstantInt::get(IT, 0));
2064 Value *l1 = B.CreateICmpEQ(N, ConstantInt::get(IT, 0));
2066 B.CreateCondBr(B.CreateOr(l0, l1), end, init);
2071 IRBuilder<> B(init);
2072 j = B.CreatePHI(IT, 2,
"j");
2073 j->addIncoming(ConstantInt::get(IT, 0), entry);
2078 IRBuilder<> B(body);
2079 PHINode *i = B.CreatePHI(IT, 2,
"i");
2080 i->addIncoming(ConstantInt::get(IT, 0), init);
2082 Value *dsti = B.CreateInBoundsGEP(
2084 B.CreateAdd(i, B.CreateMul(j, M,
"",
true,
true),
"",
true,
true),
2086 Value *srci = B.CreateInBoundsGEP(
2088 B.CreateAdd(i, B.CreateMul(j, LDA,
"",
true,
true),
"",
true,
true),
2090 LoadInst *srcl = B.CreateLoad(elementType, srci,
"src.i.l");
2092 StoreInst *dsts = B.CreateStore(srcl, dsti);
2095 dsts->setAlignment(Align(dstalign));
2098 srcl->setAlignment(Align(srcalign));
2102 B.CreateAdd(i, ConstantInt::get(IT, 1),
"i.next",
true,
true);
2103 i->addIncoming(nexti, body);
2104 B.CreateCondBr(B.CreateICmpEQ(nexti, M), initend, body);
2108 IRBuilder<> B(initend);
2110 B.CreateAdd(j, ConstantInt::get(IT, 1),
"j.next",
true,
true);
2111 j->addIncoming(nextj, initend);
2112 B.CreateCondBr(B.CreateICmpEQ(nextj, N), end, init);
2124 Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT,
2125 IntegerType *CT,
unsigned dstalign,
unsigned srcalign,
bool zeroSrc) {
2126 assert(elementType->isFPOrFPVectorTy());
2127#if LLVM_VERSION_MAJOR < 17
2128#if LLVM_VERSION_MAJOR >= 15
2129 if (Mod.getContext().supportsTypedPointers()) {
2131#if LLVM_VERSION_MAJOR >= 13
2132 if (!PT->isOpaquePointerTy())
2134 assert(PT->getPointerElementType() == elementType);
2135#if LLVM_VERSION_MAJOR >= 15
2139 std::string name =
"__enzyme_dmemcpy_" +
tofltstr(elementType) +
"_mat_" +
2140 std::to_string(cast<IntegerType>(IT)->getBitWidth()) +
2141 (zeroSrc ?
"_zero" :
"");
2142 FunctionType *FT = FunctionType::get(Type::getVoidTy(Mod.getContext()),
2143 {CT, IT, IT, PT, IT, PT, IT},
false);
2145 Function *F = cast<Function>(Mod.getOrInsertFunction(name, FT).getCallee());
2150 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2151#if LLVM_VERSION_MAJOR >= 16
2152 F->setOnlyAccessesArgMemory();
2154 F->addFnAttr(Attribute::ArgMemOnly);
2156 F->addFnAttr(Attribute::NoUnwind);
2157 F->addFnAttr(Attribute::AlwaysInline);
2158 F->addParamAttr(3, Attribute::NoAlias);
2159 F->addParamAttr(5, Attribute::NoAlias);
2161 BasicBlock *entry = BasicBlock::Create(F->getContext(),
"entry", F);
2162 BasicBlock *swtch = BasicBlock::Create(F->getContext(),
"swtch", F);
2163 BasicBlock *Ginit = BasicBlock::Create(F->getContext(),
"Ginit.idx", F);
2164 BasicBlock *Uinit = BasicBlock::Create(F->getContext(),
"Uinit.idx", F);
2165 BasicBlock *Linit = BasicBlock::Create(F->getContext(),
"Linit.idx", F);
2166 BasicBlock *end = BasicBlock::Create(F->getContext(),
"for.end", F);
2168 auto uplo = F->arg_begin();
2169 uplo->setName(
"uplo");
2176 dst->setName(
"dst");
2177 auto ldst = dst + 1;
2178 ldst->setName(
"ldst");
2179 auto src = ldst + 1;
2180 src->setName(
"src");
2181 auto lsrc = src + 1;
2182 lsrc->setName(
"lsrc");
2185 IRBuilder<> B(entry);
2186 Value *l0 = B.CreateICmpEQ(M, ConstantInt::get(IT, 0));
2187 Value *l1 = B.CreateICmpEQ(N, ConstantInt::get(IT, 0));
2189 B.CreateCondBr(B.CreateOr(l0, l1), end, swtch);
2193 IRBuilder<> B(swtch);
2194 auto swtchT = B.CreateSwitch(uplo, Ginit);
2195 swtchT->addCase(ConstantInt::get(CT,
'U'), Uinit);
2196 swtchT->addCase(ConstantInt::get(CT,
'L'), Linit);
2199 std::pair<char, BasicBlock *> todo[] = {
2200 {
'G', Ginit}, {
'U', Uinit}, {
'L', Linit}};
2201 for (
auto &&[direction, init] : todo) {
2203 std::string dir(1, direction);
2204 BasicBlock *body = BasicBlock::Create(F->getContext(), dir +
"for.body", F);
2205 BasicBlock *initend =
2206 BasicBlock::Create(F->getContext(), dir +
"init.end", F);
2208 Value *istart = ConstantInt::get(IT, 0);
2213 IRBuilder<> B(init);
2214 j = B.CreatePHI(IT, 2, dir +
"j");
2215 j->addIncoming(ConstantInt::get(IT, 0), swtch);
2217 if (direction ==
'L') {
2219 }
else if (direction ==
'U') {
2220 auto jp1 = B.CreateAdd(j, ConstantInt::get(IT, 1),
"",
true,
true);
2221 iend = B.CreateSelect(B.CreateICmpULT(jp1, M), jp1, M);
2228 IRBuilder<> B(body);
2229 PHINode *i = B.CreatePHI(IT, 2, dir +
"i");
2230 i->addIncoming(istart, init);
2232 Value *srci = B.CreateInBoundsGEP(
2234 B.CreateAdd(i, B.CreateMul(j, lsrc,
"",
true,
true),
"",
true,
true),
2237 Value *dsti = B.CreateInBoundsGEP(
2239 B.CreateAdd(i, B.CreateMul(j, ldst,
"",
true,
true),
"",
true,
true),
2241 LoadInst *srcl = B.CreateLoad(elementType, srci, dir +
"src.i.l");
2242 LoadInst *dstl = B.CreateLoad(elementType, dsti, dir +
"dst.i.l");
2243 auto res = B.CreateFAdd(srcl, dstl);
2244 StoreInst *dsts = B.CreateStore(res, dsti);
2245 StoreInst *srcs =
nullptr;
2247 srcs = B.CreateStore(Constant::getNullValue(res->getType()), srci);
2249 dsts->setAlignment(Align(dstalign));
2250 dstl->setAlignment(Align(dstalign));
2254 srcs->setAlignment(Align(srcalign));
2255 srcl->setAlignment(Align(srcalign));
2259 B.CreateAdd(i, ConstantInt::get(IT, 1), dir +
"i.next",
true,
true);
2260 i->addIncoming(nexti, body);
2261 B.CreateCondBr(B.CreateICmpEQ(nexti, iend), initend, body);
2265 IRBuilder<> B(initend);
2267 B.CreateAdd(j, ConstantInt::get(IT, 1), dir +
"j.next",
true,
true);
2268 j->addIncoming(nextj, initend);
2269 B.CreateCondBr(B.CreateICmpEQ(nextj, N), end, init);
2283 Module &M, Type *T,
unsigned dstalign,
unsigned srcalign,
unsigned dstaddr,
2284 unsigned srcaddr,
unsigned bitwidth,
bool runtimeActivity,
bool atomic) {
2287 <<
"warning: didn't implement memmove, using memcpy as fallback "
2288 "which can result in errors\n";
2290 srcaddr, bitwidth, runtimeActivity,
2295 StringRef callee, FunctionType *FT) {
2296 auto res = M.getOrInsertFunction(
2298 if (
auto F = dyn_cast<Function>(res.getCallee()))
2299 if (!F->hasFnAttribute(
"enzyme_math"))
2300 F->addFnAttr(
"enzyme_math", callee);
2306 FunctionType *FreeTy = call->getFunctionType();
2307 Value *Free = call->getCalledOperand();
2308 AttributeList FreeAttributes = call->getAttributes();
2309 CallingConv::ID CallingConvention = call->getCallingConv();
2311 std::string name =
"__enzyme_checked_free_" + std::to_string(width);
2314 if (callname !=
"free")
2315 name +=
"_" + callname.str();
2317 SmallVector<Type *, 3> types;
2318 types.push_back(Ty);
2319 for (
unsigned i = 0; i < width; i++) {
2320 types.push_back(Ty);
2322#if LLVM_VERSION_MAJOR >= 14
2323 for (
size_t i = 1; i < call->arg_size(); i++)
2325 for (
size_t i = 1; i < call->getNumArgOperands(); i++)
2328 types.push_back(call->getArgOperand(i)->getType());
2332 FunctionType::get(Type::getVoidTy(M.getContext()), types,
false);
2334 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2339 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2340#if LLVM_VERSION_MAJOR >= 16
2341 F->setOnlyAccessesArgMemory();
2343 F->addFnAttr(Attribute::ArgMemOnly);
2345 F->addFnAttr(Attribute::NoUnwind);
2346 F->addFnAttr(Attribute::AlwaysInline);
2348 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
2349 BasicBlock *free0 = BasicBlock::Create(M.getContext(),
"free0", F);
2350 BasicBlock *end = BasicBlock::Create(M.getContext(),
"end", F);
2352 IRBuilder<> EntryBuilder(entry);
2353 IRBuilder<> Free0Builder(free0);
2354 IRBuilder<> EndBuilder(end);
2356 auto primal = F->arg_begin();
2357 Argument *first_shadow = F->arg_begin() + 1;
2359 if (Ty->isPointerTy()) {
2364 Value *isNotEqual = EntryBuilder.CreateICmpNE(primal, first_shadow);
2365 EntryBuilder.CreateCondBr(isNotEqual, free0, end);
2367 SmallVector<Value *, 1> args = {first_shadow};
2368#if LLVM_VERSION_MAJOR >= 14
2369 for (
size_t i = 1; i < call->arg_size(); i++)
2371 for (
size_t i = 1; i < call->getNumArgOperands(); i++)
2374 args.push_back(F->arg_begin() + width + i);
2377 CallInst *CI = Free0Builder.CreateCall(FreeTy, Free, args);
2378 CI->setAttributes(FreeAttributes);
2379 CI->setCallingConv(CallingConvention);
2382 Value *checkResult =
nullptr;
2383 BasicBlock *free1 = BasicBlock::Create(M.getContext(),
"free1", F);
2384 IRBuilder<> Free1Builder(free1);
2386 for (
unsigned i = 0; i < width; i++) {
2388 Argument *shadow = F->arg_begin() + i + 1;
2390 if (i < width - 1) {
2391 Argument *nextShadow = F->arg_begin() + i + 2;
2392 Value *isNotEqual = Free0Builder.CreateICmpNE(shadow, nextShadow);
2393 checkResult = checkResult
2394 ? Free0Builder.CreateAnd(isNotEqual, checkResult)
2397 args[0] = nextShadow;
2398 CallInst *CI = Free1Builder.CreateCall(FreeTy, Free, args);
2399 CI->setAttributes(FreeAttributes);
2400 CI->setCallingConv(CallingConvention);
2403 Free0Builder.CreateCondBr(checkResult, free1, end);
2404 Free1Builder.CreateBr(end);
2406 Free0Builder.CreateBr(end);
2409 EndBuilder.CreateRetVoid();
2416 assert(V->getType()->isIntegerTy());
2417 IntegerType *T = cast<IntegerType>(V->getType());
2418 V = B.CreateAdd(V, ConstantInt::get(T, -1,
true));
2419 for (
size_t i = 1; i < T->getBitWidth(); i *= 2) {
2420 V = B.CreateOr(V, B.CreateLShr(V, ConstantInt::get(T, i)));
2422 V = B.CreateAdd(V, ConstantInt::get(T, 1));
2427 ArrayRef<llvm::Type *> T,
2428 PointerType *reqType) {
2429 std::string name =
"__enzyme_differential_waitall_save";
2430 FunctionType *FT = FunctionType::get(
getUnqual(reqType), T,
false);
2431 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2436 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2437 F->addFnAttr(Attribute::NoUnwind);
2438 F->addFnAttr(Attribute::AlwaysInline);
2440 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
2442 auto buff = F->arg_begin();
2443 buff->setName(
"count");
2444 Value *count = buff;
2445 Value *req = buff + 1;
2446 req->setName(
"req");
2447 Value *dreq = buff + 2;
2448 dreq->setName(
"dreq");
2450 IRBuilder<> B(entry);
2451 count = B.CreateZExtOrTrunc(count, Type::getInt64Ty(entry->getContext()));
2455 BasicBlock *loopBlock = BasicBlock::Create(M.getContext(),
"loop", F);
2456 BasicBlock *endBlock = BasicBlock::Create(M.getContext(),
"end", F);
2458 B.CreateCondBr(B.CreateICmpEQ(count, ConstantInt::get(count->getType(), 0)),
2459 endBlock, loopBlock);
2461 B.SetInsertPoint(loopBlock);
2462 auto idx = B.CreatePHI(count->getType(), 2);
2463 idx->addIncoming(ConstantInt::get(count->getType(), 0), entry);
2464 auto inc = B.CreateAdd(idx, ConstantInt::get(count->getType(), 1));
2465 idx->addIncoming(inc, loopBlock);
2467 Type *reqT = reqType;
2468 Value *idxs[] = {idx};
2469 Value *ireq = B.CreateInBoundsGEP(reqT, req, idxs);
2470 Value *idreq = B.CreateInBoundsGEP(reqT, dreq, idxs);
2471 Value *iout = B.CreateInBoundsGEP(reqType, ret, idxs);
2472 Value *isNull =
nullptr;
2473 if (
auto GV = M.getNamedValue(
"ompi_request_null")) {
2474 Value *reql = B.CreatePointerCast(ireq,
getUnqual(GV->getType()));
2475 reql = B.CreateLoad(GV->getType(), reql);
2476 isNull = B.CreateICmpEQ(reql, GV);
2479 idreq = B.CreatePointerCast(idreq,
getUnqual(reqType));
2480 Value *d_reqp = B.CreateLoad(reqType, idreq);
2482 d_reqp = B.CreateSelect(isNull, Constant::getNullValue(d_reqp->getType()),
2485 B.CreateStore(d_reqp, iout);
2487 B.CreateCondBr(B.CreateICmpEQ(inc, count), endBlock, loopBlock);
2489 B.SetInsertPoint(endBlock);
2495 ArrayRef<llvm::Type *> T,
2498 llvm::SmallVector<llvm::Type *, 4> types(T.begin(), T.end());
2499 types.push_back(reqType);
2503 std::string name =
"__enzyme_differential_mpi_wait";
2504 if (prefix.size() != 0 || postfix.size() != 0) {
2505 name = (Twine(name) +
"$" + prefix +
"$" + postfix).
str();
2508 FunctionType::get(Type::getVoidTy(M.getContext()), types,
false);
2509 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2514 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2515 F->addFnAttr(Attribute::NoUnwind);
2516 F->addFnAttr(Attribute::AlwaysInline);
2518 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
2519 BasicBlock *isend = BasicBlock::Create(M.getContext(),
"invertISend", F);
2520 BasicBlock *irecv = BasicBlock::Create(M.getContext(),
"invertIRecv", F);
2529 Type::getInt8Ty(call.getContext())
2532 auto buff = F->arg_begin();
2533 buff->setName(
"buf");
2535 Value *count = buff + 1;
2536 count->setName(
"count");
2537 Value *datatype = buff + 2;
2538 datatype->setName(
"datatype");
2539 Value *source = buff + 3;
2540 source->setName(
"source");
2541 Value *tag = buff + 4;
2542 tag->setName(
"tag");
2543 Value *comm = buff + 5;
2544 comm->setName(
"comm");
2545 Value *fn = buff + 6;
2547 Value *d_req = buff + 7;
2548 d_req->setName(
"d_req");
2553 FunctionType *FuT = isendfn->getFunctionType();
2555 auto irecvfn = cast<Function>(
2560 IRBuilder<> B(entry);
2561 auto arg = isendfn->arg_begin();
2562 if (arg->getType()->isIntegerTy())
2563 buf = B.CreatePtrToInt(buf, arg->getType());
2565 count = B.CreateZExtOrTrunc(count, arg->getType());
2567 datatype = B.CreatePointerCast(datatype, arg->getType());
2569 source = B.CreateZExtOrTrunc(source, arg->getType());
2571 tag = B.CreateZExtOrTrunc(tag, arg->getType());
2573 comm = B.CreatePointerCast(comm, arg->getType());
2575 if (arg->getType()->isIntegerTy())
2576 d_req = B.CreatePtrToInt(d_req, arg->getType());
2578 buf, count, datatype, source, tag, comm, d_req,
2581 B.CreateCondBr(B.CreateICmpEQ(fn, ConstantInt::get(fn->getType(),
2586 B.SetInsertPoint(isend);
2587 auto fcall = B.CreateCall(irecvfn, args);
2588 fcall->setCallingConv(isendfn->getCallingConv());
2593 B.SetInsertPoint(irecv);
2594 auto fcall = B.CreateCall(isendfn, args);
2595 fcall->setCallingConv(isendfn->getCallingConv());
2602 llvm::Type *OpPtr, llvm::Type *OpType,
2605 std::string name =
"__enzyme_mpi_sum" + CT.
str();
2609 if (
auto Glob = M.getGlobalVariable(name)) {
2610 return B2.CreateLoad(Glob->getValueType(), Glob);
2616 FunctionType::get(Type::getVoidTy(M.getContext()), types,
false);
2618 cast<Function>(M.getOrInsertFunction(name +
"_run", FuT).getCallee());
2620 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2621#if LLVM_VERSION_MAJOR >= 16
2622 F->setOnlyAccessesArgMemory();
2624 F->addFnAttr(Attribute::ArgMemOnly);
2626 F->addFnAttr(Attribute::NoUnwind);
2627 F->addFnAttr(Attribute::AlwaysInline);
2629 F->addParamAttr(0, Attribute::ReadOnly);
2632 F->addParamAttr(2, Attribute::ReadOnly);
2634 F->addParamAttr(3, Attribute::ReadNone);
2636 BasicBlock *entry = BasicBlock::Create(M.getContext(),
"entry", F);
2637 BasicBlock *body = BasicBlock::Create(M.getContext(),
"for.body", F);
2638 BasicBlock *end = BasicBlock::Create(M.getContext(),
"for.end", F);
2640 auto src = F->arg_begin();
2641 src->setName(
"src");
2643 dst->setName(
"dst");
2644 auto lenp = dst + 1;
2645 lenp->setName(
"lenp");
2651 IRBuilder<> B(entry);
2652 len = B.CreateLoad(intType, lenp);
2653 B.CreateCondBr(B.CreateICmpEQ(len, ConstantInt::get(len->getType(), 0)),
2658 IRBuilder<> B(body);
2659 B.setFastMathFlags(
getFast());
2660 PHINode *idx = B.CreatePHI(len->getType(), 2,
"idx");
2661 idx->addIncoming(ConstantInt::get(len->getType(), 0), entry);
2663 Value *dsti = B.CreateInBoundsGEP(FlT, dst, idx,
"dst.i");
2664 LoadInst *dstl = B.CreateLoad(FlT, dsti,
"dst.i.l");
2666 Value *srci = B.CreateInBoundsGEP(FlT, src, idx,
"src.i");
2667 LoadInst *srcl = B.CreateLoad(FlT, srci,
"src.i.l");
2668 B.CreateStore(B.CreateFAdd(srcl, dstl), dsti);
2671 B.CreateNUWAdd(idx, ConstantInt::get(len->getType(), 1),
"idx.next");
2672 idx->addIncoming(next, body);
2673 B.CreateCondBr(B.CreateICmpEQ(len, next), end, body);
2681 llvm::Type *rtypes[] = {
getInt8PtrTy(M.getContext()), intType, OpPtr};
2682 FunctionType *RFT = FunctionType::get(intType, rtypes,
false);
2684 std::string opCreate =
2686 Constant *RF = M.getNamedValue(opCreate);
2688 RF = cast<Function>(
2692 RF = ConstantExpr::getBitCast(RF,
getUnqual(RFT));
2695 GlobalVariable *GV =
2696 new GlobalVariable(M, OpType,
false, GlobalVariable::InternalLinkage,
2697 UndefValue::get(OpType), name);
2699 Type *i1Ty = Type::getInt1Ty(M.getContext());
2700 GlobalVariable *initD =
new GlobalVariable(
2701 M, i1Ty,
false, GlobalVariable::InternalLinkage,
2702 ConstantInt::getFalse(M.getContext()), name +
"_initd");
2706 FunctionType *IFT = FunctionType::get(Type::getVoidTy(M.getContext()),
2707 ArrayRef<Type *>(),
false);
2708 Function *initializerFunction = cast<Function>(
2709 M.getOrInsertFunction(name +
"initializer", IFT).getCallee());
2711 initializerFunction->setLinkage(Function::LinkageTypes::InternalLinkage);
2712 initializerFunction->addFnAttr(Attribute::NoUnwind);
2716 BasicBlock::Create(M.getContext(),
"entry", initializerFunction);
2718 BasicBlock::Create(M.getContext(),
"run", initializerFunction);
2720 BasicBlock::Create(M.getContext(),
"end", initializerFunction);
2721 IRBuilder<> B(entry);
2723 B.CreateCondBr(B.CreateLoad(initD->getValueType(), initD), end, run);
2725 B.SetInsertPoint(run);
2726 Value *args[] = {ConstantExpr::getPointerCast(F, rtypes[0]),
2727 ConstantInt::get(rtypes[1], 1,
false),
2728 ConstantExpr::getPointerCast(GV, rtypes[2])};
2729 B.CreateCall(RFT, RF, args);
2730 B.CreateStore(ConstantInt::getTrue(M.getContext()), initD);
2732 B.SetInsertPoint(end);
2736 B2.CreateCall(M.getFunction(name +
"initializer"));
2737 return B2.CreateLoad(GV->getValueType(), GV);
2741 llvm::Instruction *inst,
2742 const llvm::SetVector<Instruction *> &stores,
2743 const llvm::Loop *region) {
2744 using namespace llvm;
2745 std::map<BasicBlock *, SmallVector<Instruction *, 1>> maybeBlocks;
2746 BasicBlock *instBlk = inst->getParent();
2747 for (
auto store : stores) {
2748 BasicBlock *storeBlk = store->getParent();
2749 if (instBlk == storeBlk) {
2752 if (store != inst) {
2753 BasicBlock::const_iterator It = storeBlk->begin();
2754 for (; &*It != store && &*It != inst; ++It)
2759 results.push_back(store);
2762 maybeBlocks[storeBlk].push_back(store);
2764 maybeBlocks[storeBlk].push_back(store);
2768 if (maybeBlocks.size() == 0)
2771 llvm::SmallVector<BasicBlock *, 2> todo;
2772 for (
auto B : successors(instBlk)) {
2773 if (region && region->getHeader() == B) {
2779 SmallPtrSet<BasicBlock *, 2> seen;
2780 while (todo.size()) {
2781 auto cur = todo.back();
2783 if (seen.count(cur))
2786 auto found = maybeBlocks.find(cur);
2787 if (found != maybeBlocks.end()) {
2788 for (
auto store : found->second)
2789 results.push_back(store);
2790 maybeBlocks.erase(found);
2792 for (
auto B : successors(cur)) {
2793 if (region && region->getHeader() == B) {
2802 const SCEV *It, ScalarEvolution &SE) {
2803 Type *Ty = AR->getType();
2804 unsigned N = AR->getNumOperands();
2805 if (!Ty->isIntegerTy() || !It->getType()->isIntegerTy() || N > 4)
2806 return AR->evaluateAtIteration(It, SE);
2811 Type *CalcTy = SE.getTypeSizeInBits(It->getType()) > SE.getTypeSizeInBits(Ty)
2814 const SCEV *I = SE.getTruncateOrZeroExtend(It, CalcTy);
2815 const SCEV *Res = AR->getOperand(0);
2817 Res = SE.getAddExpr(
2818 Res, SE.getMulExpr(AR->getOperand(1), SE.getTruncateOrNoop(I, Ty)));
2822 const SCEV *One = SE.getOne(CalcTy);
2823 const SCEV *Two = SE.getConstant(CalcTy, 2);
2824 const SCEV *Half = SE.getUDivExpr(I, Two);
2825 const SCEV *IM1 = SE.getMinusSCEV(I, One);
2826 const SCEV *Parity = SE.getMinusSCEV(I, SE.getMulExpr(Two, Half));
2828 SE.getAddExpr(SE.getMulExpr(Half, IM1),
2829 SE.getMulExpr(Parity, SE.getUDivExpr(IM1, Two)));
2830 Res = SE.getAddExpr(
2831 Res, SE.getMulExpr(AR->getOperand(2), SE.getTruncateOrNoop(Tri, Ty)));
2836 unsigned Bits = SE.getTypeSizeInBits(CalcTy);
2837 APInt Three(Bits, 3), Inv(Bits, 3);
2838 for (
unsigned Correct = 3; Correct < Bits; Correct *= 2)
2839 Inv *= APInt(Bits, 2) - Three * Inv;
2840 const SCEV *Tet = SE.getMulExpr(
2841 SE.getMulExpr(Tri, SE.getMinusSCEV(I, Two)), SE.getConstant(Inv));
2842 Res = SE.getAddExpr(
2843 Res, SE.getMulExpr(AR->getOperand(3), SE.getTruncateOrNoop(Tet, Ty)));
2850 llvm::ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT,
2851 llvm::Instruction *maybeReader,
const llvm::SCEV *LoadStart,
2852 const llvm::SCEV *LoadEnd, llvm::Instruction *maybeWriter,
2853 const llvm::SCEV *StoreStart,
const llvm::SCEV *StoreEnd,
2854 llvm::Loop *scope) {
2862 Loop *anc =
getAncestor(LI.getLoopFor(maybeReader->getParent()),
2863 LI.getLoopFor(maybeWriter->getParent()));
2868 assert(scope == anc || scope->contains(anc));
2904 SmallPtrSet<const Loop *, 1> visitedAncestors;
2905 auto skipLoop = [&](
const Loop *L) {
2907 if (scope && L->contains(scope))
2910 if (anc && (anc == L || anc->contains(L))) {
2911 visitedAncestors.insert(L);
2921 auto hasOverlap = [&](
const SCEV *EndPrev,
const SCEV *StartNext,
2923 for (
auto slim = StartNext; slim != SE.getCouldNotCompute();) {
2926 if (
auto startL = dyn_cast<SCEVAddRecExpr>(slim))
2927 if (skipLoop(startL->getLoop()) &&
2928 SE.isKnownNonPositive(startL->getStepRecurrence(SE))) {
2933 for (
auto elim = EndPrev; elim != SE.getCouldNotCompute();) {
2938 if (
auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2939 if (skipLoop(endL->getLoop()) &&
2940 SE.isKnownNonNegative(endL->getStepRecurrence(SE))) {
2951 if (
auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2952 auto EH = endL->getLoop()->getHeader();
2953 if (
auto startL = dyn_cast<SCEVAddRecExpr>(slim)) {
2954 auto SH = startL->getLoop()->getHeader();
2955 if (EH != SH && !DT.dominates(EH, SH) &&
2956 !DT.dominates(SH, EH))
2962 auto sub = SE.getMinusSCEV(slim, elim);
2963 if (sub != SE.getCouldNotCompute() && SE.isKnownNonNegative(sub))
2968 if (
auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2969 if (SE.isKnownNonPositive(endL->getStepRecurrence(SE))) {
2970 elim = endL->getStart();
2972 }
else if (SE.isKnownNonNegative(endL->getStepRecurrence(SE))) {
2973#if LLVM_VERSION_MAJOR >= 12
2974 auto ebd = SE.getSymbolicMaxBackedgeTakenCount(endL->getLoop());
2976 auto ebd = SE.getBackedgeTakenCount(endL->getLoop());
2978 if (ebd == SE.getCouldNotCompute())
2987 if (
auto startL = dyn_cast<SCEVAddRecExpr>(slim)) {
2988 if (SE.isKnownNonNegative(startL->getStepRecurrence(SE))) {
2989 slim = startL->getStart();
2991 }
else if (SE.isKnownNonPositive(startL->getStepRecurrence(SE))) {
2992#if LLVM_VERSION_MAJOR >= 12
2993 auto sbd = SE.getSymbolicMaxBackedgeTakenCount(startL->getLoop());
2995 auto sbd = SE.getBackedgeTakenCount(startL->getLoop());
2997 if (sbd == SE.getCouldNotCompute())
3010 visitedAncestors.clear();
3011 if (!hasOverlap(StoreEnd, LoadStart,
true)) {
3015 for (
const Loop *L = anc; L != scope; L = L->getParentLoop()) {
3016 if (!visitedAncestors.count(L))
3025 visitedAncestors.clear();
3026 if (!hasOverlap(LoadEnd, StoreStart,
false)) {
3030 for (
const Loop *L = anc; L != scope; L = L->getParentLoop()) {
3031 if (!visitedAncestors.count(L))
3041 llvm::TargetLibraryInfo &TLI, ScalarEvolution &SE,
3042 llvm::LoopInfo &LI, llvm::DominatorTree &DT,
3043 llvm::Instruction *maybeReader,
3044 llvm::Instruction *maybeWriter,
3045 llvm::Loop *scope) {
3046 using namespace llvm;
3049 const SCEV *LoadBegin = SE.getCouldNotCompute();
3050 const SCEV *LoadEnd = SE.getCouldNotCompute();
3052 const SCEV *StoreBegin = SE.getCouldNotCompute();
3053 const SCEV *StoreEnd = SE.getCouldNotCompute();
3055 Value *loadPtr =
nullptr;
3056 Value *storePtr =
nullptr;
3057 if (
auto LI = dyn_cast<LoadInst>(maybeReader)) {
3058 loadPtr = LI->getPointerOperand();
3059 LoadBegin = SE.getSCEV(LI->getPointerOperand());
3060 if (LoadBegin != SE.getCouldNotCompute() &&
3061 !LoadBegin->getType()->isIntegerTy()) {
3062 auto &DL = maybeWriter->getModule()->getDataLayout();
3063 auto width = cast<IntegerType>(DL.getIndexType(LoadBegin->getType()))
3065#if LLVM_VERSION_MAJOR >= 18
3066 auto TS = SE.getConstant(
3067 APInt(width, (int64_t)DL.getTypeStoreSize(LI->getType())));
3069 auto TS = SE.getConstant(
3070 APInt(width, DL.getTypeStoreSize(LI->getType()).getFixedSize()));
3072 LoadEnd = SE.getAddExpr(LoadBegin, TS);
3075 if (
auto SI = dyn_cast<StoreInst>(maybeWriter)) {
3076 storePtr = SI->getPointerOperand();
3077 StoreBegin = SE.getSCEV(SI->getPointerOperand());
3078 if (StoreBegin != SE.getCouldNotCompute() &&
3079 !StoreBegin->getType()->isIntegerTy()) {
3080 auto &DL = maybeWriter->getModule()->getDataLayout();
3081 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3083#if LLVM_VERSION_MAJOR >= 18
3085 SE.getConstant(APInt(width, (int64_t)DL.getTypeStoreSize(
3086 SI->getValueOperand()->getType())));
3088 auto TS = SE.getConstant(
3089 APInt(width, DL.getTypeStoreSize(SI->getValueOperand()->getType())
3092 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3095 if (
auto MS = dyn_cast<MemSetInst>(maybeWriter)) {
3096 storePtr = MS->getArgOperand(0);
3097 StoreBegin = SE.getSCEV(MS->getArgOperand(0));
3098 if (StoreBegin != SE.getCouldNotCompute() &&
3099 !StoreBegin->getType()->isIntegerTy()) {
3100 if (
auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3101 auto &DL = MS->getModule()->getDataLayout();
3102 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3105 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3106 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3110 if (
auto MS = dyn_cast<MemTransferInst>(maybeWriter)) {
3111 storePtr = MS->getArgOperand(0);
3112 StoreBegin = SE.getSCEV(MS->getArgOperand(0));
3113 if (StoreBegin != SE.getCouldNotCompute() &&
3114 !StoreBegin->getType()->isIntegerTy()) {
3115 if (
auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3116 auto &DL = MS->getModule()->getDataLayout();
3117 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3120 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3121 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3125 if (
auto MS = dyn_cast<MemTransferInst>(maybeReader)) {
3126 loadPtr = MS->getArgOperand(1);
3127 LoadBegin = SE.getSCEV(MS->getArgOperand(1));
3128 if (LoadBegin != SE.getCouldNotCompute() &&
3129 !LoadBegin->getType()->isIntegerTy()) {
3130 if (
auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3131 auto &DL = MS->getModule()->getDataLayout();
3132 auto width = cast<IntegerType>(DL.getIndexType(LoadBegin->getType()))
3135 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3136 LoadEnd = SE.getAddExpr(LoadBegin, TS);
3141 if (loadPtr && storePtr)
3148 maybeWriter, StoreBegin, StoreEnd, scope))
3156 llvm::TargetLibraryInfo &TLI,
3157 llvm::Instruction *maybeReader,
3158 llvm::Instruction *maybeWriter) {
3159 assert(maybeReader->getParent()->getParent() ==
3160 maybeWriter->getParent()->getParent());
3161 using namespace llvm;
3162 if (isa<StoreInst>(maybeReader))
3164 if (isa<FenceInst>(maybeReader)) {
3167 if (
auto call = dyn_cast<CallInst>(maybeWriter)) {
3184 if (funcName ==
"jl_array_copy" || funcName ==
"ijl_array_copy")
3187 if (funcName ==
"jl_genericmemory_copy_slice" ||
3188 funcName ==
"ijl_genericmemory_copy_slice")
3191 if (funcName ==
"jl_new_array" || funcName ==
"ijl_new_array")
3194 if (funcName ==
"julia.safepoint")
3197 if (funcName ==
"jl_idtable_rehash" || funcName ==
"ijl_idtable_rehash")
3201 if (funcName ==
"MPI_Send" || funcName ==
"PMPI_Send") {
3205 if (funcName ==
"MPI_Wait" || funcName ==
"PMPI_Wait" ||
3206 funcName ==
"MPI_Waitall" || funcName ==
"PMPI_Waitall") {
3207#if LLVM_VERSION_MAJOR > 11
3208 auto loc = LocationSize::afterPointer();
3210 auto loc = MemoryLocation::UnknownSize;
3212 size_t off = (funcName ==
"MPI_Wait" || funcName ==
"PMPI_Wait") ? 0 : 1;
3214 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(off + 1),
3217 if (!isRefSet(AA.getModRefInfo(maybeReader,
3218 call->getArgOperand(off + 0), loc)))
3222 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3232 if (funcName ==
"MPI_Isend" || funcName ==
"PMPI_Isend") {
3235 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3242#if LLVM_VERSION_MAJOR > 11
3243 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3244 LocationSize::afterPointer())))
3247 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3248 MemoryLocation::UnknownSize)))
3253 if (funcName ==
"MPI_Irecv" || funcName ==
"PMPI_Irecv" ||
3254 funcName ==
"MPI_Recv" || funcName ==
"PMPI_Recv") {
3256 if (Constant *C = dyn_cast<Constant>(call->getArgOperand(2))) {
3257 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
3258 C = CE->getOperand(0);
3260 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
3261 if (GV->getName() ==
"ompi_mpi_double") {
3262 type =
ConcreteType(Type::getDoubleTy(C->getContext()));
3263 }
else if (GV->getName() ==
"ompi_mpi_float") {
3264 type =
ConcreteType(Type::getFloatTy(C->getContext()));
3271 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3273 if (R.isKnown() && type != R) {
3276 if (funcName ==
"MPI_Recv" || funcName ==
"PMPI_Recv" ||
3279#if LLVM_VERSION_MAJOR > 11
3280 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3281 LocationSize::afterPointer())))
3284 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3285 MemoryLocation::UnknownSize)))
3291 if (
auto II = dyn_cast<IntrinsicInst>(call)) {
3292 if (II->getIntrinsicID() == Intrinsic::stacksave)
3294 if (II->getIntrinsicID() == Intrinsic::stackrestore)
3296 if (II->getIntrinsicID() == Intrinsic::trap)
3298#if LLVM_VERSION_MAJOR >= 13
3299 if (II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
3304 if (
auto iasm = dyn_cast<InlineAsm>(call->getCalledOperand())) {
3305 if (StringRef(iasm->getAsmString()).contains(
"exit"))
3309 if (
auto call = dyn_cast<CallInst>(maybeReader)) {
3324 if (
auto II = dyn_cast<IntrinsicInst>(call)) {
3325 if (II->getIntrinsicID() == Intrinsic::stacksave)
3327 if (II->getIntrinsicID() == Intrinsic::stackrestore)
3329 if (II->getIntrinsicID() == Intrinsic::trap)
3331#if LLVM_VERSION_MAJOR >= 13
3332 if (II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
3337 if (
auto call = dyn_cast<InvokeInst>(maybeWriter)) {
3351 if (funcName ==
"jl_array_copy" || funcName ==
"ijl_array_copy")
3354 if (funcName ==
"jl_genericmemory_copy_slice" ||
3355 funcName ==
"ijl_genericmemory_copy_slice")
3358 if (funcName ==
"jl_idtable_rehash" || funcName ==
"ijl_idtable_rehash")
3361 if (
auto iasm = dyn_cast<InlineAsm>(call->getCalledOperand())) {
3362 if (StringRef(iasm->getAsmString()).contains(
"exit"))
3366 if (
auto call = dyn_cast<InvokeInst>(maybeReader)) {
3381 assert(maybeWriter->mayWriteToMemory());
3382 assert(maybeReader->mayReadFromMemory());
3384 if (
auto li = dyn_cast<LoadInst>(maybeReader)) {
3386 auto TT = TR->
query(li)[{-1}];
3388 if (
auto si = dyn_cast<StoreInst>(maybeWriter)) {
3389 auto TT2 = TR->
query(si->getValueOperand())[{-1}];
3394 auto &dl = li->getParent()->getParent()->getParent()->getDataLayout();
3396 (dl.getTypeSizeInBits(si->getValueOperand()->getType()) + 7) / 8;
3397 TT2 = TR->
query(si->getPointerOperand()).
Lookup(len, dl)[{-1}];
3405 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(li)));
3407 if (
auto rmw = dyn_cast<AtomicRMWInst>(maybeReader)) {
3408 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(rmw)));
3410 if (
auto xch = dyn_cast<AtomicCmpXchgInst>(maybeReader)) {
3411 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(xch)));
3413 if (
auto mti = dyn_cast<MemTransferInst>(maybeReader)) {
3415 AA.getModRefInfo(maybeWriter, MemoryLocation::getForSource(mti)));
3418 if (
auto si = dyn_cast<StoreInst>(maybeWriter)) {
3419 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(si)));
3421 if (
auto rmw = dyn_cast<AtomicRMWInst>(maybeWriter)) {
3422 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(rmw)));
3424 if (
auto xch = dyn_cast<AtomicCmpXchgInst>(maybeWriter)) {
3425 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(xch)));
3427 if (
auto mti = dyn_cast<MemIntrinsic>(maybeWriter)) {
3429 AA.getModRefInfo(maybeReader, MemoryLocation::getForDest(mti)));
3432 if (
auto cb = dyn_cast<CallInst>(maybeReader)) {
3433 return isModOrRefSet(AA.getModRefInfo(maybeWriter, cb));
3435 if (
auto cb = dyn_cast<InvokeInst>(maybeReader)) {
3436 return isModOrRefSet(AA.getModRefInfo(maybeWriter, cb));
3438 llvm::errs() <<
" maybeReader: " << *maybeReader
3439 <<
" maybeWriter: " << *maybeWriter <<
"\n";
3440 llvm_unreachable(
"unknown inst2");
3448 if (
auto CI = dyn_cast<CastInst>(ptr)) {
3449 ptr = CI->getOperand(0);
3452 if (
auto CI = dyn_cast<GetElementPtrInst>(ptr)) {
3453 auto &DL = CI->getParent()->getParent()->getParent()->getDataLayout();
3454#if LLVM_VERSION_MAJOR >= 20
3455 SmallMapVector<Value *, APInt, 4> VariableOffsets;
3457 MapVector<Value *, APInt> VariableOffsets;
3459 auto width =
sizeof(size_t) * 8;
3460 APInt Offset(width, 0);
3461 bool success =
collectOffset(cast<GEPOperator>(CI), DL, width,
3462 VariableOffsets, Offset);
3463 if (!success || VariableOffsets.size() != 0 || Offset.isNegative()) {
3466 offset += Offset.getZExtValue();
3467 ptr = CI->getOperand(0);
3470 if (isa<AllocaInst>(ptr)) {
3473 if (
auto LI = dyn_cast<LoadInst>(ptr)) {
3481 return cast<AllocaInst>(ptr);
3487SmallVector<std::tuple<Instruction *, Value *, size_t>, 1>
3489 SmallVector<std::pair<Value *, size_t>, 1> todo;
3490 todo.emplace_back(AI, 0);
3492 SmallVector<std::tuple<Instruction *, Value *, size_t>, 1> users;
3493 while (todo.size()) {
3494 auto pair = todo.pop_back_val();
3495 Value *ptr = pair.first;
3496 size_t suboff = pair.second;
3498 for (
auto U : ptr->users()) {
3499 if (
auto CI = dyn_cast<CastInst>(U)) {
3500 todo.emplace_back(CI, suboff);
3503 if (
auto CI = dyn_cast<GetElementPtrInst>(U)) {
3504 auto &DL = CI->getParent()->getParent()->getParent()->getDataLayout();
3505#if LLVM_VERSION_MAJOR >= 20
3506 SmallMapVector<Value *, APInt, 4> VariableOffsets;
3508 MapVector<Value *, APInt> VariableOffsets;
3510 auto width =
sizeof(size_t) * 8;
3511 APInt Offset(width, 0);
3512 bool success =
collectOffset(cast<GEPOperator>(CI), DL, width,
3513 VariableOffsets, Offset);
3515 if (!success || VariableOffsets.size() != 0 || Offset.isNegative()) {
3516 users.emplace_back(cast<Instruction>(U), ptr, suboff);
3519 todo.emplace_back(CI, suboff + Offset.getZExtValue());
3522 users.emplace_back(cast<Instruction>(U), ptr, suboff);
3532SmallVector<std::pair<Value *, size_t>, 1>
3535 SmallVector<std::pair<Value *, size_t>, 1> options;
3538 std::set<std::tuple<Instruction *, Value *, size_t>> seen;
3540 while (todo.size()) {
3541 auto pair = todo.pop_back_val();
3542 if (seen.count(pair))
3545 Instruction *U = std::get<0>(pair);
3546 Value *ptr = std::get<1>(pair);
3547 size_t suboff = std::get<2>(pair);
3550 if (isa<LoadInst>(U)) {
3553 if (
auto MTI = dyn_cast<MemTransferInst>(U))
3554 if (MTI->getOperand(0) != ptr) {
3557 if (
auto I = dyn_cast<Instruction>(U)) {
3558 if (!I->mayWriteToMemory() && I->getType()->isVoidTy())
3562 if (
auto SI = dyn_cast<StoreInst>(U)) {
3563 auto &DL = SI->getParent()->getParent()->getParent()->getDataLayout();
3566 if (SI->getPointerOperand() == ptr) {
3568 (DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType()) + 7) /
3571 if (storeSz + suboff <= offset)
3574 if (offset + valSz <= suboff)
3577 if (valSz <= storeSz) {
3578 assert(offset >= suboff);
3579 options.emplace_back(SI->getValueOperand(), offset - suboff);
3586 if (SI->getValueOperand() == ptr) {
3588 size_t mid_offset = 0;
3591 bool sublegal =
true;
3592 auto ptrSz = (DL.getTypeStoreSizeInBits(ptr->getType()) + 7) / 8;
3599 for (
auto &&[subPtr, subOff] : subPtrs) {
3603 todo.emplace_back(std::move(pair3));
3612 if (
auto II = dyn_cast<IntrinsicInst>(U)) {
3613 if (II->getCalledFunction()->getName() ==
"llvm.enzyme.lifetime_start" ||
3614 II->getCalledFunction()->getName() ==
"llvm.enzyme.lifetime_end")
3616 if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
3617 II->getIntrinsicID() == Intrinsic::lifetime_end)
3623 if (
auto MTI = dyn_cast<MemTransferInst>(U)) {
3624 if (
auto CI = dyn_cast<ConstantInt>(MTI->getLength())) {
3625 if (MTI->getOperand(0) == ptr) {
3626 auto storeSz = CI->getValue();
3629 if ((storeSz + suboff).ule(offset))
3633 if (offset + valSz <= suboff)
3636 if (suboff == 0 && CI->getValue().uge(offset + valSz)) {
3637 size_t midoffset = 0;
3643 if (midoffset != 0) {
3648 todo.emplace_back(std::move(pair3));
3665 if (
auto LI = dyn_cast<LoadInst>(V)) {
3667 auto &DL = LI->getParent()->getParent()->getParent()->getDataLayout();
3668 valSz = (DL.getTypeSizeInBits(LI->getType()) + 7) / 8;
3671 Value *ptr = LI->getPointerOperand();
3681 offset += preOffset;
3689 std::set<Value *> res;
3690 for (
auto &&[opt, startOff] : opts) {
3697 if (res.size() != 1) {
3700 Value *retval = *res.begin();
3703 if (
auto EVI = dyn_cast<ExtractValueInst>(V)) {
3707 EVI->getIndices(),
"",
false);
3708 if (em !=
nullptr) {
3713 if (
auto LI = dyn_cast<LoadInst>(EVI->getAggregateOperand())) {
3714 auto offset = preOffset;
3716 auto &DL = LI->getParent()->getParent()->getParent()->getDataLayout();
3718 DL, EVI->getOperand(0)->getType(), EVI->getIndices());
3721 auto &DL = EVI->getParent()->getParent()->getParent()->getDataLayout();
3722 valSz = (DL.getTypeSizeInBits(EVI->getType()) + 7) / 8;
3731 while (!isa<Function>(fn)) {
3732 if (
auto ci = dyn_cast<CastInst>(fn)) {
3733 fn = ci->getOperand(0);
3736 if (
auto ci = dyn_cast<ConstantExpr>(fn)) {
3738 fn = ci->getOperand(0);
3742 if (
auto ci = dyn_cast<BlockAddress>(fn)) {
3743 fn = ci->getFunction();
3746 if (
auto *GA = dyn_cast<GlobalAlias>(fn)) {
3747 fn = GA->getAliasee();
3750 if (
auto *
Call = dyn_cast<CallInst>(fn)) {
3751 if (
auto F =
Call->getCalledFunction()) {
3752 SmallPtrSet<Value *, 1> ret;
3753 for (
auto &BB : *F) {
3754 if (
auto RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
3755 ret.insert(RI->getReturnValue());
3758 if (ret.size() == 1) {
3759 auto val = *ret.begin();
3761 if (isa<Constant>(val)) {
3765 if (
auto arg = dyn_cast<Argument>(val)) {
3766 fn =
Call->getArgOperand(arg->getArgNo());
3772 if (
auto *
Call = dyn_cast<InvokeInst>(fn)) {
3773 if (
auto F =
Call->getCalledFunction()) {
3774 SmallPtrSet<Value *, 1> ret;
3775 for (
auto &BB : *F) {
3776 if (
auto RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
3777 ret.insert(RI->getReturnValue());
3780 if (ret.size() == 1) {
3781 auto val = *ret.begin();
3782 while (isa<LoadInst>(val)) {
3790 if (isa<Constant>(val)) {
3794 if (
auto arg = dyn_cast<Argument>(val)) {
3795 fn =
Call->getArgOperand(arg->getArgNo());
3817 if (!F.isDeclaration()) {
3836 ArrayRef<const char *> floatTypes,
3837 ArrayRef<const char *> stems,
3838 ArrayRef<const char *> suffixes,
3839 const char *&prefixOut,
const char *&floatTypeOut,
3840 const char *&stemOut,
const char *&suffixOut) {
3841 for (
const char *p : prefixes) {
3842 StringRef afterPrefix = in;
3843 if (!afterPrefix.consume_front(p))
3845 for (
const char *t : floatTypes) {
3846 StringRef afterType = afterPrefix;
3847 if (!afterType.consume_front(t))
3849 for (
const char *s : suffixes) {
3850 StringRef stem = afterType;
3851 if (!stem.consume_back(s))
3853 for (
const char *f : stems) {
3868#if LLVM_VERSION_MAJOR >= 16
3869std::optional<BlasInfo>
extractBLAS(llvm::StringRef in)
3874 static const char *extractable[] = {
3875 "dot",
"scal",
"axpy",
"gemv",
"gemm",
"spmv",
"syrk",
"nrm2",
3876 "trmm",
"trmv",
"symm",
"potrf",
"potrs",
"copy",
"spmv",
"syr2k",
3877 "potrs",
"getrf",
"getrs",
"trtrs",
"getri",
"symv",
"lacpy",
"trsv",
3879 static const char *floatType[] = {
"s",
"d",
"c",
"z"};
3880 static const char *prefixes[] = {
"" ,
"cblas_"};
3881 static const char *suffixes[] = {
"",
"_",
"64_",
"_64_"};
3883 const char *p =
nullptr, *t =
nullptr, *f =
nullptr, *s =
nullptr;
3884 if (
matchBLASName(in, prefixes, floatType, extractable, suffixes, p, t, f,
3886 bool is64 = llvm::StringRef(s).contains(
"64");
3893 static const char *cuCFloatType[] = {
"S",
"D",
"C",
"Z"};
3894 static const char *cuCPrefixes[] = {
"cublas"};
3895 static const char *cuSuffixes[] = {
"",
"_v2",
"_64",
"_v2_64"};
3896 if (
matchBLASName(in, cuCPrefixes, cuCFloatType, extractable, cuSuffixes, p,
3898 bool is64 = llvm::StringRef(s).contains(
"64");
3905 static const char *cuFFloatType[] = {
"s",
"d",
"c",
"z"};
3906 static const char *cuFPrefixes[] = {
"cublas_"};
3907 static const char *cuFSuffixes[] = {
""};
3908 if (
matchBLASName(in, cuFPrefixes, cuFFloatType, extractable, cuFSuffixes, p,
3921 return cast<Constant>(
3924 return Constant::getNullValue(T);
3926 return UndefValue::get(T);
3930 llvm::IRBuilder<> &BuilderM,
3931 llvm::Value *mask) {
3933 auto current_bb = BuilderM.GetInsertBlock();
3934 auto fn = current_bb->getParent();
3935 auto mod = fn->getParent();
3936 auto &Context = mod->getContext();
3938 std::string type_str;
3939 llvm::raw_string_ostream type_ss(type_str);
3940 toset->getType()->print(type_ss);
3941 std::string fn_name =
"__enzyme_sanitize_nan_" + type_str;
3943 llvm::FunctionType *SanitizeFT = llvm::FunctionType::get(
3944 llvm::Type::getVoidTy(Context),
3947 auto SanitizeFCallee = mod->getOrInsertFunction(fn_name, SanitizeFT);
3948 llvm::Function *SanitizeF =
3949 llvm::cast<llvm::Function>(SanitizeFCallee.getCallee());
3951 if (SanitizeF->empty()) {
3952 SanitizeF->setLinkage(Function::LinkageTypes::InternalLinkage);
3953 llvm::BasicBlock *entry =
3954 llvm::BasicBlock::Create(Context,
"entry", SanitizeF);
3955 llvm::BasicBlock *good =
3956 llvm::BasicBlock::Create(Context,
"good", SanitizeF);
3957 llvm::BasicBlock *bad =
3958 llvm::BasicBlock::Create(Context,
"bad", SanitizeF);
3960 llvm::IRBuilder<> B(entry);
3961 llvm::Value *inp = SanitizeF->getArg(0);
3962 llvm::Value *msg_ptr = SanitizeF->getArg(1);
3964 llvm::Value *cmp = B.CreateFCmpUNO(inp, inp);
3965 if (
auto VT = llvm::dyn_cast<llvm::VectorType>(inp->getType())) {
3966#if LLVM_VERSION_MAJOR >= 12
3967 unsigned len = VT->getElementCount().getKnownMinValue();
3969 unsigned len = VT->getNumElements();
3971 llvm::Value *res = B.CreateExtractElement(cmp, (uint64_t)0);
3972 for (
unsigned i = 1; i < len; ++i) {
3973 res = B.CreateOr(res, B.CreateExtractElement(cmp, (uint64_t)i));
3977 B.CreateCondBr(cmp, bad, good);
3979 B.SetInsertPoint(good);
3982 B.SetInsertPoint(bad);
3985 wrap(msg_ptr), wrap(&B));
3987 llvm::FunctionType *PutsFT = llvm::FunctionType::get(
3988 llvm::Type::getInt32Ty(Context), {
getInt8PtrTy(Context)},
false);
3989 auto PutsF = mod->getOrInsertFunction(
"puts", PutsFT);
3990 B.CreateCall(PutsF, msg_ptr);
3992 llvm::FunctionType *ExitFT =
3993 llvm::FunctionType::get(llvm::Type::getVoidTy(Context),
3994 {llvm::Type::getInt32Ty(Context)},
false);
3995 auto ExitF = mod->getOrInsertFunction(
"exit", ExitFT);
3997 ExitF, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Context), 1));
3999 B.CreateUnreachable();
4002 std::string stringv =
"Enzyme: Found nan while computing derivative of ";
4005 llvm::raw_string_ostream ss(
str);
4006 if (
auto inst = llvm::dyn_cast<llvm::Instruction>(val)) {
4007 ss << *inst <<
"\n";
4012 stringv += ss.str();
4017 BuilderM.CreateCall(SanitizeFCallee, {toset,
getString(*mod, stringv)});
4022 wrap(&BuilderM), wrap(mask)));
4027 llvm::FastMathFlags f;
4034 llvm::SmallVectorImpl<llvm::Value *> &cacheValues,
4035 llvm::IRBuilder<> &BuilderZ,
const Twine &name) {
4038 if (!arg->getType()->isPointerTy()) {
4039 assert(arg->getType() == ty);
4040 cacheValues.push_back(arg);
4043#if LLVM_VERSION_MAJOR < 17
4044 auto PT = cast<PointerType>(arg->getType());
4045#if LLVM_VERSION_MAJOR <= 14
4046 if (PT->getElementType() != ty)
4047 arg = BuilderZ.CreatePointerCast(
4051 if (!PT->isOpaqueOrPointeeTypeMatches(PT2))
4052 arg = BuilderZ.CreatePointerCast(
4056 arg = BuilderZ.CreateLoad(ty, arg,
"avld." + name);
4057 cacheValues.push_back(arg);
4063 bool cublas, IntegerType *julia_decl,
4064 IRBuilder<> &entryBuilder,
4065 llvm::Twine
const &name) {
4070 entryBuilder.CreateAlloca(V->getType(),
nullptr,
"byref." + name);
4071 B.CreateStore(V, allocV);
4074 allocV = B.CreatePointerCast(allocV,
getInt8PtrTy(V->getContext()),
4080 Type *fpTy, IRBuilder<> &entryBuilder,
4081 llvm::Twine
const &name) {
4086 entryBuilder.CreateAlloca(V->getType(),
nullptr,
"byref." + name);
4087 B.CreateStore(V, allocV);
4090 allocV = B.CreatePointerCast(allocV, fpTy,
"fpcast." + name);
4095Value *
is_lower(IRBuilder<> &B, Value *uplo,
bool byRef,
bool cublas) {
4097 Value *isNormal =
nullptr;
4098 isNormal = B.CreateICmpEQ(
4099 uplo, ConstantInt::get(uplo->getType(),
4103 if (
auto CI = dyn_cast<ConstantInt>(uplo)) {
4104 if (CI->getValue() ==
'L' || CI->getValue() ==
'l')
4105 return ConstantInt::getTrue(B.getContext());
4106 if (CI->getValue() ==
'U' || CI->getValue() ==
'u')
4107 return ConstantInt::getFalse(B.getContext());
4111 IntegerType *charTy = IntegerType::get(uplo->getContext(), 8);
4112 uplo = B.CreateLoad(charTy, uplo,
"loaded.trans");
4114 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'L'));
4115 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'l'));
4117 return B.CreateOr(isl, isL);
4120 auto capi = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 122));
4123 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'L'));
4124 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'l'));
4125 return B.CreateOr(capi, B.CreateOr(isl, isL));
4129Value *
is_nonunit(IRBuilder<> &B, Value *uplo,
bool byRef,
bool cublas) {
4131 Value *isNormal =
nullptr;
4133 B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
4137 if (
auto CI = dyn_cast<ConstantInt>(uplo)) {
4138 if (CI->getValue() ==
'N' || CI->getValue() ==
'n')
4139 return ConstantInt::getTrue(B.getContext());
4140 if (CI->getValue() ==
'U' || CI->getValue() ==
'u')
4141 return ConstantInt::getFalse(B.getContext());
4145 IntegerType *charTy = IntegerType::get(uplo->getContext(), 8);
4146 uplo = B.CreateLoad(charTy, uplo,
"loaded.nonunit");
4148 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'N'));
4149 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'n'));
4151 return B.CreateOr(isl, isL);
4154 auto capi = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 131));
4157 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'N'));
4158 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
'n'));
4159 return B.CreateOr(capi, B.CreateOr(isl, isL));
4163llvm::Value *
is_normal(IRBuilder<> &B, llvm::Value *trans,
bool byRef,
4166 Value *isNormal =
nullptr;
4167 isNormal = B.CreateICmpEQ(
4168 trans, ConstantInt::get(trans->getType(),
4173 if (
auto CI = dyn_cast<ConstantInt>(trans)) {
4174 if (CI->getValue() ==
'N' || CI->getValue() ==
'n')
4175 return ConstantInt::getTrue(
4180 IntegerType *charTy = IntegerType::get(trans->getContext(), 8);
4181 trans = B.CreateLoad(charTy, trans,
"loaded.trans");
4183 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'N'));
4184 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'n'));
4186 return B.CreateOr(isn, isN);
4191 auto capi = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 111));
4192 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'N'));
4193 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'n'));
4195 return B.CreateOr(capi, B.CreateOr(isn, isN));
4199llvm::Value *
is_left(IRBuilder<> &B, llvm::Value *side,
bool byRef,
4202 Value *isNormal =
nullptr;
4203 isNormal = B.CreateICmpEQ(
4204 side, ConstantInt::get(side->getType(),
4209 if (
auto CI = dyn_cast<ConstantInt>(side)) {
4210 if (CI->getValue() ==
'L' || CI->getValue() ==
'l')
4211 return ConstantInt::getTrue(B.getContext());
4212 if (CI->getValue() ==
'R' || CI->getValue() ==
'r')
4213 return ConstantInt::getFalse(B.getContext());
4217 IntegerType *charTy = IntegerType::get(side->getContext(), 8);
4218 side = B.CreateLoad(charTy, side,
"loaded.side");
4220 auto isL = B.CreateICmpEQ(side, ConstantInt::get(side->getType(),
'L'));
4221 auto isl = B.CreateICmpEQ(side, ConstantInt::get(side->getType(),
'l'));
4223 return B.CreateOr(isl, isL);
4228 auto capi = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 141));
4229 auto isL = B.CreateICmpEQ(side, ConstantInt::get(side->getType(),
'L'));
4230 auto isl = B.CreateICmpEQ(side, ConstantInt::get(side->getType(),
'l'));
4232 return B.CreateOr(capi, B.CreateOr(isl, isL));
4242llvm::Value *
transpose(std::string floatType, IRBuilder<> &B, llvm::Value *V,
4244 llvm::Type *T = V->getType();
4246 auto isT1 = B.CreateICmpEQ(V, ConstantInt::get(T, 1));
4247 auto isT0 = B.CreateICmpEQ(V, ConstantInt::get(T, 0));
4248 return B.CreateSelect(isT1, ConstantInt::get(V->getType(), 0),
4249 B.CreateSelect(isT0,
4250 ConstantInt::get(V->getType(), 1),
4251 ConstantInt::get(V->getType(), 42)));
4252 }
else if (T->isIntegerTy(8)) {
4253 if (floatType ==
"z" || floatType ==
"c") {
4254 auto isn = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'n'));
4255 auto sel1 = B.CreateSelect(isn, ConstantInt::get(V->getType(),
'c'),
4256 ConstantInt::get(V->getType(), 0));
4258 auto isN = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'N'));
4260 B.CreateSelect(isN, ConstantInt::get(V->getType(),
'C'), sel1);
4262 auto ist = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'c'));
4264 B.CreateSelect(ist, ConstantInt::get(V->getType(),
'n'), sel2);
4266 auto isT = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'C'));
4267 return B.CreateSelect(isT, ConstantInt::get(V->getType(),
'N'), sel3);
4270 auto isn = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'n'));
4271 auto sel1 = B.CreateSelect(isn, ConstantInt::get(V->getType(),
't'),
4272 ConstantInt::get(V->getType(),
'N'));
4274 auto isN = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'N'));
4276 B.CreateSelect(isN, ConstantInt::get(V->getType(),
'T'), sel1);
4278 auto ist = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
't'));
4280 B.CreateSelect(ist, ConstantInt::get(V->getType(),
'n'), sel2);
4282 auto isT = B.CreateICmpEQ(V, ConstantInt::get(V->getType(),
'T'));
4283 return B.CreateSelect(isT, ConstantInt::get(V->getType(),
'N'), sel3);
4286 }
else if (T->isIntegerTy(32)) {
4287 auto is111 = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 111));
4288 auto sel1 = B.CreateSelect(
4289 B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 112)),
4290 ConstantInt::get(V->getType(), 111), ConstantInt::get(V->getType(), 0));
4291 return B.CreateSelect(is111, ConstantInt::get(V->getType(), 112), sel1);
4294 llvm::raw_string_ostream ss(s);
4295 ss <<
"cannot handle unknown trans blas value\n" << V;
4298 nullptr,
nullptr,
nullptr);
4300 EmitFailure(
"unknown trans blas value", B.getCurrentDebugLocation(),
4301 B.GetInsertBlock()->getParent(), ss.str());
4314 llvm::ArrayRef<llvm::Value *> trans,
4315 llvm::Value *arg_ld, llvm::Value *dim1,
4316 llvm::Value *dim2,
bool cacheMat,
bool byRef,
4321 assert(trans.size() == 1);
4323 llvm::Value *width =
4329llvm::Value *
transpose(std::string floatType, llvm::IRBuilder<> &B,
4330 llvm::Value *V,
bool byRef,
bool cublas,
4331 llvm::IntegerType *julia_decl,
4332 llvm::IRBuilder<> &entryBuilder,
4333 const llvm::Twine &name) {
4337 if (
auto CI = dyn_cast<ConstantInt>(V)) {
4338 if (floatType ==
"c" || floatType ==
"z") {
4339 if (CI->getValue() ==
'N')
4340 return ConstantInt::get(CI->getType(),
'C');
4341 if (CI->getValue() ==
'c')
4342 return ConstantInt::get(CI->getType(),
'c');
4344 if (CI->getValue() ==
'N')
4345 return ConstantInt::get(CI->getType(),
'T');
4346 if (CI->getValue() ==
'n')
4347 return ConstantInt::get(CI->getType(),
't');
4353 return B.CreateSelect(
4354 B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 111)),
4355 ConstantInt::get(V->getType(), 112),
4356 ConstantInt::get(V->getType(), 111));
4360 auto charType = IntegerType::get(V->getContext(), 8);
4361 V = B.CreateLoad(charType, V,
"ld." + name);
4367 "transpose." + name);
4371 llvm::Value *V,
bool byRef) {
4375 if (V->getType()->isIntegerTy())
4376 V = B.CreateIntToPtr(V,
getUnqual(intType));
4378 V = B.CreatePointerCast(
4380 cast<PointerType>(V->getType())->getAddressSpace()));
4381 return B.CreateLoad(intType, V);
4385 ArrayRef<llvm::Value *> transA,
4386 bool byRef,
bool cublas) {
4387 assert(transA.size() == 1);
4388 auto trans = transA[0];
4390 auto charType = IntegerType::get(trans->getContext(), 8);
4391 trans = B.CreateLoad(charType, trans,
"ld.row.trans");
4394 Value *cond =
nullptr;
4398 cond = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 111));
4400 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'n'));
4401 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(),
'N'));
4402 cond = B.CreateOr(isN, isn);
4407 cond = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 0));
4412 ArrayRef<llvm::Value *> transA,
4413 ArrayRef<llvm::Value *> row,
4414 ArrayRef<llvm::Value *> col,
4415 bool byRef,
bool cublas) {
4417 assert(row.size() == col.size());
4418 SmallVector<Value *, 1> toreturn;
4419 for (
size_t i = 0; i < row.size(); i++) {
4422 if (lhs->getType() != rhs->getType())
4423 rhs = B.CreatePointerCast(rhs, lhs->getType());
4424 toreturn.push_back(B.CreateSelect(conds[0], lhs, rhs));
4432 if (isa<PointerType>(T)) {
4438 }
else if (isa<StructType>(T) || isa<ArrayType>(T) || isa<VectorType>(T)) {
4439 for (Type *ElT : T->subtypes()) {
4445 if (isa<ArrayType>(T))
4446 count *= cast<ArrayType>(T)->getNumElements();
4447 else if (isa<VectorType>(T)) {
4448#if LLVM_VERSION_MAJOR >= 12
4449 count *= cast<VectorType>(T)->getElementCount().getKnownMinValue();
4451 count *= cast<VectorType>(T)->getNumElements();
4459#if LLVM_VERSION_MAJOR >= 20
4460bool collectOffset(GEPOperator *gep,
const DataLayout &DL,
unsigned BitWidth,
4461 SmallMapVector<Value *, APInt, 4> &VariableOffsets,
4462 APInt &ConstantOffset)
4465 MapVector<Value *, APInt> &VariableOffsets,
4466 APInt &ConstantOffset)
4469#if LLVM_VERSION_MAJOR >= 13
4470 return gep->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset);
4472 assert(BitWidth == DL.getIndexSizeInBits(gep->getPointerAddressSpace()) &&
4473 "The offset bit width does not match DL specification.");
4475 auto CollectConstantOffset = [&](APInt Index, uint64_t Size) {
4476 Index = Index.sextOrTrunc(BitWidth);
4477 APInt IndexedSize = APInt(BitWidth, Size);
4478 ConstantOffset += Index * IndexedSize;
4481 for (gep_type_iterator GTI = gep_type_begin(gep), GTE = gep_type_end(gep);
4482 GTI != GTE; ++GTI) {
4484 bool ScalableType = isa<ScalableVectorType>(GTI.getIndexedType());
4486 Value *V = GTI.getOperand();
4487 StructType *STy = GTI.getStructTypeOrNull();
4489 if (
auto ConstOffset = dyn_cast<ConstantInt>(V)) {
4490 if (ConstOffset->isZero())
4501 unsigned ElementIdx = ConstOffset->getZExtValue();
4502 const StructLayout *SL = DL.getStructLayout(STy);
4504 CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)),
4508 CollectConstantOffset(ConstOffset->getValue(),
4509 DL.getTypeAllocSize(GTI.getIndexedType()));
4513 if (STy || ScalableType)
4516 APInt(BitWidth, DL.getTypeAllocSize(GTI.getIndexedType()));
4519 if (IndexedSize != 0) {
4520 VariableOffsets.insert({V, APInt(BitWidth, 0)});
4521 VariableOffsets[V] += IndexedSize;
4529 llvm::Intrinsic::ID ID, llvm::Type *RetTy,
4530 llvm::ArrayRef<llvm::Value *>
Args,
4531 llvm::Instruction *FMFSource,
4532 const llvm::Twine &Name) {
4533#if LLVM_VERSION_MAJOR >= 23
4534 llvm::CallInst *nres = dyn_cast<llvm::CallInst>(
4535 B.CreateIntrinsic(RetTy, ID,
Args, FMFSource, Name));
4536#elif LLVM_VERSION_MAJOR >= 16
4537 llvm::CallInst *nres = B.CreateIntrinsic(RetTy, ID,
Args, FMFSource, Name);
4539 SmallVector<Intrinsic::IITDescriptor, 1> Table;
4540 Intrinsic::getIntrinsicInfoTableEntries(ID, Table);
4541 ArrayRef<Intrinsic::IITDescriptor> TableRef(Table);
4543 SmallVector<Type *, 2> ArgTys;
4544 ArgTys.reserve(
Args.size());
4545 for (
auto &I :
Args)
4546 ArgTys.push_back(I->getType());
4547 FunctionType *FTy = FunctionType::get(RetTy, ArgTys,
false);
4548 SmallVector<Type *, 2> OverloadTys;
4549 Intrinsic::MatchIntrinsicTypesResult Res =
4550 matchIntrinsicSignature(FTy, TableRef, OverloadTys);
4552 assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
4553 "Wrong types for intrinsic!");
4554 Function *Fn = Intrinsic::getDeclaration(B.GetInsertPoint()->getModule(), ID,
4556 CallInst *nres = B.CreateCall(Fn,
Args, {}, Name);
4558 nres->copyFastMathFlags(FMFSource);
4570llvm::Value *
get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res) {
4571 auto ty = res->getType();
4572 unsigned tsize = builder.GetInsertBlock()
4576 .getTypeSizeInBits(ty);
4578 auto ity = IntegerType::get(ty->getContext(), tsize);
4580 auto as_int = builder.CreateBitCast(res, ity);
4581 auto masked = builder.CreateXor(as_int, ConstantInt::get(ity, 1));
4582 auto neighbor = builder.CreateBitCast(masked, ty);
4584 auto diff = builder.CreateFSub(res, neighbor);
4586 auto absres = builder.CreateIntrinsic(Intrinsic::fabs,
4587 ArrayRef<Type *>(diff->getType()),
4588 ArrayRef<Value *>(diff));
4594 llvm::Instruction &inst,
4596 llvm::IRBuilder<> &Builder2,
4597 llvm::Value *condition) {
4601 wrap(condition), wrap(&Builder2)));
4603 auto &M = *inst.getParent()->getParent()->getParent();
4604 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4605 {getInt8PtrTy(M.getContext())},
false);
4607 raw_string_ostream ss(
str);
4608 ss << message <<
"\n";
4612 auto PutsF = M.getOrInsertFunction(
"puts", FT);
4613 Builder2.CreateCall(PutsF, msg);
4616 FunctionType::get(Type::getVoidTy(M.getContext()),
4617 {Type::getInt32Ty(M.getContext())},
false);
4619 auto ExitF = M.getOrInsertFunction(
"exit", FT2);
4620 Builder2.CreateCall(ExitF,
4621 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4624 if (StringRef(message).
contains(
"cannot handle above cast")) {
4627 EmitFailure(
"NoDerivative", inst.getDebugLoc(), &inst, message);
4634 Value *toshow = todiff;
4636 toshow = context.
req;
4640 nullptr, wrap(todiff), wrap(context.
ip));
4643 auto &M = *context.
ip->GetInsertBlock()->getParent()->getParent();
4644 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4645 {getInt8PtrTy(M.getContext())},
false);
4647 raw_string_ostream ss(
str);
4648 ss << message <<
"\n";
4649 if (
auto inst = dyn_cast<Instruction>(todiff))
4652 auto PutsF = M.getOrInsertFunction(
"puts", FT);
4653 context.
ip->CreateCall(PutsF, msg);
4656 FunctionType::get(Type::getVoidTy(M.getContext()),
4657 {Type::getInt32Ty(M.getContext())},
false);
4659 auto ExitF = M.getOrInsertFunction(
"exit", FT2);
4660 context.
ip->CreateCall(
4661 ExitF, ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4663 }
else if (context.
req) {
4667 }
else if (
auto arg = dyn_cast<Instruction>(todiff)) {
4668 auto loc = arg->getDebugLoc();
4679 gutils->
TR.
analyzer,
nullptr, wrap(&Builder2));
4681 auto &M = *inst.getParent()->getParent()->getParent();
4682 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4683 {getInt8PtrTy(M.getContext())},
false);
4685 raw_string_ostream ss(
str);
4686 ss << message <<
"\n";
4689 auto PutsF = M.getOrInsertFunction(
"puts", FT);
4690 Builder2.CreateCall(PutsF, msg);
4693 FunctionType::get(Type::getVoidTy(M.getContext()),
4694 {Type::getInt32Ty(M.getContext())},
false);
4696 auto ExitF = M.getOrInsertFunction(
"exit", FT2);
4697 Builder2.CreateCall(ExitF,
4698 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4704 raw_string_ostream ss(
str);
4705 ss << message <<
"\n";
4706 ss <<
"fn: " << *inst.getParent()->getParent() <<
"\n";
4709 EmitFailure(
"CannotDeduceType", inst.getDebugLoc(), &inst, ss.str());
4717 size_t end = start + length;
4720 llvm::SmallVector<llvm::Metadata *, 8> out;
4721 auto I64 = llvm::Type::getInt64Ty(md->getContext());
4722 for (
size_t i = 0; i < md->getNumOperands(); i += 2) {
4724 llvm::cast<llvm::ConstantInt>(
4725 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 1))
4730 size_t next = SIZE_MAX;
4731 if (i + 3 < md->getNumOperands())
4732 next = llvm::cast<llvm::ConstantInt>(
4733 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 3))
4738 size_t newoff = offset <= start ? 0 : offset - start;
4739 out.push_back(md->getOperand(i));
4741 llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(I64, newoff)));
4745 return llvm::MDNode::get(md->getContext(), out);
4748std::vector<std::tuple<llvm::Type *, size_t, size_t>>
4750 std::vector<std::pair<ConcreteType, size_t>> parsed;
4751 for (
size_t i = 0; i < md->getNumOperands(); i += 2) {
4753 llvm::cast<llvm::MDString>(md->getOperand(i))->getString(),
4755 auto size = llvm::cast<llvm::ConstantInt>(
4756 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 1))
4759 parsed.emplace_back(base, size);
4762 std::vector<std::tuple<llvm::Type *, size_t, size_t>> toIterate;
4764 while (idx < parsed.size()) {
4766 auto dt = parsed[idx].first;
4767 size_t start = parsed[idx].second;
4768 size_t end = 0x0fffffff;
4769 for (idx = idx + 1; idx < parsed.size(); ++idx) {
4772 auto next = parsed[idx].first;
4773 tmp.checkedOrIn(next,
true, Legal);
4790 if ((parsed[idx].first.isFloat() ==
nullptr) ==
4791 (parsed[idx - 1].first.isFloat() ==
nullptr)) {
4799 end = parsed[idx].second;
4805 assert(dt.isKnown());
4806 toIterate.emplace_back(dt.isFloat(), start, end - start);
4811void dumpModule(llvm::Module *mod) { llvm::errs() << *mod <<
"\n"; }
4813void dumpValue(llvm::Value *val) { llvm::errs() << *val <<
"\n"; }
4815void dumpBlock(llvm::BasicBlock *blk) { llvm::errs() << *blk <<
"\n"; }
4817void dumpType(llvm::Type *ty) { llvm::errs() << *ty <<
"\n"; }
4822 auto II = dyn_cast<IntrinsicInst>(V);
4825 switch (II->getIntrinsicID()) {
4826 case Intrinsic::nvvm_ldu_global_i:
4827 case Intrinsic::nvvm_ldu_global_p:
4828 case Intrinsic::nvvm_ldu_global_f:
4829#if LLVM_VERSION_MAJOR < 20
4830 case Intrinsic::nvvm_ldg_global_i:
4831 case Intrinsic::nvvm_ldg_global_p:
4832 case Intrinsic::nvvm_ldg_global_f:
4842 size_t checkLoadCaptures, Instruction *startinst,
4843 llvm::TargetLibraryInfo *TLI) {
4845 Instruction *VI = startinst;
4847 VI = dyn_cast<Instruction>(V);
4849 VI = VI->getNextNode();
4851 VI = &*inst->getParent()->getParent()->getEntryBlock().begin();
4852 SmallPtrSet<BasicBlock *, 1> regionBetween;
4854 SmallVector<BasicBlock *, 1> todo;
4855 todo.push_back(VI->getParent());
4856 while (todo.size()) {
4857 auto cur = todo.pop_back_val();
4858 if (regionBetween.count(cur))
4860 regionBetween.insert(cur);
4861 if (cur == inst->getParent())
4863 for (
auto BB : successors(cur))
4867 SmallVector<std::tuple<Instruction *, size_t, Value *>, 1> todo;
4868 for (
auto U : V->users()) {
4869 if (
auto I = dyn_cast<Instruction>(U)) {
4870 todo.emplace_back(cast<Instruction>(I), checkLoadCaptures, V);
4873 std::set<std::tuple<Value *, size_t, Value *>> seen;
4874 while (todo.size()) {
4875 auto pair = todo.pop_back_val();
4876 if (seen.count(pair))
4879 auto UI = std::get<0>(pair);
4880 auto level = std::get<1>(pair);
4881 auto prev = std::get<2>(pair);
4883 if (!regionBetween.count(UI->getParent()))
4885 if (UI->getParent() == VI->getParent()) {
4886 if (UI->comesBefore(VI))
4889 if (UI->getParent() == inst->getParent())
4890 if (inst->comesBefore(UI))
4896 for (
auto U2 : UI->users()) {
4897 auto UI2 = cast<Instruction>(U2);
4898 todo.emplace_back(UI2, level, UI);
4903 if (isa<MemSetInst>(UI))
4906 if (isa<MemTransferInst>(UI)) {
4909 if (UI->getOperand(1) != prev)
4913 if (
auto CI = dyn_cast<CallBase>(UI)) {
4917#if LLVM_VERSION_MAJOR >= 14
4918 for (
size_t i = 0, size = CI->arg_size(); i < size; i++)
4920 for (
size_t i = 0, size = CI->getNumArgOperands(); i < size; i++)
4923 if (prev == CI->getArgOperand(i)) {
4933 if (isa<CmpInst>(UI)) {
4936 if (isa<LoadInst>(UI)) {
4938 for (
auto U2 : UI->users()) {
4939 auto UI2 = cast<Instruction>(U2);
4940 todo.emplace_back(UI2, level - 1, UI);
4946 if (
auto SI = dyn_cast<StoreInst>(UI)) {
4947 if (SI->getValueOperand() != prev) {
4963#if LLVM_VERSION_MAJOR >= 16
4969 llvm::DominatorTree &DT, llvm::LoopInfo &LI,
4970 llvm::Value *op0, llvm::Value *op1,
4971 bool offsetAllowed) {
4978 if (
auto i1 = dyn_cast<Instruction>(op1))
4979 if (isa<ConstantPointerNull>(op0) &&
4983 if (
auto i0 = dyn_cast<Instruction>(op0))
4984 if (isa<ConstantPointerNull>(op1) &&
4989 if (!lhs->getType()->isPointerTy() && !rhs->getType()->isPointerTy())
4995 bool noalias[2] = {noalias_lhs, noalias_rhs};
4997 for (
int i = 0; i < 2; i++) {
4999 Value *start = (i == 0) ? lhs : rhs;
5001 Value *end = (i == 0) ? rhs : lhs;
5003 if (noalias[start_i]) {
5004 if (noalias[end_i]) {
5007 if (isa<Argument>(end)) {
5011 if (
auto starti = dyn_cast<Instruction>(start)) {
5014 if (DT.dominates(end, starti)) {
5019 if (
auto endi = dyn_cast<Instruction>(end)) {
5021 if (
auto ld = dyn_cast<LoadInst>(endi)) {
5032 if (
auto ld = dyn_cast<LoadInst>(start)) {
5036 auto alloc_call = cast<Instruction>(base);
5051 if (end_base != alloc_call && noalias[end_i] &&
5060 bool alloc_written =
false;
5062 if (!I->mayWriteToMemory())
5068 alloc_written =
true;
5074 if (!alloc_written && end_base != alloc_call) {
5079 if (noalias[end_i] || DT.dominates(end_base, alloc_call)) {
5088 SmallVector<Value *, 8> worklist;
5089 SmallPtrSet<Value *, 8> visited;
5090 SmallVector<LoadInst *, 8> alloc_loads;
5092 worklist.push_back(alloc_call);
5093 visited.insert(alloc_call);
5095 while (!worklist.empty()) {
5096 Value *V = worklist.pop_back_val();
5097 for (User *U : V->users()) {
5098 if (!visited.insert(U).second)
5102 worklist.push_back(U);
5103 }
else if (
auto LI = dyn_cast<LoadInst>(U)) {
5104 alloc_loads.push_back(LI);
5109 bool all_loads_no_alias =
true;
5110 for (LoadInst *alloc_load : alloc_loads) {
5111 if (alloc_load == end) {
5112 all_loads_no_alias =
false;
5115 if (
auto end_inst = dyn_cast<Instruction>(end)) {
5116 if (DT.dominates(end_inst, alloc_load)) {
5121 all_loads_no_alias =
false;
5126 if (all_loads_no_alias) {
5139 ArrayRef<unsigned> path) {
5140 SmallVector<Value *, 2> vals;
5141 vals.push_back(ConstantInt::get(B.getInt64Ty(), 0));
5142 for (
auto v : path) {
5143 vals.push_back(ConstantInt::get(B.getInt32Ty(), v));
5145 return B.CreateInBoundsGEP(type, value, vals);
5149 llvm::Value *sret, llvm::Type *root_ty,
5150 llvm::Value *rootRet,
size_t rootOffset,
5152 std::deque<std::pair<llvm::Type *, std::vector<unsigned>>> todo = {
5154 SmallVector<Value *> extracted;
5156 auto rootOffset0 = rootOffset;
5157 while (!todo.empty()) {
5158 auto cur = std::move(todo[0]);
5160 auto path = std::move(cur.second);
5161 auto ty = cur.first;
5163 if (
auto PT = dyn_cast<PointerType>(ty)) {
5167 Value *loc =
nullptr;
5168 switch (direction) {
5176 llvm_unreachable(
"Unhandled");
5178 switch (direction) {
5181 outloc = B.CreateLoad(ty, outloc);
5182 B.CreateStore(outloc, loc);
5187 outloc = B.CreatePointerCast(
5190 B.CreateStore(outloc, loc);
5194 loc = B.CreateLoad(ty, loc);
5195 val = B.CreateInsertValue(val, loc, path);
5200 *B.GetInsertBlock()->getParent()->getParent(), ty,
false);
5201 val = B.CreateInsertValue(val, loc, path);
5206 loc = B.CreateLoad(ty, loc);
5207 extracted.push_back(loc);
5208 B.CreateStore(loc, outloc);
5212 llvm_unreachable(
"Unhandled");
5220 if (
auto AT = dyn_cast<ArrayType>(ty)) {
5221 for (
size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5222 std::vector<unsigned> path2(path);
5223 path2.push_back(E - 1 - i);
5224 todo.emplace_front(AT->getElementType(), path2);
5229 if (
auto VT = dyn_cast<VectorType>(ty)) {
5230 for (
size_t i = 0, E = VT->getElementCount().getKnownMinValue(); i < E;
5232 std::vector<unsigned> path2(path);
5233 path2.push_back(E - 1 - i);
5234 todo.emplace_front(VT->getElementType(), path2);
5239 if (
auto ST = dyn_cast<StructType>(ty)) {
5240 for (
size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5241 std::vector<unsigned> path2(path);
5242 path2.push_back(E - 1 - i);
5243 todo.emplace_front(ST->getTypeAtIndex(E - 1 - i), path2);
5251 auto PT = cast<PointerType>(obj->getType());
5252 assert(PT->getAddressSpace() == 0 || PT->getAddressSpace() == 10);
5253 if (PT->getAddressSpace() == 10 && extracted.size()) {
5254 extracted.insert(extracted.begin(), obj);
5255 auto JLT =
getPointerType(StructType::get(PT->getContext(), {}), 10);
5256 auto FT = FunctionType::get(JLT, {},
true);
5258 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
5259 "julia.write_barrier", FT);
5260 assert(obj->getType() == JLT);
5261 B.CreateCall(wb, extracted);
5266 assert(rootOffset - rootOffset0 == tracked.
count);
5272 llvm::Type *dstType, llvm::Value *dst,
5273 llvm::ArrayRef<unsigned> dstPrefix0,
5274 llvm::Type *srcType, llvm::Value *src,
5275 llvm::ArrayRef<unsigned> srcPrefix0,
bool shouldZero) {
5277 std::tuple<llvm::Type *, std::vector<unsigned>, std::vector<unsigned>>>
5279 std::vector<unsigned>(dstPrefix0.begin(), dstPrefix0.end()),
5280 std::vector<unsigned>(srcPrefix0.begin(), srcPrefix0.end())}};
5282 auto &M = *B.GetInsertBlock()->getParent()->getParent();
5284 size_t numRootsSeen = 0;
5286 while (!todo.empty()) {
5287 auto cur = std::move(todo[0]);
5288 auto &&[ty, dstPrefix, srcPrefix] = cur;
5291 if (
auto PT = dyn_cast<PointerType>(ty)) {
5292 if (PT->getAddressSpace() == 10) {
5296 if (dstPrefix.size() > 0)
5305 if (
auto AT = dyn_cast<ArrayType>(ty)) {
5306 for (
size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5307 std::vector<unsigned> nextDst(dstPrefix);
5308 std::vector<unsigned> nextSrc(srcPrefix);
5309 nextDst.push_back(E - 1 - i);
5310 nextSrc.push_back(E - 1 - i);
5311 todo.emplace_front(AT->getElementType(), std::move(nextDst),
5312 std::move(nextSrc));
5317 if (
auto ST = dyn_cast<StructType>(ty)) {
5318 for (
size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5319 std::vector<unsigned> nextDst(dstPrefix);
5320 std::vector<unsigned> nextSrc(srcPrefix);
5321 nextDst.push_back(E - 1 - i);
5322 nextSrc.push_back(E - 1 - i);
5323 todo.emplace_front(ST->getElementType(E - 1 - i), std::move(nextDst),
5324 std::move(nextSrc));
5330 if (dstPrefix.size() > 0)
5334 if (srcPrefix.size() > 0)
5337 auto ld = B.CreateLoad(ty, in);
5338 B.CreateStore(ld, out);
5342 assert(numRootsSeen == tracked.
count);
5348 llvm::IRBuilder<> &B) {
5349 std::deque<Value *> todo = {v};
5350 SmallVector<Value *, 1> done;
5351 while (todo.size()) {
5352 auto cur = todo.front();
5354 auto T = cur->getType();
5359 done.push_back(cur);
5362 if (
auto ST = dyn_cast<StructType>(T)) {
5363 for (
size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5364 auto T2 = ST->getElementType(E - 1 - i);
5366 auto V2 = B.CreateExtractValue(cur, E - 1 - i);
5367 todo.push_front(V2);
5372 if (
auto AT = dyn_cast<ArrayType>(T)) {
5373 for (
size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5374 todo.push_front(B.CreateExtractValue(cur, E - 1 - i));
5378 if (
auto VT = dyn_cast<VectorType>(T)) {
5379 assert(!VT->getElementCount().isScalable());
5380 size_t numElems = VT->getElementCount().getKnownMinValue();
5381 for (
size_t i = 0; i < numElems; i++) {
5382 todo.push_front(B.CreateExtractElement(cur, numElems - 1 - i));
5386 llvm_unreachable(
"unknown source of julia type");
static bool contains(ArrayRef< int > ar, int v)
static bool isDeallocationFunction(const llvm::StringRef name, const llvm::TargetLibraryInfo &TLI)
Return whether a given function is a known C/C++ memory deallocation function For updating below one ...
static bool isDeallocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static bool isAllocationFunction(const llvm::StringRef name, const llvm::TargetLibraryInfo &TLI)
Return whether a given function is a known C/C++ memory allocation function For updating below one sh...
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static Operation * getFunctionFromCall(CallOpInterface iface)
static std::string str(AugmentedStruct c)
static TypeTree parseTBAA(TBAAStructTypeNode AccessType, llvm::Instruction &I, const llvm::DataLayout &DL, std::shared_ptr< llvm::ModuleSlotTracker > MST)
Given a TBAA access node return the corresponding TypeTree This includes recursively parsing the acce...
static bool isMemFreeLibMFunction(llvm::StringRef str, llvm::Intrinsic::ID *ID=nullptr)
llvm::Value * get_cached_mat_width(llvm::IRBuilder<> &B, llvm::ArrayRef< llvm::Value * > trans, llvm::Value *arg_ld, llvm::Value *dim1, llvm::Value *dim2, bool cacheMat, bool byRef, bool cublas)
void addValueToCache(llvm::Value *arg, bool cache_arg, llvm::Type *ty, llvm::SmallVectorImpl< llvm::Value * > &cacheValues, llvm::IRBuilder<> &BuilderZ, const Twine &name)
llvm::Value * load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType, llvm::Value *V, bool byRef)
CallInst * CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree)
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, bool ZeroMem)
AllocaInst * getBaseAndOffset(Value *ptr, size_t &offset)
bool isFlangRuntimeNoFree(llvm::StringRef name)
LLVM flang runtime functions that neither free memory of the program nor return or store an allocatio...
Value * lookup_with_layout(IRBuilder<> &B, Type *fpType, Value *layout, Value *const base, Value *lda, Value *row, Value *col)
llvm::cl::opt< bool > EnzymeZeroCache
llvm::Value * to_blas_fp_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef, Type *fpTy, IRBuilder<> &entryBuilder, llvm::Twine const &name)
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
llvm::SmallVector< llvm::Instruction *, 2 > PostCacheStore(llvm::StoreInst *SI, llvm::IRBuilder<> &B)
Function * getFirstFunctionDefinition(Module &M)
llvm::cl::opt< bool > EnzymeFastMath("enzyme-fast-math", cl::init(true), cl::Hidden, cl::desc("Use fast math on derivative compuation"))
llvm::Function * getOrInsertDifferentialMPI_Wait(llvm::Module &M, ArrayRef< llvm::Type * > T, Type *reqType, StringRef caller)
llvm::PointerType * getDefaultAnonymousTapeType(llvm::LLVMContext &C)
static bool matchBLASName(StringRef in, ArrayRef< const char * > prefixes, ArrayRef< const char * > floatTypes, ArrayRef< const char * > stems, ArrayRef< const char * > suffixes, const char *&prefixOut, const char *&floatTypeOut, const char *&stemOut, const char *&suffixOut)
Decompose in as prefix + floatType + stem + suffix using the supplied tables, setting the out-paramet...
llvm::Function * getOrInsertDifferentialWaitallSave(llvm::Module &M, ArrayRef< llvm::Type * > T, PointerType *reqType)
Value * simplifyLoad(Value *V, size_t valSz, size_t preOffset)
bool attributeKnownFunctions(llvm::Function &F)
llvm::CallInst * createIntrinsicCall(llvm::IRBuilderBase &B, llvm::Intrinsic::ID ID, llvm::Type *RetTy, llvm::ArrayRef< llvm::Value * > Args, llvm::Instruction *FMFSource, const llvm::Twine &Name)
llvm::Value * getOrInsertOpFloatSum(llvm::Module &M, llvm::Function *templateFn, llvm::Type *OpPtr, llvm::Type *OpType, ConcreteType CT, llvm::Type *intType, IRBuilder<> &B2)
void mayExecuteAfter(llvm::SmallVectorImpl< llvm::Instruction * > &results, llvm::Instruction *inst, const llvm::SetVector< Instruction * > &stores, const llvm::Loop *region)
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.
LLVMValueRef(* EnzymeUndefinedValueForType)(LLVMModuleRef, LLVMTypeRef, uint8_t)
bool notCapturedBefore(llvm::Value *V, Instruction *inst, size_t checkLoadCaptures, Instruction *startinst, llvm::TargetLibraryInfo *TLI)
llvm::Value * nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V)
Create function to computer nearest power of two.
Function * getOrInsertMemcpyMat(Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT, unsigned dstalign, unsigned srcalign)
llvm::Value * transpose(std::string floatType, IRBuilder<> &B, llvm::Value *V, bool cublas)
void EmitNoTypeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2)
Value * is_lower(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas)
LLVMValueRef(* CustomDeallocator)(LLVMBuilderRef, LLVMValueRef)
llvm::cl::opt< bool > EnzymeCheckDerivativeNaN("enzyme-check-nan", cl::init(false), cl::Hidden, cl::desc("Add NaN checks to all derivative intermediate values"))
llvm::Optional< BlasInfo > extractBLAS(llvm::StringRef in)
Value * CreateAllocation(IRBuilder<> &Builder, llvm::Type *T, Value *Count, const Twine &Name, CallInst **caller, Instruction **ZeroMem, bool isDefault)
FunctionCallee getOrInsertPerCallingConv(Module &M, Function *templateFn, StringRef callee, FunctionType *FT)
LLVMValueRef(* CustomAllocator)(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef, LLVMValueRef, uint8_t, LLVMValueRef *)
void dumpBlock(llvm::BasicBlock *blk)
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Function * getOrInsertExponentialAllocator(Module &M, Function *newFunc, bool ZeroInit, llvm::Type *RT)
Value * GetFunctionValFromValue(Value *fn)
bool overwritesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter, llvm::Loop *scope)
bool isNVLoad(const llvm::Value *V)
llvm::Value * is_left(IRBuilder<> &B, llvm::Value *side, bool byRef, bool cublas)
llvm::Value * EmitNoDerivativeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2, llvm::Value *condition)
Constant * getString(Module &M, StringRef Str)
llvm::cl::opt< bool > EnzymeMemmoveWarning("enzyme-memmove-warning", cl::init(true), cl::Hidden, cl::desc("Warn if using memmove implementation as a fallback for memmove"))
void dumpTypeResults(TypeResults &TR)
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.
Function * getOrInsertDifferentialFloatMemcpyMat(Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT, IntegerType *CT, unsigned dstalign, unsigned srcalign, bool zeroSrc)
SmallVector< std::pair< Value *, size_t >, 1 > getAllLoadedValuesFrom(AllocaInst *ptr0, size_t offset, size_t valSz, bool &legal)
void dumpType(llvm::Type *ty)
void callMemcpyStridedBlas(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::ArrayRef< llvm::Value * > args, llvm::Type *copy_retty, llvm::ArrayRef< llvm::OperandBundleDef > bundles)
Create function for type that performs memcpy with a stride using blas copy.
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,...
void(* CustomRuntimeInactiveError)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef)
LLVMValueRef *(* EnzymePostCacheStore)(LLVMValueRef, LLVMBuilderRef, uint64_t *size)
llvm::Value * moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype, llvm::Value *sret, llvm::Type *root_ty, llvm::Value *rootRet, size_t rootOffset, SRetRootMovement direction)
void copy_lower_to_upper(llvm::IRBuilder<> &B, llvm::Type *fpType, BlasInfo blas, bool byRef, llvm::Value *layout, llvm::Value *islower, llvm::Value *A, llvm::Value *lda, llvm::Value *N)
void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal, llvm::Value *shadow, const char *Message, llvm::DebugLoc &&loc, llvm::Instruction *orig)
SmallVector< llvm::Value *, 1 > get_blas_row(llvm::IRBuilder<> &B, ArrayRef< llvm::Value * > transA, bool byRef, bool cublas)
bool isAtomic(Value *origptr, bool AtomicAdd, Function *newFunc)
Value * is_nonunit(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas)
Function * GetFunctionFromValue(Value *fn)
llvm::SmallVector< llvm::Value *, 1 > getJuliaObjects(llvm::Value *v, llvm::IRBuilder<> &B)
void callSPMVDiagUpdate(IRBuilder<> &B, Module &M, BlasInfo blas, IntegerType *IT, Type *BlasCT, Type *BlasFPT, Type *BlasPT, Type *BlasIT, Type *fpTy, ArrayRef< Value * > args, ArrayRef< OperandBundleDef > bundles, bool byRef, bool julia_decl)
bool overwritesToMemoryReadByLoop(llvm::ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT, llvm::Instruction *maybeReader, const llvm::SCEV *LoadStart, const llvm::SCEV *LoadEnd, llvm::Instruction *maybeWriter, const llvm::SCEV *StoreStart, const llvm::SCEV *StoreEnd, llvm::Loop *scope)
void dumpValue(llvm::Value *val)
LLVMTypeRef(* EnzymeDefaultTapeType)(LLVMContextRef)
Function * getOrInsertDifferentialFloatMemmove(Module &M, Type *T, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
void emit_backtrace(llvm::Instruction *inst, llvm::raw_ostream &ss)
llvm::Value * to_blas_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef, bool cublas, IntegerType *julia_decl, IRBuilder<> &entryBuilder, llvm::Twine const &name)
LLVMValueRef(* EnzymeSanitizeDerivatives)(LLVMValueRef, LLVMValueRef toset, LLVMBuilderRef, LLVMValueRef)
Function * getOrInsertDifferentialFloatMemcpy(Module &M, Type *elementType, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
Create function for type that is equivalent to memcpy but adds to destination rather than a direct co...
llvm::cl::opt< bool > EnzymeRuntimeError("enzyme-runtime-error", cl::init(false), cl::Hidden, cl::desc("Emit Runtime errors instead of compile time ones"))
llvm::CallInst * getorInsertInnerProd(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, IntegerType *IT, Type *BlasPT, Type *BlasIT, Type *fpTy, llvm::ArrayRef< llvm::Value * > args, const llvm::ArrayRef< llvm::OperandBundleDef > bundles, bool byRef, bool cublas, bool julia_decl)
const SCEV * evaluateAtIterationWithoutExt(const SCEVAddRecExpr *AR, const SCEV *It, ScalarEvolution &SE)
SmallVector< std::tuple< Instruction *, Value *, size_t >, 1 > findAllUsersOf(Value *AI)
bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI)
Check if value if b captured.
Function * getOrInsertMemcpyStrided(Module &M, Type *elementType, PointerType *T, Type *IT, unsigned dstalign, unsigned srcalign)
static Value * constantInBoundsGEPHelper(llvm::IRBuilder<> &B, llvm::Type *type, llvm::Value *value, ArrayRef< unsigned > path)
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
void copyNonJLValueInto(llvm::IRBuilder<> &B, llvm::Type *curType, llvm::Type *dstType, llvm::Value *dst, llvm::ArrayRef< unsigned > dstPrefix0, llvm::Type *srcType, llvm::Value *src, llvm::ArrayRef< unsigned > srcPrefix0, bool shouldZero)
llvm::Optional< bool > arePointersGuaranteedNoAlias(TargetLibraryInfo &TLI, llvm::AAResults &AA, llvm::DominatorTree &DT, llvm::LoopInfo &LI, llvm::Value *op0, llvm::Value *op1, bool offsetAllowed)
static std::string tofltstr(Type *T)
Convert a floating type to a string.
void dumpModule(llvm::Module *mod)
void(* CustomZero)(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef, uint8_t)
llvm::Constant * getUndefinedValueForType(llvm::Module &M, llvm::Type *T, bool forceZero)
Function * getOrInsertCheckedFree(Module &M, CallInst *call, Type *Ty, unsigned width)
std::vector< std::tuple< llvm::Type *, size_t, size_t > > parseTrueType(const llvm::MDNode *md, DerivativeMode Mode, bool const_src)
llvm::Value * is_normal(IRBuilder<> &B, llvm::Value *trans, bool byRef, bool cublas)
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask)
bool collectOffset(GEPOperator *gep, const DataLayout &DL, unsigned BitWidth, MapVector< Value *, APInt > &VariableOffsets, APInt &ConstantOffset)
static llvm::StringRef getFuncName(llvm::Function *called)
static llvm::StringRef canonicalizeMPIName(llvm::StringRef Name)
Canonicalize the name of an MPI routine across calling conventions: both the C convention ("MPI_Recv"...
static std::string getRenamedPerCallingConv(llvm::StringRef caller, llvm::StringRef callee)
llvm::cl::opt< bool > EnzymeBlasCopy
static llvm::Loop * getAncestor(llvm::Loop *R1, llvm::Loop *R2)
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
static bool anyJuliaObjects(llvm::Type *T)
@ Args
Return is a struct of all args.
static bool isNoAlias(const llvm::CallBase *call)
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
static llvm::PointerType * getUnqual(llvm::Type *T)
static bool isCertainPrint(const llvm::StringRef name)
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
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={})
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
static bool isDebugFunction(llvm::Function *called)
static bool isPointerArithmeticInst(const llvm::Value *V, bool includephi=true, bool includebin=true)
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
static bool isReadOnlyOrThrow(const llvm::Function *F)
static bool isSpecialPtr(llvm::Type *Ty)
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 ...
static std::tuple< llvm::StringRef, llvm::StringRef, llvm::StringRef > tripleSplitDollar(llvm::StringRef caller)
@ RootPointerToSRetPointer
@ SRetPointerToRootPointer
static llvm::Value * CreateSelect(llvm::IRBuilder<> &Builder2, llvm::Value *cmp, llvm::Value *tval, llvm::Value *fval, const llvm::Twine &Name="")
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
llvm::cl::opt< bool > EnzymeLapackCopy
llvm::cl::opt< bool > EnzymeNonPower2Cache
Concrete SubType of a given value.
bool isKnown() const
Whether this ConcreteType has information (is not unknown)
std::string str() const
Convert the ConcreteType to a string.
llvm::Type * isFloat() const
Return the floating point type, if this is a float.
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)
static llvm::Value * extractMeta(llvm::IRBuilder<> &Builder, llvm::Value *Agg, unsigned off, const llvm::Twine &name="")
Helper routine to extract a nested element from a struct/array. This is.
A holder class representing the results of running TypeAnalysis on a given function.
void dump(llvm::raw_ostream &ss=llvm::errs()) const
Prints all known information.
const TypeTree & query(llvm::Value *val) const
The TypeTree of a particular Value.
TypeTree Lookup(size_t len, const llvm::DataLayout &dl) const
Select all submappings whose first index is in range [0, len) and remove the first index.
llvm::Type * fpType(llvm::LLVMContext &ctx, bool to_scalar=false) const
llvm::IntegerType * intType(llvm::LLVMContext &ctx) const
CountTrackedPointers(llvm::Type *T)