26#include <llvm/Config/llvm-config.h>
29#if LLVM_VERSION_MAJOR >= 16
31#include "llvm/Analysis/ScalarEvolution.h"
32#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
35#include "SCEV/ScalarEvolution.h"
36#include "SCEV/ScalarEvolutionExpander.h"
39#include "llvm/ADT/ArrayRef.h"
40#include "llvm/ADT/MapVector.h"
42#if LLVM_VERSION_MAJOR <= 16
43#include "llvm/ADT/Optional.h"
45#include "llvm/ADT/SetVector.h"
46#include "llvm/ADT/SmallSet.h"
47#include "llvm/ADT/SmallVector.h"
49#include "llvm/Passes/PassBuilder.h"
51#include "llvm/IR/BasicBlock.h"
52#include "llvm/IR/Constants.h"
53#include "llvm/IR/Function.h"
54#include "llvm/IR/IRBuilder.h"
55#include "llvm/IR/InstrTypes.h"
56#include "llvm/IR/Instructions.h"
57#include "llvm/IR/MDBuilder.h"
58#include "llvm/IR/Metadata.h"
60#include "llvm/Analysis/ScalarEvolution.h"
61#include "llvm/Support/Debug.h"
62#include "llvm/Support/ErrorHandling.h"
63#include "llvm/Transforms/Scalar.h"
65#include "llvm/Analysis/BasicAliasAnalysis.h"
66#include "llvm/Analysis/GlobalsModRef.h"
67#include "llvm/Analysis/InlineAdvisor.h"
68#include "llvm/Analysis/InlineCost.h"
69#include "llvm/Analysis/ScalarEvolution.h"
70#include "llvm/Analysis/TargetLibraryInfo.h"
71#include "llvm/IR/AbstractCallSite.h"
72#include "llvm/Support/CommandLine.h"
73#include "llvm/Transforms/Utils/BasicBlockUtils.h"
74#include "llvm/Transforms/Utils/Cloning.h"
76#include "ActivityAnalysis.h"
79#include "EnzymeLogic.h"
80#include "GradientUtils.h"
88#include "llvm/Transforms/Utils.h"
90#include "llvm/Transforms/IPO/Attributor.h"
91#include "llvm/Transforms/IPO/OpenMPOpt.h"
92#include "llvm/Transforms/Utils/Mem2Reg.h"
99#define DEBUG_TYPE "lower-enzyme-intrinsic"
101llvm::cl::opt<bool>
EnzymeEnable(
"enzyme-enable", cl::init(
true), cl::Hidden,
102 cl::desc(
"Run the Enzyme pass"));
105 "enzyme-lto-prelink", cl::init(
false), cl::Hidden,
106 cl::desc(
"Run Enzyme in an LTO pre-link pipeline, rather than deferring "
107 "differentiation to the post-link run"));
111 cl::desc(
"Run enzymepostprocessing optimizations"));
115 cl::desc(
"Run attributor post Enzyme"));
117llvm::cl::opt<bool>
EnzymeOMPOpt(
"enzyme-omp-opt", cl::init(
false), cl::Hidden,
118 cl::desc(
"Whether to enable openmp opt"));
121 "enzyme-detect-readthrow", cl::init(
true), cl::Hidden,
122 cl::desc(
"Run preprocessing detect readonly or throw optimization"));
125 "enzyme-truncate-all", cl::init(
""), cl::Hidden,
127 "Truncate all floating point operations. "
128 "E.g. \"64to32\" or \"64to<exponent_width>-<significand_width>\"."));
130#define addAttribute addAttributeAtIndex
131#define getAttribute getAttributeAtIndex
135castToDiffeFunctionArgType(IRBuilder<> &Builder, llvm::CallInst *CI,
136 llvm::FunctionType *FT, llvm::Type *destType,
138 llvm::Value *value,
unsigned int truei) {
140 if (
auto ptr = dyn_cast<PointerType>(res->getType())) {
141 if (
auto PT = dyn_cast<PointerType>(destType)) {
142 if (ptr->getAddressSpace() != PT->getAddressSpace()) {
143#if LLVM_VERSION_MAJOR < 17
144 if (CI->getContext().supportsTypedPointers()) {
145 res = Builder.CreateAddrSpaceCast(
147 PT->getAddressSpace()));
149 res = Builder.CreateAddrSpaceCast(res, PT);
152 res = Builder.CreateAddrSpaceCast(res, PT);
157 llvm::errs() <<
"Warning cast(2) __enzyme_autodiff argument " << i
158 <<
" " << *res <<
"|" << *res->getType() <<
" to argument "
159 << truei <<
" " << *destType <<
"\n"
160 <<
"orig: " << *FT <<
"\n";
166 if (!res->getType()->canLosslesslyBitCastTo(destType)) {
168 assert(value->getType());
171 auto loc = CI->getDebugLoc();
172 if (
auto arg = dyn_cast<Instruction>(res)) {
173 loc = arg->getDebugLoc();
176 "Cannot cast __enzyme_autodiff shadow argument ", i,
", found ",
177 *res,
", type ", *res->getType(),
" - to arg ", truei,
" ",
181 return Builder.CreateBitCast(value, destType);
184#if LLVM_VERSION_MAJOR > 16
185static std::optional<StringRef> getMetadataName(llvm::Value *res);
187static Optional<StringRef> getMetadataName(llvm::Value *res);
191#if LLVM_VERSION_MAJOR > 16
192static std::optional<StringRef> recursePhiReads(PHINode *val)
194static Optional<StringRef> recursePhiReads(PHINode *val)
197#if LLVM_VERSION_MAJOR > 16
198 std::optional<StringRef> finalMetadata;
200 Optional<StringRef> finalMetadata;
202 SmallVector<PHINode *, 1> todo = {val};
203 SmallSet<PHINode *, 1> done;
204 while (todo.size()) {
205 auto phiInst = todo.back();
207 if (done.count(phiInst))
209 done.insert(phiInst);
210 for (
unsigned j = 0; j < phiInst->getNumIncomingValues(); ++j) {
211 auto newVal = phiInst->getIncomingValue(j);
212 if (
auto phi = dyn_cast<PHINode>(newVal)) {
215 auto metaString = getMetadataName(newVal);
217 if (!finalMetadata) {
218 finalMetadata = metaString;
219 }
else if (finalMetadata != metaString) {
226 return finalMetadata;
229#if LLVM_VERSION_MAJOR > 16
230std::optional<StringRef> getMetadataName(llvm::Value *res)
232Optional<StringRef> getMetadataName(llvm::Value *res)
236 return getMetadataName(S);
238 if (
auto av = dyn_cast<MetadataAsValue>(res)) {
239 return cast<MDString>(av->getMetadata())->getString();
240 }
else if ((isa<LoadInst>(res) || isa<CastInst>(res)) &&
241 isa<GlobalVariable>(cast<Instruction>(res)->getOperand(0))) {
243 cast<GlobalVariable>(cast<Instruction>(res)->getOperand(0));
244 return gv->getName();
245 }
else if (isa<LoadInst>(res) &&
246 isa<ConstantExpr>(cast<LoadInst>(res)->getOperand(0)) &&
247 cast<ConstantExpr>(cast<LoadInst>(res)->getOperand(0))->isCast() &&
249 cast<ConstantExpr>(cast<LoadInst>(res)->getOperand(0))
251 auto gv = cast<GlobalVariable>(
252 cast<ConstantExpr>(cast<LoadInst>(res)->getOperand(0))->getOperand(0));
253 return gv->getName();
254 }
else if (
auto gv = dyn_cast<GlobalVariable>(res)) {
255 return gv->getName();
256 }
else if (isa<ConstantExpr>(res) && cast<ConstantExpr>(res)->isCast() &&
257 isa<GlobalVariable>(cast<ConstantExpr>(res)->getOperand(0))) {
258 auto gv = cast<GlobalVariable>(cast<ConstantExpr>(res)->getOperand(0));
259 return gv->getName();
260 }
else if (isa<CastInst>(res) && cast<CastInst>(res) &&
261 isa<AllocaInst>(cast<CastInst>(res)->getOperand(0))) {
262 auto gv = cast<AllocaInst>(cast<CastInst>(res)->getOperand(0));
263 return gv->getName();
264 }
else if (
auto gv = dyn_cast<AllocaInst>(res)) {
265 return gv->getName();
266 }
else if (isa<PHINode>(res)) {
267 return recursePhiReads(cast<PHINode>(res));
273static Value *adaptReturnedVector(Value *ret, Value *diffret,
274 IRBuilder<> &Builder,
unsigned width) {
275 Type *returnType = ret->getType();
277 if (StructType *sty = dyn_cast<StructType>(returnType)) {
278 Value *agg = ConstantAggregateZero::get(sty);
280 for (
unsigned int i = 0; i < width; i++) {
281 Value *elem = Builder.CreateExtractValue(diffret, {i});
282 if (
auto vty = dyn_cast<FixedVectorType>(elem->getType())) {
283 for (
unsigned j = 0; j < vty->getNumElements(); ++j) {
284 Value *vecelem = Builder.CreateExtractElement(elem, j);
285 agg = Builder.CreateInsertValue(agg, vecelem, {i * j});
288 agg = Builder.CreateInsertValue(agg, elem, {i});
296static bool ReplaceOriginalCall(IRBuilder<> &Builder, Value *ret,
297 Type *retElemType, Value *diffret,
299 Type *retType = ret->getType();
300 Type *diffretType = diffret->getType();
301 auto &DL = CI->getModule()->getDataLayout();
303 if (diffretType->isEmptyTy() || diffretType->isVoidTy() ||
304 retType->isEmptyTy() || retType->isVoidTy()) {
305 CI->replaceAllUsesWith(UndefValue::get(CI->getType()));
306 CI->eraseFromParent();
310 if (retType == diffretType) {
311 CI->replaceAllUsesWith(diffret);
312 CI->eraseFromParent();
316 if (
auto sretType = dyn_cast<StructType>(retType),
317 diffsretType = dyn_cast<StructType>(diffretType);
318 sretType && diffsretType && sretType->isLayoutIdentical(diffsretType)) {
319 Value *newStruct = UndefValue::get(sretType);
320 for (
unsigned int i = 0; i < sretType->getStructNumElements(); i++) {
321 Value *elem = Builder.CreateExtractValue(diffret, {i});
322 newStruct = Builder.CreateInsertValue(newStruct, elem, {i});
324 CI->replaceAllUsesWith(newStruct);
325 CI->eraseFromParent();
329 if (isa<PointerType>(retType)) {
330 retType = retElemType;
332 if (
auto sretType = dyn_cast<StructType>(retType),
333 diffsretType = dyn_cast<StructType>(diffretType);
334 sretType && diffsretType && sretType->isLayoutIdentical(diffsretType)) {
335 for (
unsigned int i = 0; i < sretType->getStructNumElements(); i++) {
336 Value *sgep = Builder.CreateStructGEP(retType, ret, i);
337 Builder.CreateStore(Builder.CreateExtractValue(diffret, {i}), sgep);
339 CI->eraseFromParent();
343 if (DL.getTypeSizeInBits(retType) >= DL.getTypeSizeInBits(diffretType)) {
345 diffret, Builder.CreatePointerCast(ret,
getUnqual(diffretType)));
346 CI->eraseFromParent();
352 DL.getTypeSizeInBits(retType) >= DL.getTypeSizeInBits(diffretType)) ||
355 DL.getTypeSizeInBits(retType) == DL.getTypeSizeInBits(diffretType))) {
356 IRBuilder<> EB(
getFirstNonPHI(&CI->getFunction()->getEntryBlock()));
357 auto AL = EB.CreateAlloca(retType);
358 Builder.CreateStore(diffret,
359 Builder.CreatePointerCast(AL,
getUnqual(diffretType)));
360 Value *cload = Builder.CreateLoad(retType, AL);
361 CI->replaceAllUsesWith(cload);
362 CI->eraseFromParent();
367 diffret->getType()->isAggregateType()) {
368 auto diffreti = Builder.CreateExtractValue(diffret, {0});
369 if (diffreti->getType() == retType) {
370 CI->replaceAllUsesWith(diffreti);
371 CI->eraseFromParent();
373 }
else if (diffretType == retType) {
374 CI->replaceAllUsesWith(diffret);
375 CI->eraseFromParent();
380 auto diffretsize = DL.getTypeSizeInBits(diffretType);
381 auto retsize = DL.getTypeSizeInBits(retType);
382 EmitFailure(
"IllegalReturnCast", CI->getDebugLoc(), CI,
383 "Cannot cast return type of gradient ", *diffretType, *diffret,
384 " of size ", diffretsize,
" bits ",
", to desired type ",
385 *retType,
" of size ", retsize,
" bits");
392 EnzymeBase(
bool PostOpt)
397 Function *parseFunctionParameter(CallInst *CI) {
398 Value *fn = CI->getArgOperand(0);
401 if (CI->hasStructRetAttr()) {
402 fn = CI->getArgOperand(1);
408 if (!fn || !isa<Function>(fn)) {
410 EmitFailure(
"NoFunctionToDifferentiate", CI->getDebugLoc(), CI,
411 "failed to find fn to differentiate", *CI,
" - found - ",
415 if (cast<Function>(fn)->empty()) {
416 EmitFailure(
"EmptyFunctionToDifferentiate", CI->getDebugLoc(), CI,
417 "failed to find fn to differentiate", *CI,
" - found - ",
422 return cast<Function>(fn);
425#if LLVM_VERSION_MAJOR > 16
426 static std::optional<unsigned> parseWidthParameter(CallInst *CI)
428 static Optional<unsigned> parseWidthParameter(CallInst *CI)
433 for (
auto [i, found] = std::tuple{0u,
false}; i < CI->arg_size(); ++i) {
434 Value *arg = CI->getArgOperand(i);
436 if (
auto MDName = getMetadataName(arg)) {
437 if (*MDName ==
"enzyme_width") {
439 EmitFailure(
"IllegalVectorWidth", CI->getDebugLoc(), CI,
440 "vector width declared more than once",
441 *CI->getArgOperand(i),
" in", *CI);
445 if (i + 1 >= CI->arg_size()) {
446 EmitFailure(
"MissingVectorWidth", CI->getDebugLoc(), CI,
447 "constant integer followong enzyme_width is missing",
448 *CI->getArgOperand(i),
" in", *CI);
452 Value *width_arg = CI->getArgOperand(i + 1);
453 if (
auto cint = dyn_cast<ConstantInt>(width_arg)) {
454 width = cint->getZExtValue();
457 EmitFailure(
"IllegalVectorWidth", CI->getDebugLoc(), CI,
458 "enzyme_width must be a constant integer",
459 *CI->getArgOperand(i),
" in", *CI);
464 EmitFailure(
"IllegalVectorWidth", CI->getDebugLoc(), CI,
465 "illegal enzyme vector argument width ",
466 *CI->getArgOperand(i),
" in", *CI);
478 Value *dynamic_interface;
482 Value *diffeLikelihood;
484 int allocatedTapeSize;
488 bool differentialReturn;
492 StringSet<> ActiveRandomVariables;
493 std::vector<bool> overwritten_args;
494 bool runtimeActivity;
496 bool subsequent_calls_may_write;
499#if LLVM_VERSION_MAJOR > 16
500 static std::optional<Options>
501 handleArguments(IRBuilder<> &Builder, CallInst *CI, Function *fn,
503 std::vector<DIFFE_TYPE> &constants,
504 SmallVectorImpl<Value *> &args, std::map<int, Type *> &byVal)
506 static Optional<Options>
507 handleArguments(IRBuilder<> &Builder, CallInst *CI, Function *fn,
509 std::vector<DIFFE_TYPE> &constants,
510 SmallVectorImpl<Value *> &args, std::map<int, Type *> &byVal)
513 FunctionType *FT = fn->getFunctionType();
515 Value *differet =
nullptr;
516 Value *tape =
nullptr;
517 Value *dynamic_interface =
nullptr;
518 Value *trace =
nullptr;
519 Value *observations =
nullptr;
520 Value *likelihood =
nullptr;
521 Value *diffeLikelihood =
nullptr;
523 int allocatedTapeSize = -1;
524 bool freeMemory =
true;
525 bool tapeIsPointer =
false;
526 bool diffeTrace =
false;
528 unsigned byRefSize = 0;
529 bool primalReturn =
false;
530 bool runtimeActivity =
false;
531 bool strongZero =
false;
532 bool subsequent_calls_may_write =
536 StringSet<> ActiveRandomVariables;
540 if (fn->hasParamAttribute(0, Attribute::StructRet)) {
542 Ty = fn->getParamAttribute(0, Attribute::StructRet).getValueAsType();
549 !fn->getReturnType()->isVoidTy() && !fn->getReturnType()->isEmptyTy();
551 bool sret = CI->hasStructRetAttr() ||
552 fn->hasParamAttribute(0, Attribute::StructRet);
554 std::vector<bool> overwritten_args(
555 fn->getFunctionType()->getNumParams(),
558 for (
unsigned i = 1 + sret; i < CI->arg_size(); ++i) {
559 Value *res = CI->getArgOperand(i);
560 auto metaString = getMetadataName(res);
562 if (metaString &&
startsWith(*metaString,
"enzyme_")) {
563 if (*metaString ==
"enzyme_const_return") {
566 }
else if (*metaString ==
"enzyme_active_return") {
569 }
else if (*metaString ==
"enzyme_dup_return") {
572 }
else if (*metaString ==
"enzyme_noret") {
575 }
else if (*metaString ==
"enzyme_primal_return") {
586 if (
auto parsedWidth = parseWidthParameter(CI)) {
587 width = *parsedWidth;
593 if (fn->hasParamAttribute(0, Attribute::StructRet)) {
596 const DataLayout &DL = CI->getParent()->getModule()->getDataLayout();
598 Ty = fn->getParamAttribute(0, Attribute::StructRet).getValueAsType();
600 CTy = CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
602 auto FnSize = (DL.getTypeSizeInBits(Ty) / 8);
603 auto CSize = CTy ? (DL.getTypeSizeInBits(CTy) / 8) : 0;
611 if (CSize < count * FnSize) {
613 "IllegalByRefSize", CI->getDebugLoc(), CI,
"Struct return type ",
614 *CTy,
" (", CSize,
" bytes), not large enough to store ", count,
615 " returns of type ", *Ty,
" (", FnSize,
" bytes), width=", width,
616 " primal requested=", primalReturn);
618 Value *primal =
nullptr;
620 Value *sretPt = CI->getArgOperand(0);
621 PointerType *pty = cast<PointerType>(sretPt->getType());
622 primal = Builder.CreatePointerCast(
625 AllocaInst *primalA =
new AllocaInst(Ty, DL.getAllocaAddrSpace(),
626 nullptr, DL.getPrefTypeAlign(Ty));
631 Value *shadow =
nullptr;
637 Value *sretPt = CI->getArgOperand(0);
638 PointerType *pty = cast<PointerType>(sretPt->getType());
639 auto shadowPtr = Builder.CreatePointerCast(
643 shadowPtr = Builder.CreateConstGEP1_64(Ty, shadowPtr, 1);
646 Value *acc = UndefValue::get(ArrayType::get(
648 for (
size_t i = 0; i < width; ++i) {
650 Builder.CreateConstGEP1_64(Ty, shadowPtr, i + primalReturn);
651 acc = Builder.CreateInsertValue(acc, elem, i);
662 shadow = CI->getArgOperand(1);
668 args.push_back(primal);
670 args.push_back(shadow);
673 constants.push_back(retType);
675 primalReturn =
false;
678 ssize_t interleaved = -1;
681 maxsize = CI->arg_size();
682 size_t num_args = maxsize;
683 for (
unsigned i = 1 + sret; i < maxsize; ++i) {
684 Value *res = CI->getArgOperand(i);
685 auto metaString = getMetadataName(res);
686 if (metaString &&
startsWith(*metaString,
"enzyme_")) {
687 if (*metaString ==
"enzyme_interleave") {
697 for (ssize_t i = 1 + sret; (size_t)i < maxsize; ++i) {
698 Value *res = CI->getArgOperand(i);
699 auto metaString = getMetadataName(res);
700#if LLVM_VERSION_MAJOR > 16
701 std::optional<Value *> batchOffset;
702 std::optional<DIFFE_TYPE> opt_ty;
704 Optional<Value *> batchOffset;
705 Optional<DIFFE_TYPE> opt_ty;
710 bool skipArg =
false;
713 while (metaString &&
startsWith(*metaString,
"enzyme_")) {
720 EmitFailure(
"IllegalDiffeType", CI->getDebugLoc(), CI,
721 "illegal enzyme metadata classification ", *CI,
726 if (marker->activity)
727 opt_ty = *marker->activity;
729 if (marker->extraOperands) {
730 if ((
size_t)i + marker->extraOperands >= CI->arg_size()) {
731 EmitFailure(
"EnzymeCallingError", CI->getDebugLoc(), CI,
732 "Too few arguments to Enzyme call ", *CI);
735 i += marker->extraOperands;
741 if (*metaString ==
"enzyme_not_overwritten") {
743 }
else if (*metaString ==
"enzyme_byref") {
744 if (!isa<ConstantInt>(CI->getArgOperand(i))) {
745 EmitFailure(
"IllegalAllocatedSize", CI->getDebugLoc(), CI,
746 "illegal enzyme byref size ", *CI->getArgOperand(i),
750 byRefSize = cast<ConstantInt>(CI->getArgOperand(i))->getZExtValue();
751 assert(byRefSize > 0);
752 }
else if (*metaString ==
"enzyme_dupv" ||
753 *metaString ==
"enzyme_dupnoneedv") {
754 Value *offset_arg = CI->getArgOperand(i);
755 if (offset_arg->getType()->isIntegerTy()) {
756 batchOffset = offset_arg;
758 EmitFailure(
"IllegalVectorOffset", CI->getDebugLoc(), CI,
759 "enzyme_batch must be followd by an integer "
761 *CI->getArgOperand(i),
" in", *CI);
764 }
else if (*metaString ==
"enzyme_allocated") {
766 if (!isa<ConstantInt>(CI->getArgOperand(i))) {
767 EmitFailure(
"IllegalAllocatedSize", CI->getDebugLoc(), CI,
768 "illegal enzyme allocated size ", *CI->getArgOperand(i),
773 cast<ConstantInt>(CI->getArgOperand(i))->getZExtValue();
774 }
else if (*metaString ==
"enzyme_tape") {
776 tape = CI->getArgOperand(i);
777 tapeIsPointer =
true;
778 }
else if (*metaString ==
"enzyme_nofree") {
781 }
else if (*metaString ==
"enzyme_runtime_activity") {
782 runtimeActivity =
true;
783 }
else if (*metaString ==
"enzyme_strong_zero") {
785 }
else if (*metaString ==
"enzyme_interface") {
786 dynamic_interface = CI->getArgOperand(i);
787 }
else if (*metaString ==
"enzyme_trace") {
788 trace = CI->getArgOperand(i);
789 }
else if (*metaString ==
"enzyme_duptrace") {
790 trace = CI->getArgOperand(i);
792 }
else if (*metaString ==
"enzyme_likelihood") {
793 likelihood = CI->getArgOperand(i);
794 }
else if (*metaString ==
"enzyme_duplikelihood") {
795 likelihood = CI->getArgOperand(i - 1);
796 diffeLikelihood = CI->getArgOperand(i);
797 }
else if (*metaString ==
"enzyme_observations") {
798 observations = CI->getArgOperand(i);
799 }
else if (*metaString ==
"enzyme_active_rand_var") {
800 Value *
string = CI->getArgOperand(i);
801 StringRef const_string;
802 if (getConstantStringInfo(
string, const_string)) {
803 ActiveRandomVariables.insert(const_string);
806 "IllegalStringType", CI->getDebugLoc(), CI,
807 "active variable address must be a compile-time constant", *CI,
819 constants.push_back(*opt_ty);
825 if (i == CI->arg_size()) {
826 EmitFailure(
"EnzymeCallingError", CI->getDebugLoc(), CI,
827 "Too few arguments to Enzyme call ", *CI);
830 res = CI->getArgOperand(i);
831 metaString = getMetadataName(res);
838 Type *subTy =
nullptr;
839 if (truei < FT->getNumParams()) {
840 subTy = FT->getParamType(i);
843 if (differentialReturn && differet ==
nullptr) {
844 subTy = FT->getReturnType();
850 "illegal enzyme byval arg", truei,
" ", *res);
854 auto &DL = fn->getParent()->getDataLayout();
855 auto BitSize = DL.getTypeSizeInBits(subTy);
856 if (BitSize / 8 != byRefSize) {
857 EmitFailure(
"IllegalByRefSize", CI->getDebugLoc(), CI,
858 "illegal enzyme pointer type size ", *res,
" expected ",
859 byRefSize,
" (bytes) actual size ", BitSize,
862 res = Builder.CreateBitCast(
865 subTy, cast<PointerType>(res->getType())->getAddressSpace()));
866 res = Builder.CreateLoad(subTy, res);
870 if (truei >= FT->getNumParams()) {
871 if (!isa<MetadataAsValue>(res) &&
874 if (differentialReturn && differet ==
nullptr) {
876 if (CI->paramHasAttr(i, Attribute::ByVal)) {
878 T = CI->getParamAttr(i, Attribute::ByVal).getValueAsType();
879 differet = Builder.CreateLoad(T, differet);
881 if (differet->getType() != fn->getReturnType())
882 if (
auto ST0 = dyn_cast<StructType>(differet->getType()))
883 if (
auto ST1 = dyn_cast<StructType>(fn->getReturnType()))
884 if (ST0->isLayoutIdentical(ST1)) {
885 IRBuilder<> B(&Builder.GetInsertBlock()
889 auto AI = B.CreateAlloca(ST1);
890 Builder.CreateStore(differet, Builder.CreatePointerCast(
892 differet = Builder.CreateLoad(ST1, AI);
895 if (differet->getType() !=
898 "Bad DiffRet type ", *differet,
" expected ",
899 *fn->getReturnType());
903 }
else if (tape ==
nullptr) {
905 if (CI->paramHasAttr(i, Attribute::ByVal)) {
907 T = CI->getParamAttr(i, Attribute::ByVal).getValueAsType();
908 tape = Builder.CreateLoad(T, tape);
914 "Had too many arguments to __enzyme_autodiff", *CI,
915 " - extra arg - ", *res);
918 assert(truei < FT->getNumParams());
919 overwritten_args[truei] = overwritten;
921 auto PTy = FT->getParamType(truei);
924 : ((interleaved == -1) ?
whatType(PTy, mode) : last_ty);
927 constants.push_back(ty);
929 assert(truei < FT->getNumParams());
931 if (PTy != res->getType()) {
932 if (
auto ptr = dyn_cast<PointerType>(res->getType())) {
933 if (
auto PT = dyn_cast<PointerType>(PTy)) {
934 if (ptr->getAddressSpace() != PT->getAddressSpace()) {
935#if LLVM_VERSION_MAJOR < 17
936 if (CI->getContext().supportsTypedPointers()) {
937 res = Builder.CreateAddrSpaceCast(
939 PT->getAddressSpace()));
941 res = Builder.CreateAddrSpaceCast(res, PT);
944 res = Builder.CreateAddrSpaceCast(res, PT);
949 llvm::errs() <<
"Warning cast(1) __enzyme_autodiff argument " << i
950 <<
" " << *res <<
"|" << *res->getType()
951 <<
" to argument " << truei <<
" " << *PTy <<
"\n"
952 <<
"orig: " << *FT <<
"\n";
956 if (res->getType()->canLosslesslyBitCastTo(PTy)) {
957 res = Builder.CreateBitCast(res, PTy);
959 if (res->getType() != PTy && res->getType()->isIntegerTy() &&
960 PTy->isIntegerTy(1)) {
961 res = Builder.CreateTrunc(res, PTy);
963 if (res->getType() != PTy) {
964 auto loc = CI->getDebugLoc();
965 if (
auto arg = dyn_cast<Instruction>(res)) {
966 loc = arg->getDebugLoc();
972 "Cannot cast __enzyme_autodiff primal argument ", i,
973 ", found ", *res,
", type ", *res->getType(),
974 " (simplified to ", *S,
" ) ",
" - to arg ", truei,
", ",
979 if (CI->isByValArgument(i)) {
980 byVal[args.size()] = CI->getParamByValType(i);
985 if (interleaved == -1)
988 Value *res =
nullptr;
989#if LLVM_VERSION_MAJOR >= 16
990 bool batch = batchOffset.has_value();
992 bool batch = batchOffset.hasValue();
995 for (
unsigned v = 0; v < width; ++v) {
996 if ((
size_t)((interleaved == -1) ? i : interleaved) >= num_args) {
997 EmitFailure(
"MissingArgShadow", CI->getDebugLoc(), CI,
998 "__enzyme_autodiff missing argument shadow at index ",
999 *((interleaved == -1) ? &i : &interleaved),
1000 ", need shadow of type ", *PTy,
1001 " to shadow primal argument ", *args.back(),
1008 CI->getArgOperand((interleaved == -1) ? i : interleaved);
1010 if (
auto elementPtrTy = dyn_cast<PointerType>(element->getType())) {
1011 element = Builder.CreateBitCast(
1013 elementPtrTy->getAddressSpace()));
1014 element = Builder.CreateGEP(
1015 Type::getInt8Ty(CI->getContext()), element,
1018 ConstantInt::get((*batchOffset)->getType(), v)));
1019 element = Builder.CreateBitCast(element, elementPtrTy);
1022 "NonPointerBatch", CI->getDebugLoc(), CI,
1023 "Batched argument at index ",
1024 *((interleaved == -1) ? &i : &interleaved),
1025 " must be of pointer type, found: ", *element->getType());
1029 if (PTy != element->getType()) {
1030 element = castToDiffeFunctionArgType(
1031 Builder, CI, FT, PTy, (interleaved == -1) ? i : interleaved,
1032 mode, element, truei);
1040 res ? Builder.CreateInsertValue(res, element, {v})
1041 : Builder.CreateInsertValue(UndefValue::get(ArrayType::get(
1042 element->getType(), width)),
1045 if (v < width - 1 && !batch && (interleaved == -1)) {
1053 if (interleaved != -1)
1057 args.push_back(res);
1062 if (truei < FT->getNumParams()) {
1063 auto numParams = FT->getNumParams();
1065 "EnzymeInsufficientArgs", CI->getDebugLoc(), CI,
1066 "Insufficient number of args passed to derivative call required ",
1067 numParams,
" primal args, found ", truei);
1071 return Options({differet,
1087 ActiveRandomVariables,
1091 subsequent_calls_may_write});
1097 for (
auto &a : type_args.
Function->args()) {
1099 bool parsed =
false;
1100 if (fn->getAttributes().hasParamAttr(a.getArgNo(),
"enzyme_type")) {
1102 fn->getAttributes().getParamAttr(a.getArgNo(),
"enzyme_type");
1105 }
else if (a.getType()->isFPOrFPVectorTy()) {
1107 }
else if (a.getType()->isPointerTy()) {
1108#if LLVM_VERSION_MAJOR < 17
1109 if (a.getContext().supportsTypedPointers()) {
1110 auto et = a.getType()->getPointerElementType();
1111 if (et->isFPOrFPVectorTy()) {
1113 }
else if (et->isPointerTy()) {
1119 }
else if (a.getType()->isIntOrIntVectorTy()) {
1122 type_args.
Arguments.insert(std::pair<Argument *, TypeTree>(
1123 &a, parsed ? dt : dt.
Only(-1,
nullptr)));
1127 std::pair<Argument *, std::set<int64_t>>(&a, {}));
1130 bool parsed =
false;
1131 if (fn->getAttributes().hasRetAttr(
"enzyme_type")) {
1132 auto attr = fn->getAttributes().getAttribute(AttributeList::ReturnIndex,
1136 }
else if (fn->getReturnType()->isFPOrFPVectorTy()) {
1137 dt =
ConcreteType(fn->getReturnType()->getScalarType());
1139 type_args.
Return = parsed ? dt : dt.
Only(-1,
nullptr);
1154 llvm_unreachable(
"Invalid float width");
1158 bool HandleTruncateFunc(CallInst *CI,
TruncateMode mode) {
1159 IRBuilder<> Builder(CI);
1160 Function *F = parseFunctionParameter(CI);
1163 unsigned ArgSize = CI->arg_size();
1164 if (ArgSize != 4 && ArgSize != 3) {
1166 "Had incorrect number of args to __enzyme_truncate_func", *CI,
1167 " - expected 3 or 4");
1172 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1174 auto Cto = cast<ConstantInt>(CI->getArgOperand(2));
1177 getDefaultFloatRepr((
unsigned)Cfrom->getValue().getZExtValue()),
1178 getDefaultFloatRepr((
unsigned)Cto->getValue().getZExtValue()),
1180 }
else if (ArgSize == 4) {
1181 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1183 auto Cto_exponent = cast<ConstantInt>(CI->getArgOperand(2));
1184 assert(Cto_exponent);
1185 auto Cto_significand = cast<ConstantInt>(CI->getArgOperand(3));
1186 assert(Cto_significand);
1188 getDefaultFloatRepr((
unsigned)Cfrom->getValue().getZExtValue()),
1190 (
unsigned)Cto_exponent->getValue().getZExtValue(),
1191 (
unsigned)Cto_significand->getValue().getZExtValue()),
1194 llvm_unreachable(
"??");
1201 res = Builder.CreatePointerCast(res, CI->getType());
1202 CI->replaceAllUsesWith(res);
1203 CI->eraseFromParent();
1207 bool HandleTruncateValue(CallInst *CI,
bool isTruncate) {
1208 IRBuilder<> Builder(CI);
1209 if (CI->arg_size() != 3) {
1211 "Had incorrect number of args to __enzyme_truncate_value",
1212 *CI,
" - expected 3");
1215 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1217 auto Cto = cast<ConstantInt>(CI->getArgOperand(2));
1219 auto Addr = CI->getArgOperand(0);
1223 getDefaultFloatRepr((
unsigned)Cfrom->getValue().getZExtValue()),
1224 getDefaultFloatRepr((
unsigned)Cto->getValue().getZExtValue()),
1231 bool HandleBatch(CallInst *CI) {
1234 std::map<unsigned, Value *> batchOffset;
1235 SmallVector<Value *, 4> args;
1236 SmallVector<BATCH_TYPE, 4> arg_types;
1237 IRBuilder<> Builder(CI);
1238 Function *F = parseFunctionParameter(CI);
1243 FunctionType *FT = F->getFunctionType();
1246 if (
auto parsedWidth = parseWidthParameter(CI)) {
1247 width = *parsedWidth;
1254 CI->hasStructRetAttr() || F->hasParamAttribute(0, Attribute::StructRet);
1256 if (F->hasParamAttribute(0, Attribute::StructRet)) {
1258 Value *sretPt = CI->getArgOperand(0);
1260 args.push_back(sretPt);
1264 for (
unsigned i = 1 + sret; i < CI->arg_size(); ++i) {
1265 Value *res = CI->getArgOperand(i);
1267 if (truei >= FT->getNumParams()) {
1269 "Had too many arguments to __enzyme_batch", *CI,
1270 " - extra arg - ", *res);
1273 assert(truei < FT->getNumParams());
1274 auto PTy = FT->getParamType(truei);
1277 auto metaString = getMetadataName(res);
1280 if (metaString &&
startsWith(*metaString,
"enzyme_")) {
1281 if (*metaString ==
"enzyme_scalar") {
1283 }
else if (*metaString ==
"enzyme_vector") {
1285 }
else if (*metaString ==
"enzyme_buffer") {
1288 Value *offset_arg = CI->getArgOperand(i);
1289 if (offset_arg->getType()->isIntegerTy()) {
1290 batchOffset[i + 1] = offset_arg;
1292 EmitFailure(
"IllegalVectorOffset", CI->getDebugLoc(), CI,
1293 "enzyme_batch must be followd by an integer "
1295 *CI->getArgOperand(i),
" in", *CI);
1299 }
else if (*metaString ==
"enzyme_width") {
1303 EmitFailure(
"IllegalDiffeType", CI->getDebugLoc(), CI,
1304 "illegal enzyme metadata classification ", *CI,
1309 res = CI->getArgOperand(i);
1312 arg_types.push_back(ty);
1316 Value *res =
nullptr;
1317 bool batch = batchOffset.count(i - 1) != 0;
1319 for (
unsigned v = 0; v < width; ++v) {
1320 if (i >= CI->arg_size()) {
1321 EmitFailure(
"MissingVectorArg", CI->getDebugLoc(), CI,
1322 "__enzyme_batch missing vector argument at index ", i,
1323 ", need argument of type ", *PTy,
" at call ", *CI);
1328 Value *element = CI->getArgOperand(i);
1330 if (
auto elementPtrTy = dyn_cast<PointerType>(element->getType())) {
1331 element = Builder.CreateBitCast(
1333 elementPtrTy->getAddressSpace()));
1334 element = Builder.CreateGEP(
1335 Type::getInt8Ty(CI->getContext()), element,
1338 ConstantInt::get(batchOffset[i - 1]->getType(), v)));
1339 element = Builder.CreateBitCast(element, elementPtrTy);
1347 res ? Builder.CreateInsertValue(res, element, {v})
1348 : Builder.CreateInsertValue(UndefValue::get(ArrayType::get(
1349 element->getType(), width)),
1352 if (v < width - 1 && !batch) {
1361 args.push_back(res);
1364 args.push_back(res);
1370 BATCH_TYPE ret_type = (F->getReturnType()->isVoidTy() || width == 1)
1375 arg_types, ret_type);
1381 Builder.CreateCall(newFunc->getFunctionType(), newFunc, args);
1383 batch = adaptReturnedVector(CI, batch, Builder, width);
1386 Type *retElemType =
nullptr;
1387 if (CI->hasStructRetAttr()) {
1388 ret = CI->getArgOperand(0);
1390 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1393 ReplaceOriginalCall(Builder, ret, retElemType, batch, CI,
1399 bool HandleAutoDiff(Instruction *CI, CallingConv::ID CallingConv, Value *ret,
1400 Type *retElemType, SmallVectorImpl<Value *> &args,
1401 const std::map<int, Type *> &byVal,
1402 const std::vector<DIFFE_TYPE> &constants, Function *fn,
1404 SmallVectorImpl<CallInst *> &calls) {
1405 auto &differet = options.differet;
1406 auto &tape = options.tape;
1407 auto &width = options.width;
1408 auto &allocatedTapeSize = options.allocatedTapeSize;
1409 auto &freeMemory = options.freeMemory;
1410 auto &returnUsed = options.returnUsed;
1411 auto &tapeIsPointer = options.tapeIsPointer;
1412 auto &differentialReturn = options.differentialReturn;
1413 auto &retType = options.retType;
1414 auto &overwritten_args = options.overwritten_args;
1415 auto primalReturn = options.primalReturn;
1416 auto subsequent_calls_may_write = options.subsequent_calls_may_write;
1418 auto TT = Triple(CI->getModule()->getTargetTriple());
1422 FnTypeInfo type_args = populate_type_args(TA, fn, mode);
1424 std::vector<bool> nowrite_shadows(fn->arg_size(),
false);
1426 IRBuilder Builder(CI);
1430 Function *newFunc =
nullptr;
1431 Type *tapeType =
nullptr;
1436 if (primalReturn && fn->getReturnType()->isVoidTy()) {
1437 auto fnname = fn->getName();
1438 EmitFailure(
"PrimalRetOfVoid", CI->getDebugLoc(), CI,
1439 "Requested primal result of void-returning function type ",
1440 *fn->getFunctionType(),
" ", fnname,
" ", *CI);
1443 context, fn, retType, constants, TA,
1444 primalReturn, mode, freeMemory,
1445 options.runtimeActivity, options.strongZero, width,
1446 nullptr, type_args, subsequent_calls_may_write,
1451 bool forceAnonymousTape = !sizeOnly && allocatedTapeSize == -1;
1453 context, fn, retType, constants, TA,
1454 false,
false, type_args,
1455 subsequent_calls_may_write, overwritten_args, nowrite_shadows,
1456 forceAnonymousTape, options.runtimeActivity, options.strongZero,
1458 auto &DL = fn->getParent()->getDataLayout();
1459 if (!forceAnonymousTape) {
1463 tapeType = (tapeIdx == -1)
1464 ? aug->
fn->getReturnType()
1465 : cast<StructType>(aug->
fn->getReturnType())
1466 ->getElementType(tapeIdx);
1469 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), 0,
false));
1470 CI->eraseFromParent();
1475 auto size = DL.getTypeSizeInBits(tapeType) / 8;
1476 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), size,
false));
1477 CI->eraseFromParent();
1481 DL.getTypeSizeInBits(tapeType) > 8 * (
size_t)allocatedTapeSize) {
1482 auto bytes = DL.getTypeSizeInBits(tapeType) / 8;
1483 EmitFailure(
"Insufficient tape allocation size", CI->getDebugLoc(),
1484 CI,
"need ", bytes,
" bytes have ", allocatedTapeSize,
1491 context, fn, retType, constants, TA,
1492 primalReturn, mode, freeMemory,
1493 options.runtimeActivity, options.strongZero, width,
1494 tapeType, type_args, subsequent_calls_may_write,
1495 overwritten_args, aug);
1504 .constant_args = constants,
1505 .subsequent_calls_may_write =
1506 subsequent_calls_may_write,
1507 .overwritten_args = overwritten_args,
1508 .returnUsed = primalReturn,
1509 .shadowReturnUsed =
false,
1512 .freeMemory = freeMemory,
1513 .AtomicAdd = AtomicAdd,
1514 .additionalType =
nullptr,
1515 .forceAnonymousTape =
false,
1516 .typeInfo = type_args,
1517 .runtimeActivity = options.runtimeActivity,
1518 .strongZero = options.strongZero},
1525 "SplitPrimalRet", CI->getDebugLoc(), CI,
1526 "Option enzyme_primal_return not available in reverse split mode");
1528 bool forceAnonymousTape = !sizeOnly && allocatedTapeSize == -1;
1532 context, fn, retType, constants, TA, returnUsed, shadowReturnUsed,
1533 type_args, subsequent_calls_may_write, overwritten_args,
1534 nowrite_shadows, forceAnonymousTape, options.runtimeActivity,
1535 options.strongZero, width, AtomicAdd);
1536 auto &DL = fn->getParent()->getDataLayout();
1537 if (!forceAnonymousTape) {
1538 assert(!aug->tapeType);
1541 tapeType = (tapeIdx == -1)
1542 ? aug->fn->getReturnType()
1543 : cast<StructType>(aug->fn->getReturnType())
1544 ->getElementType(tapeIdx);
1547 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), 0,
false));
1548 CI->eraseFromParent();
1553 auto size = DL.getTypeSizeInBits(tapeType) / 8;
1554 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), size,
false));
1555 CI->eraseFromParent();
1559 DL.getTypeSizeInBits(tapeType) > 8 * (
size_t)allocatedTapeSize) {
1560 auto bytes = DL.getTypeSizeInBits(tapeType) / 8;
1561 EmitFailure(
"Insufficient tape allocation size", CI->getDebugLoc(),
1562 CI,
"need ", bytes,
" bytes have ", allocatedTapeSize,
1575 .constant_args = constants,
1576 .subsequent_calls_may_write =
1577 subsequent_calls_may_write,
1578 .overwritten_args = overwritten_args,
1579 .returnUsed =
false,
1580 .shadowReturnUsed =
false,
1583 .freeMemory = freeMemory,
1584 .AtomicAdd = AtomicAdd,
1585 .additionalType = tapeType,
1586 .forceAnonymousTape = forceAnonymousTape,
1587 .typeInfo = type_args,
1588 .runtimeActivity = options.runtimeActivity,
1589 .strongZero = options.strongZero},
1595 StringRef n = fn->getName();
1596 EmitFailure(
"FailedToDifferentiate", fn->getSubprogram(),
1597 &*fn->getEntryBlock().begin(),
1598 "Could not generate derivative function of ", n);
1602 if (differentialReturn) {
1604 args.push_back(differet);
1605 else if (fn->getReturnType()->isFPOrFPVectorTy()) {
1606 Constant *seed = ConstantFP::get(fn->getReturnType(), 1.0);
1608 args.push_back(seed);
1610 ArrayType *arrayType = ArrayType::get(fn->getReturnType(), width);
1611 args.push_back(ConstantArray::get(
1612 arrayType, SmallVector<Constant *, 3>(width, seed)));
1614 }
else if (
auto ST = dyn_cast<StructType>(fn->getReturnType())) {
1615 SmallVector<Constant *, 2> csts;
1616 for (
auto e : ST->elements()) {
1617 csts.push_back(ConstantFP::get(e, 1.0));
1619 args.push_back(ConstantStruct::get(ST, csts));
1620 }
else if (
auto AT = dyn_cast<ArrayType>(fn->getReturnType())) {
1621 SmallVector<Constant *, 2> csts(
1622 AT->getNumElements(), ConstantFP::get(AT->getElementType(), 1.0));
1623 args.push_back(ConstantArray::get(AT, csts));
1625 auto RT = fn->getReturnType();
1626 EmitFailure(
"EnzymeCallingError", CI->getDebugLoc(), CI,
1627 "Differential return required for call ", *CI,
1628 " but one of type ", *RT,
" could not be auto deduced");
1636 auto &DL = fn->getParent()->getDataLayout();
1637 if (tapeIsPointer) {
1638 tape = Builder.CreateBitCast(
1641 cast<PointerType>(tape->getType())->getAddressSpace()));
1642 tape = Builder.CreateLoad(tapeType, tape);
1643 }
else if (tapeType != tape->getType() &&
1644 DL.getTypeSizeInBits(tapeType) <=
1645 DL.getTypeSizeInBits(tape->getType())) {
1646 IRBuilder<> EB(&CI->getParent()->getParent()->getEntryBlock().front());
1647 auto AL = EB.CreateAlloca(tape->getType());
1648 Builder.CreateStore(tape, AL);
1649 tape = Builder.CreateLoad(
1650 tapeType, Builder.CreatePointerCast(AL,
getUnqual(tapeType)));
1652 assert(tape->getType() == tapeType);
1653 args.push_back(tape);
1657 llvm::errs() <<
"postfn:\n" << *newFunc <<
"\n";
1659 Builder.setFastMathFlags(
getFast());
1662 if (args.size() != newFunc->getFunctionType()->getNumParams()) {
1663 llvm::errs() << *CI <<
"\n";
1664 llvm::errs() << *newFunc <<
"\n";
1665 for (
auto arg : args) {
1666 llvm::errs() <<
" + " << *arg <<
"\n";
1670 "TooFewArguments", CI->getDebugLoc(), CI,
1671 "Too few arguments passed to __enzyme_autodiff mode=", modestr);
1674 assert(args.size() == newFunc->getFunctionType()->getNumParams());
1675 for (
size_t i = 0; i < args.size(); i++) {
1676 if (args[i]->getType() != newFunc->getFunctionType()->getParamType(i)) {
1677 llvm::errs() << *CI <<
"\n";
1678 llvm::errs() << *newFunc <<
"\n";
1679 for (
auto arg : args) {
1680 llvm::errs() <<
" + " << *arg <<
"\n";
1683 EmitFailure(
"BadArgumentType", CI->getDebugLoc(), CI,
1684 "Incorrect argument type passed to __enzyme_autodiff mode=",
1685 modestr,
" at index ", i,
" expected ",
1686 *newFunc->getFunctionType()->getParamType(i),
" found ",
1687 *args[i]->getType());
1691 CallInst *diffretc = cast<CallInst>(Builder.CreateCall(newFunc, args));
1692 diffretc->setCallingConv(CallingConv);
1693 diffretc->setDebugLoc(CI->getDebugLoc());
1695 for (
auto &&[attr, ty] : byVal) {
1696 diffretc->addParamAttr(
1697 attr, Attribute::getWithByValType(diffretc->getContext(), ty));
1700 Value *diffret = diffretc;
1704 tapeType = (tapeIdx == -1) ? aug->
fn->getReturnType()
1705 : cast<StructType>(aug->
fn->getReturnType())
1706 ->getElementType(tapeIdx);
1707 unsigned idxs[] = {(unsigned)tapeIdx};
1708 Value *tapeRes = (tapeIdx == -1)
1710 : Builder.CreateExtractValue(diffret, idxs);
1711 Builder.CreateStore(
1712 tapeRes, Builder.CreateBitCast(
1714 cast<PointerType>(tape->getType())
1715 ->getAddressSpace())));
1716 if (tapeIdx != -1) {
1717 auto ST = cast<StructType>(diffret->getType());
1718 SmallVector<Type *, 2> tys(ST->elements().begin(),
1719 ST->elements().end());
1720 tys.erase(tys.begin());
1721 auto ST0 = StructType::get(ST->getContext(), tys);
1722 Value *out = UndefValue::get(ST0);
1723 for (
unsigned i = 0; i < tys.size(); i++) {
1724 out = Builder.CreateInsertValue(
1725 out, Builder.CreateExtractValue(diffret, {i + 1}), {i});
1729 auto ST0 = StructType::get(tape->getContext(), {});
1730 diffret = UndefValue::get(ST0);
1737 if (width > 1 && !diffret->getType()->isEmptyTy() &&
1738 !diffret->getType()->isVoidTy() &&
1742 diffret = adaptReturnedVector(ret, diffret, Builder, width);
1745 ReplaceOriginalCall(Builder, ret, retElemType, diffret, CI, mode);
1746 calls.push_back(diffretc);
1751 bool HandleAutoDiffArguments(CallInst *CI,
DerivativeMode mode,
bool sizeOnly,
1752 SmallVectorImpl<CallInst *> &calls) {
1755 Function *fn = parseFunctionParameter(CI);
1759 IRBuilder<> Builder(CI);
1762 llvm::errs() <<
"prefn:\n" << *fn <<
"\n";
1764 std::map<int, Type *> byVal;
1765 std::vector<DIFFE_TYPE> constants;
1766 SmallVector<Value *, 2> args;
1768 auto options = handleArguments(Builder, CI, fn, mode, sizeOnly, constants,
1776 Type *retElemType =
nullptr;
1777 if (CI->hasStructRetAttr()) {
1778 ret = CI->getArgOperand(0);
1780 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1784 return HandleAutoDiff(CI, CI->getCallingConv(), ret, retElemType, args,
1785 byVal, constants, fn, mode, *options, sizeOnly,
1790 SmallVectorImpl<CallInst *> &calls) {
1791 IRBuilder<> Builder(CI);
1792 Function *F = parseFunctionParameter(CI);
1798 std::vector<DIFFE_TYPE> constants;
1799 std::map<int, Type *> byVal;
1800 SmallVector<Value *, 4> args;
1804 auto opt = handleArguments(Builder, CI, F, diffeMode,
false, constants,
1807 SmallVector<Value *, 6> dargs(args.begin(), args.end());
1809#if LLVM_VERSION_MAJOR >= 16
1810 if (!opt.has_value())
1813 if (!opt.hasValue())
1817 auto dynamic_interface = opt->dynamic_interface;
1818 auto trace = opt->trace;
1819 auto dtrace = opt->diffeTrace;
1820 auto observations = opt->observations;
1821 auto likelihood = opt->likelihood;
1822 auto dlikelihood = opt->diffeLikelihood;
1825 bool has_dynamic_interface = dynamic_interface !=
nullptr;
1826 bool needs_interface =
1828 std::unique_ptr<TraceInterface> interface;
1829 if (has_dynamic_interface) {
1832 }
else if (needs_interface) {
1837 SmallPtrSet<Function *, 4> sampleFunctions;
1838 SmallPtrSet<Function *, 4> observeFunctions;
1839 for (
auto &func : F->getParent()->functions()) {
1840 if (func.getName().contains(
"__enzyme_sample")) {
1841 assert(func.getFunctionType()->getNumParams() >= 3);
1842 sampleFunctions.insert(&func);
1843 }
else if (func.getName().contains(
"__enzyme_observe")) {
1844 assert(func.getFunctionType()->getNumParams() >= 3);
1845 observeFunctions.insert(&func);
1849 assert(!sampleFunctions.empty() || !observeFunctions.empty());
1851 bool autodiff = dtrace || dlikelihood;
1855 likelihood = AllocaBuilder.CreateAlloca(AllocaBuilder.getDoubleTy(),
1856 nullptr,
"likelihood");
1857 Builder.CreateStore(ConstantFP::getNullValue(Builder.getDoubleTy()),
1860 args.push_back(likelihood);
1862 if (autodiff && !dlikelihood) {
1863 dlikelihood = AllocaBuilder.CreateAlloca(AllocaBuilder.getDoubleTy(),
1864 nullptr,
"dlikelihood");
1865 Builder.CreateStore(ConstantFP::get(Builder.getDoubleTy(), 1.0),
1870 dargs.push_back(likelihood);
1871 dargs.push_back(dlikelihood);
1873 opt->overwritten_args.push_back(
false);
1876 opt->overwritten_args.push_back(
false);
1880 opt->overwritten_args.push_back(
false);
1881 args.push_back(observations);
1882 dargs.push_back(observations);
1887 opt->overwritten_args.push_back(
false);
1888 args.push_back(trace);
1889 dargs.push_back(trace);
1894 RequestContext(CI, &Builder), F, sampleFunctions, observeFunctions,
1895 opt->ActiveRandomVariables, mode, autodiff, interface.get());
1898 auto call = CallInst::Create(newFunc->getFunctionType(), newFunc, args);
1899 ReplaceInstWithInst(CI, call);
1904 Type *retElemType =
nullptr;
1905 if (CI->hasStructRetAttr()) {
1906 ret = CI->getArgOperand(0);
1908 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1912 bool status = HandleAutoDiff(
1913 CI, CI->getCallingConv(), ret, retElemType, dargs, byVal, constants,
1919 bool handleFullModuleTrunc(Function &F) {
1922 typedef std::vector<FloatTruncation> TruncationsTy;
1923 static TruncationsTy FullModuleTruncs = []() -> TruncationsTy {
1925 auto Invalid = [=]() {
1927 llvm::report_fatal_error(
"error: invalid format for truncation config");
1931 auto parseFloatRepr = [&]() -> std::optional<FloatRepresentation> {
1933 if (ConfigStr.consumeInteger(10, Tmp))
1935 if (ConfigStr.consume_front(
"-")) {
1937 if (ConfigStr.consumeInteger(10, Tmp2))
1941 return getDefaultFloatRepr(Tmp);
1947 auto From = parseFloatRepr();
1948 if (!From && !ConfigStr.empty())
1952 if (!ConfigStr.consume_front(
"to"))
1954 auto To = parseFloatRepr();
1958 ConfigStr.consume_front(
";");
1963 if (FullModuleTruncs.empty())
1967 for (
auto Truncation : FullModuleTruncs) {
1968 IRBuilder<> Builder(F.getContext());
1973 ValueToValueMapTy Mapping;
1974 for (
auto &&[Arg, TArg] : llvm::zip(F.args(), TruncatedFunc->args()))
1975 Mapping[&TArg] = &Arg;
1979#if LLVM_VERSION_MAJOR >= 16
1980 F.splice(F.begin(), TruncatedFunc);
1982 F.getBasicBlockList().splice(F.begin(),
1983 TruncatedFunc->getBasicBlockList());
1985 RemapFunction(F, Mapping,
1986 RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
1987 TruncatedFunc->deleteBody();
1992 bool lowerEnzymeCalls(Function &F, std::set<Function *> &done) {
2000 if (handleFullModuleTrunc(F))
2003 bool Changed =
false;
2005 for (BasicBlock &BB : F)
2006 if (InvokeInst *II = dyn_cast<InvokeInst>(BB.getTerminator())) {
2008 Function *Fn = II->getCalledFunction();
2010 if (
auto castinst = dyn_cast<ConstantExpr>(II->getCalledOperand())) {
2011 if (castinst->isCast())
2012 if (
auto fn = dyn_cast<Function>(castinst->getOperand(0)))
2018 if (!(Fn->getName().contains(
"__enzyme_float") ||
2019 Fn->getName().contains(
"__enzyme_double") ||
2020 Fn->getName().contains(
"__enzyme_integer") ||
2021 Fn->getName().contains(
"__enzyme_pointer") ||
2022 Fn->getName().contains(
"__enzyme_virtualreverse") ||
2023 Fn->getName().contains(
"__enzyme_call_inactive") ||
2024 Fn->getName().contains(
"__enzyme_autodiff") ||
2025 Fn->getName().contains(
"__enzyme_fwddiff") ||
2026 Fn->getName().contains(
"__enzyme_fwdsplit") ||
2027 Fn->getName().contains(
"__enzyme_augmentfwd") ||
2028 Fn->getName().contains(
"__enzyme_augmentsize") ||
2029 Fn->getName().contains(
"__enzyme_reverse") ||
2030 Fn->getName().contains(
"__enzyme_truncate") ||
2031 Fn->getName().contains(
"__enzyme_batch") ||
2032 Fn->getName().contains(
"__enzyme_error_estimate") ||
2033 Fn->getName().contains(
"__enzyme_trace") ||
2034 Fn->getName().contains(
"__enzyme_condition")))
2037 SmallVector<Value *, 16> CallArgs(II->arg_begin(), II->arg_end());
2038 SmallVector<OperandBundleDef, 1> OpBundles;
2039 II->getOperandBundlesAsDefs(OpBundles);
2041#if LLVM_VERSION_MAJOR >= 24
2043 CallInst::Create(II->getFunctionType(), II->getCalledOperand(),
2044 CallArgs, OpBundles,
"", II->getIterator());
2047 CallInst::Create(II->getFunctionType(), II->getCalledOperand(),
2048 CallArgs, OpBundles,
"", II);
2050 NewCall->takeName(II);
2051 NewCall->setCallingConv(II->getCallingConv());
2052 NewCall->setAttributes(II->getAttributes());
2053 NewCall->setDebugLoc(II->getDebugLoc());
2054 II->replaceAllUsesWith(NewCall);
2057#if LLVM_VERSION_MAJOR >= 24
2064 II->getUnwindDest()->removePredecessor(&BB);
2066 II->eraseFromParent();
2070 MapVector<CallInst *, DerivativeMode> toLower;
2071 MapVector<CallInst *, DerivativeMode> toVirtual;
2072 MapVector<CallInst *, DerivativeMode> toSize;
2073 SmallVector<CallInst *, 4> toBatch;
2074 SmallVector<CallInst *, 4> toTruncateFuncMem;
2075 SmallVector<CallInst *, 4> toTruncateFuncOp;
2076 SmallVector<CallInst *, 4> toTruncateValue;
2077 SmallVector<CallInst *, 4> toExpandValue;
2078 MapVector<CallInst *, ProbProgMode> toProbProg;
2079 SetVector<CallInst *> InactiveCalls;
2080 SetVector<CallInst *> IterCalls;
2082 for (BasicBlock &BB : F) {
2083 for (Instruction &I : BB) {
2084 CallInst *CI = dyn_cast<CallInst>(&I);
2089 Function *Fn =
nullptr;
2091 Value *FnOp = CI->getCalledOperand();
2093 if ((Fn = dyn_cast<Function>(FnOp)))
2095 if (
auto castinst = dyn_cast<ConstantExpr>(FnOp)) {
2096 if (castinst->isCast()) {
2097 FnOp = castinst->getOperand(0);
2107 size_t num_args = CI->arg_size();
2109 if (Fn->getName().contains(
"__enzyme_todense") ||
2110 Fn->getName().contains(
"__enzyme_ignore_derivatives")) {
2111#if LLVM_VERSION_MAJOR >= 16
2112 CI->setOnlyReadsMemory();
2113 CI->setOnlyWritesMemory();
2115 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2118 if (Fn->getName().contains(
"__enzyme_float")) {
2119#if LLVM_VERSION_MAJOR >= 16
2120 CI->setOnlyReadsMemory();
2121 CI->setOnlyWritesMemory();
2123 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2125 for (
size_t i = 0; i < num_args; ++i) {
2126 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2127 CI->addParamAttr(i, Attribute::ReadNone);
2132 if (Fn->getName().contains(
"__enzyme_integer")) {
2133#if LLVM_VERSION_MAJOR >= 16
2134 CI->setOnlyReadsMemory();
2135 CI->setOnlyWritesMemory();
2137 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2139 for (
size_t i = 0; i < num_args; ++i) {
2140 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2141 CI->addParamAttr(i, Attribute::ReadNone);
2146 if (Fn->getName().contains(
"__enzyme_double")) {
2147#if LLVM_VERSION_MAJOR >= 16
2148 CI->setOnlyReadsMemory();
2149 CI->setOnlyWritesMemory();
2151 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2153 for (
size_t i = 0; i < num_args; ++i) {
2154 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2155 CI->addParamAttr(i, Attribute::ReadNone);
2160 if (Fn->getName().contains(
"__enzyme_pointer")) {
2161#if LLVM_VERSION_MAJOR >= 16
2162 CI->setOnlyReadsMemory();
2163 CI->setOnlyWritesMemory();
2165 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2167 for (
size_t i = 0; i < num_args; ++i) {
2168 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2169 CI->addParamAttr(i, Attribute::ReadNone);
2174 if (Fn->getName().contains(
"__enzyme_virtualreverse")) {
2175#if LLVM_VERSION_MAJOR >= 16
2176 CI->setOnlyReadsMemory();
2177 CI->setOnlyWritesMemory();
2179 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2182 if (Fn->getName().contains(
"__enzyme_iter")) {
2183#if LLVM_VERSION_MAJOR >= 16
2184 CI->setOnlyReadsMemory();
2185 CI->setOnlyWritesMemory();
2187 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2190 if (Fn->getName().contains(
"__enzyme_call_inactive")) {
2191 InactiveCalls.insert(CI);
2193 if (Fn->getName() ==
"omp_get_max_threads" ||
2194 Fn->getName() ==
"omp_get_thread_num") {
2195#if LLVM_VERSION_MAJOR >= 16
2196 Fn->setOnlyAccessesInaccessibleMemory();
2197 CI->setOnlyAccessesInaccessibleMemory();
2198 Fn->setOnlyReadsMemory();
2199 CI->setOnlyReadsMemory();
2201 Fn->addFnAttr(Attribute::InaccessibleMemOnly);
2202 CI->addAttribute(AttributeList::FunctionIndex,
2203 Attribute::InaccessibleMemOnly);
2204 Fn->addFnAttr(Attribute::ReadOnly);
2205 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly);
2208 if ((Fn->getName() ==
"cblas_ddot" || Fn->getName() ==
"cblas_sdot") &&
2209 Fn->isDeclaration()) {
2210#if LLVM_VERSION_MAJOR >= 16
2211 Fn->setOnlyAccessesArgMemory();
2212 Fn->setOnlyReadsMemory();
2213 CI->setOnlyReadsMemory();
2215 Fn->addFnAttr(Attribute::ArgMemOnly);
2216 Fn->addFnAttr(Attribute::ReadOnly);
2217 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly);
2219 CI->addParamAttr(1, Attribute::ReadOnly);
2221 CI->addParamAttr(3, Attribute::ReadOnly);
2224 if (Fn->getName() ==
"frexp" || Fn->getName() ==
"frexpf" ||
2225 Fn->getName() ==
"frexpl") {
2226#if LLVM_VERSION_MAJOR >= 16
2227 CI->setOnlyAccessesArgMemory();
2229 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ArgMemOnly);
2231 CI->addParamAttr(1, Attribute::WriteOnly);
2233 if (Fn->getName() ==
"__fd_sincos_1" || Fn->getName() ==
"__fd_cos_1" ||
2234 Fn->getName() ==
"__mth_i_ipowi") {
2235#if LLVM_VERSION_MAJOR >= 16
2236 CI->setOnlyReadsMemory();
2237 CI->setOnlyWritesMemory();
2239 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2243 Fn->addParamAttr(0, Attribute::ReadOnly);
2244 Fn->addParamAttr(1, Attribute::ReadOnly);
2245#if LLVM_VERSION_MAJOR >= 16
2246 Fn->setOnlyReadsMemory();
2247 CI->setOnlyReadsMemory();
2249 Fn->addFnAttr(Attribute::ReadOnly);
2250 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly);
2253 if (Fn->getName() ==
"f90io_fmtw_end" ||
2254 Fn->getName() ==
"f90io_unf_end") {
2255#if LLVM_VERSION_MAJOR >= 16
2256 Fn->setOnlyAccessesInaccessibleMemory();
2257 CI->setOnlyAccessesInaccessibleMemory();
2259 Fn->addFnAttr(Attribute::InaccessibleMemOnly);
2260 CI->addAttribute(AttributeList::FunctionIndex,
2261 Attribute::InaccessibleMemOnly);
2264 if (Fn->getName() ==
"f90io_open2003a") {
2265#if LLVM_VERSION_MAJOR >= 16
2266 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2267 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2269 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2270 CI->addAttribute(AttributeList::FunctionIndex,
2271 Attribute::InaccessibleMemOrArgMemOnly);
2273 for (
size_t i : {0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13}) {
2275 CI->getArgOperand(i)->getType()->isPointerTy()) {
2276 CI->addParamAttr(i, Attribute::ReadOnly);
2280 for (
size_t i : {0, 1}) {
2282 CI->getArgOperand(i)->getType()->isPointerTy()) {
2287 if (Fn->getName() ==
"f90io_fmtw_inita") {
2288#if LLVM_VERSION_MAJOR >= 16
2289 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2290 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2292 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2293 CI->addAttribute(AttributeList::FunctionIndex,
2294 Attribute::InaccessibleMemOrArgMemOnly);
2297 for (
size_t i : {0, 2}) {
2299 CI->getArgOperand(i)->getType()->isPointerTy()) {
2300 CI->addParamAttr(i, Attribute::ReadOnly);
2305 for (
size_t i : {0, 2}) {
2307 CI->getArgOperand(i)->getType()->isPointerTy()) {
2313 if (Fn->getName() ==
"f90io_unf_init") {
2314#if LLVM_VERSION_MAJOR >= 16
2315 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2316 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2318 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2319 CI->addAttribute(AttributeList::FunctionIndex,
2320 Attribute::InaccessibleMemOrArgMemOnly);
2323 for (
size_t i : {0, 1, 2, 3}) {
2325 CI->getArgOperand(i)->getType()->isPointerTy()) {
2326 CI->addParamAttr(i, Attribute::ReadOnly);
2331 for (
size_t i : {0, 1, 2, 3}) {
2333 CI->getArgOperand(i)->getType()->isPointerTy()) {
2339 if (Fn->getName() ==
"f90io_src_info03a") {
2340#if LLVM_VERSION_MAJOR >= 16
2341 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2342 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2344 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2345 CI->addAttribute(AttributeList::FunctionIndex,
2346 Attribute::InaccessibleMemOrArgMemOnly);
2349 for (
size_t i : {0, 1}) {
2351 CI->getArgOperand(i)->getType()->isPointerTy()) {
2352 CI->addParamAttr(i, Attribute::ReadOnly);
2357 for (
size_t i : {0}) {
2359 CI->getArgOperand(i)->getType()->isPointerTy()) {
2364 if (Fn->getName() ==
"f90io_sc_d_fmt_write" ||
2365 Fn->getName() ==
"f90io_sc_i_fmt_write" ||
2366 Fn->getName() ==
"ftnio_fmt_write64" ||
2367 Fn->getName() ==
"f90io_fmt_write64_aa" ||
2368 Fn->getName() ==
"f90io_fmt_writea" ||
2369 Fn->getName() ==
"f90io_unf_writea" ||
2370 Fn->getName() ==
"f90_pausea") {
2371#if LLVM_VERSION_MAJOR >= 16
2372 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2373 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2375 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2376 CI->addAttribute(AttributeList::FunctionIndex,
2377 Attribute::InaccessibleMemOrArgMemOnly);
2379 for (
size_t i = 0; i < num_args; ++i) {
2380 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2381 CI->addParamAttr(i, Attribute::ReadOnly);
2387 bool enableEnzyme =
false;
2388 bool virtualCall =
false;
2389 bool sizeOnly =
false;
2391 bool truncateFuncOp =
false;
2392 bool truncateFuncMem =
false;
2393 bool truncateValue =
false;
2394 bool expandValue =
false;
2395 bool probProg =
false;
2398 if (Fn->getName().contains(
"__enzyme_autodiff")) {
2399 enableEnzyme =
true;
2401 }
else if (Fn->getName().contains(
"__enzyme_fwddiff")) {
2402 enableEnzyme =
true;
2404 }
else if (Fn->getName().contains(
"__enzyme_error_estimate")) {
2405 enableEnzyme =
true;
2407 }
else if (Fn->getName().contains(
"__enzyme_fwdsplit")) {
2408 enableEnzyme =
true;
2410 }
else if (Fn->getName().contains(
"__enzyme_augmentfwd")) {
2411 enableEnzyme =
true;
2413 }
else if (Fn->getName().contains(
"__enzyme_augmentsize")) {
2414 enableEnzyme =
true;
2417 }
else if (Fn->getName().contains(
"__enzyme_reverse")) {
2418 enableEnzyme =
true;
2420 }
else if (Fn->getName().contains(
"__enzyme_virtualreverse")) {
2421 enableEnzyme =
true;
2424 }
else if (Fn->getName().contains(
"__enzyme_batch")) {
2425 enableEnzyme =
true;
2427 }
else if (Fn->getName().contains(
"__enzyme_truncate_mem_func")) {
2428 enableEnzyme =
true;
2429 truncateFuncMem =
true;
2430 }
else if (Fn->getName().contains(
"__enzyme_truncate_op_func")) {
2431 enableEnzyme =
true;
2432 truncateFuncOp =
true;
2433 }
else if (Fn->getName().contains(
"__enzyme_truncate_mem_value")) {
2434 enableEnzyme =
true;
2435 truncateValue =
true;
2436 }
else if (Fn->getName().contains(
"__enzyme_expand_mem_value")) {
2437 enableEnzyme =
true;
2439 }
else if (Fn->getName().contains(
"__enzyme_likelihood")) {
2440 enableEnzyme =
true;
2443 }
else if (Fn->getName().contains(
"__enzyme_trace")) {
2444 enableEnzyme =
true;
2447 }
else if (Fn->getName().contains(
"__enzyme_condition")) {
2448 enableEnzyme =
true;
2455 Value *fn = CI->getArgOperand(0);
2456 while (
auto ci = dyn_cast<CastInst>(fn)) {
2457 fn = ci->getOperand(0);
2459 while (
auto ci = dyn_cast<BlockAddress>(fn)) {
2460 fn = ci->getFunction();
2462 while (
auto ci = dyn_cast<ConstantExpr>(fn)) {
2463 fn = ci->getOperand(0);
2465 if (
auto si = dyn_cast<SelectInst>(fn)) {
2466 BasicBlock *post = BB.splitBasicBlock(CI);
2467 BasicBlock *sel1 = BasicBlock::Create(BB.getContext(),
"sel1", &F);
2468 BasicBlock *sel2 = BasicBlock::Create(BB.getContext(),
"sel2", &F);
2469 BB.getTerminator()->eraseFromParent();
2470 IRBuilder<> PB(&BB);
2471 PB.CreateCondBr(si->getCondition(), sel1, sel2);
2472 IRBuilder<> S1(sel1);
2473 auto B1 = S1.CreateBr(post);
2474 CallInst *cloned = cast<CallInst>(CI->clone());
2476 cloned->setOperand(0, si->getTrueValue());
2477 IRBuilder<> S2(sel2);
2478 auto B2 = S2.CreateBr(post);
2480 CI->setOperand(0, si->getFalseValue());
2481 if (CI->getNumUses() != 0) {
2483 auto merge = P.CreatePHI(CI->getType(), 2);
2484 merge->addIncoming(cloned, sel1);
2485 merge->addIncoming(CI, sel2);
2486 CI->replaceAllUsesWith(merge);
2491 toVirtual[CI] = derivativeMode;
2493 toSize[CI] = derivativeMode;
2495 toBatch.push_back(CI);
2496 else if (truncateFuncOp)
2497 toTruncateFuncOp.push_back(CI);
2498 else if (truncateFuncMem)
2499 toTruncateFuncMem.push_back(CI);
2500 else if (truncateValue)
2501 toTruncateValue.push_back(CI);
2502 else if (expandValue)
2503 toExpandValue.push_back(CI);
2504 else if (probProg) {
2505 toProbProg[CI] = probProgMode;
2507 toLower[CI] = derivativeMode;
2509 if (
auto dc = dyn_cast<Function>(fn)) {
2514 Changed |= lowerEnzymeCalls(*dc, done);
2521 for (
auto CI : InactiveCalls) {
2523 Value *fn = CI->getArgOperand(0);
2524 SmallVector<Value *, 4>
Args;
2525 SmallVector<Type *, 4> ArgTypes;
2526 for (
size_t i = 1; i < CI->arg_size(); ++i) {
2527 Args.push_back(CI->getArgOperand(i));
2528 ArgTypes.push_back(CI->getArgOperand(i)->getType());
2530 auto FT = FunctionType::get(CI->getType(), ArgTypes,
false);
2531 if (fn->getType() != FT) {
2532 fn = B.CreatePointerCast(fn,
getUnqual(FT));
2534 auto Rep = B.CreateCall(FT, fn,
Args);
2535 Rep->addAttribute(AttributeList::FunctionIndex,
2536 Attribute::get(Rep->getContext(),
"enzyme_inactive"));
2537 CI->replaceAllUsesWith(Rep);
2538 CI->eraseFromParent();
2542 SmallVector<CallInst *, 1> calls;
2545 for (
auto pair : toSize) {
2546 bool successful = HandleAutoDiffArguments(pair.first, pair.second,
2552 for (
auto pair : toLower) {
2553 bool successful = HandleAutoDiffArguments(pair.first, pair.second,
2560 for (
auto pair : toVirtual) {
2561 auto CI = pair.first;
2562 Constant *fn = dyn_cast<Constant>(CI->getArgOperand(0));
2564 EmitFailure(
"IllegalVirtual", CI->getDebugLoc(), CI,
2565 "Cannot create virtual version of non-constant value ", *CI,
2566 *CI->getArgOperand(0));
2571 auto TT = llvm::Triple(
2572 CI->getParent()->getParent()->getParent()->getTargetTriple());
2576 IRBuilder<> Builder(CI);
2579 Logic.
PPC.
FAM.getResult<TargetLibraryAnalysis>(F), TA, fn,
2580 pair.second,
false,
false,
2582 CI->replaceAllUsesWith(ConstantExpr::getPointerCast(val, CI->getType()));
2583 CI->eraseFromParent();
2587 for (
auto call : toBatch) {
2590 for (
auto call : toTruncateFuncMem) {
2593 for (
auto call : toTruncateFuncOp) {
2596 for (
auto call : toTruncateValue) {
2597 HandleTruncateValue(call,
true);
2599 for (
auto call : toExpandValue) {
2600 HandleTruncateValue(call,
false);
2603 for (
auto &&[call, mode] : toProbProg) {
2604 HandleProbProg(call, mode, calls);
2608 auto Params = llvm::getInlineParams();
2610 llvm::SetVector<CallInst *> Q;
2611 for (
auto call : calls)
2614 auto cur = *Q.begin();
2615 Function *outerFunc = cur->getParent()->getParent();
2616 llvm::OptimizationRemarkEmitter ORE(outerFunc);
2618 if (
auto F = cur->getCalledFunction()) {
2621 SmallVector<std::unique_ptr<AssumptionCache>, 2> ACAlloc;
2622 auto getAC = [&](Function &F) -> llvm::AssumptionCache & {
2623 auto AC = std::make_unique<AssumptionCache>(F);
2624 ACAlloc.push_back(std::move(AC));
2625 return *ACAlloc.back();
2628 [&](llvm::Function &F) ->
const llvm::TargetLibraryInfo & {
2629 return Logic.
PPC.
FAM.getResult<TargetLibraryAnalysis>(F);
2632 TargetTransformInfo TTI(F->getParent()->getDataLayout());
2633 auto GetInlineCost = [&](CallBase &CB) {
2634 auto cst = llvm::getInlineCost(CB, Params, TTI, getAC, GetTLI);
2637#if LLVM_VERSION_MAJOR >= 20
2638 if (llvm::shouldInline(*cur, TTI, GetInlineCost, ORE))
2640 if (llvm::shouldInline(*cur, GetInlineCost, ORE))
2643 InlineFunctionInfo IFI;
2644 InlineResult IR = InlineFunction(*cur, IFI);
2645 if (IR.isSuccess()) {
2647 for (
auto U : outerFunc->users()) {
2648 if (
auto CI = dyn_cast<CallInst>(U)) {
2649 if (CI->getCalledFunction() == outerFunc) {
2666#if !defined(FLANG) && !defined(ROCM)
2668 AnalysisGetter AG(Logic.
PPC.
FAM);
2669 SetVector<Function *> Functions;
2670 for (Function &F2 : *F.getParent()) {
2671 Functions.insert(&F2);
2674 CallGraphUpdater CGUpdater;
2675 BumpPtrAllocator Allocator;
2676 InformationCache InfoCache(*F.getParent(), AG, Allocator,
2679 DenseSet<const char *> Allowed = {
2683 &AAMemoryBehavior::ID,
2684 &AAMemoryLocation::ID,
2692 &AADereferenceable::ID,
2694#if LLVM_VERSION_MAJOR < 17
2695 &AAReturnedValues::ID,
2707 AttributorConfig aconfig(CGUpdater);
2708 aconfig.Allowed = &Allowed;
2709 aconfig.DeleteFns =
false;
2710 Attributor A(Functions, InfoCache, aconfig);
2711 for (Function *F : Functions) {
2714 A.identifyDefaultAbstractAttributes(*F);
2723 bool run(Module &M) {
2726 for (Function &F : make_early_inc_range(M)) {
2730 bool changed =
false;
2731 for (Function &F : M) {
2734 for (BasicBlock &BB : F) {
2735 for (Instruction &I : make_early_inc_range(BB)) {
2736 if (
auto CI = dyn_cast<CallInst>(&I)) {
2737 Function *F = CI->getCalledFunction();
2739 dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
2740 if (castinst->isCast())
2741 if (
auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
2745 if (F && F->getName() ==
"f90_mzero8") {
2749 args[0] = CI->getArgOperand(0);
2750 args[1] = ConstantInt::get(Type::getInt8Ty(M.getContext()), 0);
2751 args[2] = B.CreateMul(
2752 CI->getArgOperand(1),
2753 ConstantInt::get(CI->getArgOperand(1)->getType(), 8));
2754 B.CreateMemSet(args[0], args[1], args[2], MaybeAlign());
2756 CI->eraseFromParent();
2764 OpenMPOptPass().run(M, Logic.
PPC.
MAM);
2766 AttributorPass().run(M, Logic.
PPC.
MAM);
2769 PromotePass().run(F, Logic.
PPC.
FAM);
2777 std::set<Function *> done;
2778 for (Function &F : M) {
2782 changed |= lowerEnzymeCalls(F, done);
2785 for (Function &F : M) {
2789 for (BasicBlock &BB : F) {
2790 for (Instruction &I : make_early_inc_range(BB)) {
2791 if (
auto CI = dyn_cast<CallInst>(&I)) {
2792 Function *F = CI->getCalledFunction();
2794 dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
2795 if (castinst->isCast())
2796 if (
auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
2801 if (F->getName().contains(
"__enzyme_float") ||
2802 F->getName().contains(
"__enzyme_double") ||
2803 F->getName().contains(
"__enzyme_integer") ||
2804 F->getName().contains(
"__enzyme_pointer")) {
2805 CI->eraseFromParent();
2808 if (F->getName().contains(
"__enzyme_iter") ||
2809 F->getName().contains(
"__enzyme_ignore_derivatives")) {
2810 CI->replaceAllUsesWith(CI->getArgOperand(0));
2811 CI->eraseFromParent();
2820 SmallPtrSet<CallInst *, 16> sample_calls;
2821 SmallPtrSet<CallInst *, 16> observe_calls;
2822 for (
auto &&func : M) {
2823 for (
auto &&BB : func) {
2824 for (
auto &&Inst : BB) {
2825 if (
auto CI = dyn_cast<CallInst>(&Inst)) {
2826 Function *fun = CI->getCalledFunction();
2830 if (fun->getName().contains(
"__enzyme_sample")) {
2831 if (CI->getNumOperands() < 3) {
2833 "IllegalNumberOfArguments", CI->getDebugLoc(), CI,
2834 "Not enough arguments passed to call to __enzyme_sample");
2838 samplefn->getFunctionType()->getNumParams() + 3;
2839 unsigned actual = CI->arg_size();
2840 if (actual - 3 != samplefn->getFunctionType()->getNumParams()) {
2841 EmitFailure(
"IllegalNumberOfArguments", CI->getDebugLoc(), CI,
2842 "Illegal number of arguments passed to call to "
2844 " Expected: ", expected,
" got: ", actual);
2848 for (
unsigned i = 0;
2849 i < samplefn->getFunctionType()->getNumParams(); ++i) {
2850 Value *ci_arg = CI->getArgOperand(i + 3);
2851 Value *sample_arg = samplefn->arg_begin() + i;
2852 Value *pdf_arg = pdf->arg_begin() + i;
2854 if (ci_arg->getType() != sample_arg->getType()) {
2856 "IllegalSampleType", CI->getDebugLoc(), CI,
2857 "Type of: ", *ci_arg,
" (", *ci_arg->getType(),
")",
2858 " does not match the argument type of the sample "
2860 *samplefn,
" at: ", i,
" (", *sample_arg->getType(),
")");
2862 if (ci_arg->getType() != pdf_arg->getType()) {
2863 EmitFailure(
"IllegalSampleType", CI->getDebugLoc(), CI,
2864 "Type of: ", *ci_arg,
" (", *ci_arg->getType(),
2866 " does not match the argument type of the "
2867 "density function: ",
2868 *pdf,
" at: ", i,
" (", *pdf_arg->getType(),
")");
2872 if ((pdf->arg_end() - 1)->getType() !=
2873 samplefn->getReturnType()) {
2875 "IllegalSampleType", CI->getDebugLoc(), CI,
2876 "Return type of ", *samplefn,
" (",
2877 *samplefn->getReturnType(),
")",
2878 " does not match the last argument type of the density "
2880 *pdf,
" (", *(pdf->arg_end() - 1)->getType(),
")");
2882 sample_calls.insert(CI);
2884 }
else if (fun->getName().contains(
"__enzyme_observe")) {
2885 if (CI->getNumOperands() < 3) {
2887 "IllegalNumberOfArguments", CI->getDebugLoc(), CI,
2888 "Not enough arguments passed to call to __enzyme_sample");
2890 Value *observed = CI->getOperand(0);
2892 unsigned expected = pdf->getFunctionType()->getNumParams() - 1;
2894 unsigned actual = CI->arg_size();
2895 if (actual - 3 != expected) {
2896 EmitFailure(
"IllegalNumberOfArguments", CI->getDebugLoc(), CI,
2897 "Illegal number of arguments passed to call to "
2898 "__enzyme_observe.",
2899 " Expected: ", expected,
" got: ", actual);
2902 for (
unsigned i = 0;
2903 i < pdf->getFunctionType()->getNumParams() - 1; ++i) {
2904 Value *ci_arg = CI->getArgOperand(i + 3);
2905 Value *pdf_arg = pdf->arg_begin() + i;
2907 if (ci_arg->getType() != pdf_arg->getType()) {
2908 EmitFailure(
"IllegalSampleType", CI->getDebugLoc(), CI,
2909 "Type of: ", *ci_arg,
" (", *ci_arg->getType(),
2911 " does not match the argument type of the "
2912 "density function: ",
2913 *pdf,
" at: ", i,
" (", *pdf_arg->getType(),
")");
2917 if ((pdf->arg_end() - 1)->getType() != observed->getType()) {
2919 "IllegalSampleType", CI->getDebugLoc(), CI,
2920 "Return type of ", *observed,
" (", *observed->getType(),
2922 " does not match the last argument type of the density "
2924 *pdf,
" (", *(pdf->arg_end() - 1)->getType(),
")");
2926 observe_calls.insert(CI);
2935 for (
auto call : sample_calls) {
2938 SmallVector<Value *, 2> args;
2939 for (
auto it = call->arg_begin() + 3; it != call->arg_end(); it++) {
2940 args.push_back(*it);
2943 CallInst::Create(samplefn->getFunctionType(), samplefn, args);
2945 ReplaceInstWithInst(call, choice);
2948 for (
auto call : observe_calls) {
2949 Value *observed = call->getArgOperand(0);
2951 if (!call->getType()->isVoidTy())
2952 call->replaceAllUsesWith(observed);
2953 call->eraseFromParent();
2956 for (
const auto &pair : Logic.
PPC.
cache)
2957 pair.second->eraseFromParent();
2960 if (changed && Logic.
PostOpt) {
2961 TimeTraceScope timeScope(
"Enzyme PostOpt", M.getName());
2964 LoopAnalysisManager LAM;
2965 FunctionAnalysisManager FAM;
2966 CGSCCAnalysisManager CGAM;
2967 ModuleAnalysisManager MAM;
2968 PB.registerModuleAnalyses(MAM);
2969 PB.registerFunctionAnalyses(FAM);
2970 PB.registerLoopAnalyses(LAM);
2971 PB.registerCGSCCAnalyses(CGAM);
2972 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
2973 auto PM = PB.buildModuleSimplificationPipeline(OptimizationLevel::O2,
2974 ThinOrFullLTOPhase::None);
2977 OpenMPOptPass().run(M, MAM);
2979 AttributorPass().run(M, MAM);
2982 PromotePass().run(F, FAM);
2994class EnzymeOldPM :
public EnzymeBase,
public ModulePass {
2997 EnzymeOldPM(
bool PostOpt =
false) : EnzymeBase(PostOpt),
ModulePass(ID) {}
2999 void getAnalysisUsage(AnalysisUsage &AU)
const override {
3000 AU.addRequired<TargetLibraryInfoWrapperPass>();
3009 bool runOnModule(Module &M)
override {
return run(M); }
3014char EnzymeOldPM::ID = 0;
3016static RegisterPass<EnzymeOldPM>
X(
"enzyme",
"Enzyme Pass");
3020#include <llvm-c/Core.h>
3021#include <llvm-c/Types.h>
3023#include "llvm/IR/LegacyPassManager.h"
3029#if LLVM_VERSION_MAJOR >= 22
3030#include "llvm/Plugins/PassPlugin.h"
3032#include "llvm/Passes/PassPlugin.h"
3039 static llvm::AnalysisKey Key;
3045 Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
3046 return EnzymeBase::run(M) ? PreservedAnalyses::none()
3047 : PreservedAnalyses::all();
3054AnalysisKey EnzymeNewPM::Key;
3061#include "llvm/Passes/PassBuilder.h"
3062#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h"
3063#include "llvm/Transforms/IPO/AlwaysInliner.h"
3064#include "llvm/Transforms/IPO/CalledValuePropagation.h"
3065#include "llvm/Transforms/IPO/ConstantMerge.h"
3066#include "llvm/Transforms/IPO/CrossDSOCFI.h"
3067#include "llvm/Transforms/IPO/DeadArgumentElimination.h"
3068#include "llvm/Transforms/IPO/FunctionAttrs.h"
3069#include "llvm/Transforms/IPO/GlobalDCE.h"
3070#include "llvm/Transforms/IPO/GlobalOpt.h"
3071#include "llvm/Transforms/IPO/GlobalSplit.h"
3072#include "llvm/Transforms/IPO/InferFunctionAttrs.h"
3073#include "llvm/Transforms/IPO/SCCP.h"
3074#include "llvm/Transforms/InstCombine/InstCombine.h"
3075#include "llvm/Transforms/Scalar/CallSiteSplitting.h"
3076#include "llvm/Transforms/Scalar/EarlyCSE.h"
3077#include "llvm/Transforms/Scalar/Float2Int.h"
3078#include "llvm/Transforms/Scalar/GVN.h"
3079#include "llvm/Transforms/Scalar/LoopDeletion.h"
3080#include "llvm/Transforms/Scalar/LoopRotation.h"
3081#include "llvm/Transforms/Scalar/LoopUnrollPass.h"
3082#include "llvm/Transforms/Scalar/SROA.h"
3084#include "llvm/Transforms/IPO/ArgumentPromotion.h"
3085#include "llvm/Transforms/Scalar/ConstraintElimination.h"
3086#include "llvm/Transforms/Scalar/DeadStoreElimination.h"
3087#include "llvm/Transforms/Scalar/JumpThreading.h"
3088#include "llvm/Transforms/Scalar/MemCpyOptimizer.h"
3089#include "llvm/Transforms/Scalar/NewGVN.h"
3090#include "llvm/Transforms/Scalar/TailRecursionElimination.h"
3091#if LLVM_VERSION_MAJOR >= 17
3092#include "llvm/Transforms/Utils/MoveAutoInit.h"
3094#include "llvm/Transforms/Scalar/IndVarSimplify.h"
3095#include "llvm/Transforms/Scalar/LICM.h"
3096#include "llvm/Transforms/Scalar/LoopFlatten.h"
3097#include "llvm/Transforms/Scalar/MergedLoadStoreMotion.h"
3100#if LLVM_VERSION_MAJOR >= 23
3103 return getInlineParams(Level.getSpeedupLevel(), Level.getSizeLevel());
3107#include "llvm/Transforms/Scalar/LowerConstantIntrinsics.h"
3108#include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h"
3110#if LLVM_VERSION_MAJOR >= 24
3114 return getLicmMssaNoAccForPromotionCap();
3124#define EnableLoopFlatten false
3125#define EagerlyInvalidateAnalyses false
3126#define RunNewGVN false
3127#define EnableConstraintElimination true
3128#define UseInlineAdvisor InliningAdvisorMode::Default
3129#define EnableMemProfContextDisambiguation false
3131#define EnableMatrix false
3132#define EnableModuleInliner false
3137 auto prePass = [](ModulePassManager &MPM, OptimizationLevel Level) {
3138 FunctionPassManager OptimizePM;
3139 OptimizePM.addPass(Float2IntPass());
3140 OptimizePM.addPass(LowerConstantIntrinsicsPass());
3143 OptimizePM.addPass(LowerMatrixIntrinsicsPass());
3144 OptimizePM.addPass(EarlyCSEPass());
3147 LoopPassManager LPM;
3148 bool LTOPreLink =
false;
3151#if LLVM_VERSION_MAJOR >= 23
3152 LPM.addPass(LoopRotatePass(
true, LTOPreLink,
3155 LPM.addPass(LoopRotatePass(Level != OptimizationLevel::Oz, LTOPreLink));
3161 LPM.addPass(LoopDeletionPass());
3163 LPM.addPass(llvm::LoopFullUnrollPass());
3164 OptimizePM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
3166 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizePM)));
3169#if LLVM_VERSION_MAJOR >= 20
3170 auto loadPass = [prePass](ModulePassManager &MPM, OptimizationLevel Level,
3171 ThinOrFullLTOPhase Phase)
3173 auto loadPass = [prePass](ModulePassManager &MPM, OptimizationLevel Level)
3181#if LLVM_VERSION_MAJOR >= 20
3200 (Phase == ThinOrFullLTOPhase::ThinLTOPreLink &&
3201 Level != OptimizationLevel::O0))) {
3209 if (Level != OptimizationLevel::O0)
3210 prePass(MPM, Level);
3211 MPM.addPass(llvm::AlwaysInlinerPass());
3212 FunctionPassManager OptimizerPM;
3213 FunctionPassManager OptimizerPM2;
3214#if LLVM_VERSION_MAJOR >= 16
3215 OptimizerPM.addPass(llvm::GVNPass());
3216 OptimizerPM.addPass(llvm::SROAPass(llvm::SROAOptions::PreserveCFG));
3218 OptimizerPM.addPass(llvm::GVNPass());
3219 OptimizerPM.addPass(llvm::SROAPass());
3221 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizerPM)));
3224#if LLVM_VERSION_MAJOR >= 16
3225 OptimizerPM2.addPass(llvm::GVNPass());
3226 OptimizerPM2.addPass(llvm::SROAPass(llvm::SROAOptions::PreserveCFG));
3228 OptimizerPM2.addPass(llvm::GVNPass());
3229 OptimizerPM2.addPass(llvm::SROAPass());
3232 LoopPassManager LPM1;
3233 LPM1.addPass(LoopDeletionPass());
3234 OptimizerPM2.addPass(createFunctionToLoopPassAdaptor(std::move(LPM1)));
3236 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizerPM2)));
3237 MPM.addPass(GlobalOptPass());
3240 PB.registerOptimizerEarlyEPCallback(
loadPass);
3242 auto loadNVVM = [](ModulePassManager &MPM, OptimizationLevel) {
3249 PB.registerPipelineStartEPCallback(loadNVVM);
3250 PB.registerFullLinkTimeOptimizationEarlyEPCallback(loadNVVM);
3252 auto preLTOPass = [](ModulePassManager &MPM, OptimizationLevel Level) {
3255 MPM.addPass(CrossDSOCFIPass());
3257 if (Level == OptimizationLevel::O0) {
3263#if LLVM_VERSION_MAJOR >= 16
3264 MPM.addPass(OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink));
3266 MPM.addPass(OpenMPOptPass());
3271 MPM.addPass(GlobalDCEPass());
3275 MPM.addPass(InferFunctionAttrsPass());
3277#if LLVM_VERSION_MAJOR >= 23
3278 if (Level > OptimizationLevel::O1)
3280 if (Level.getSpeedupLevel() > 1)
3283 MPM.addPass(createModuleToFunctionPassAdaptor(CallSiteSplittingPass(),
3299#if LLVM_VERSION_MAJOR >= 23
3300 MPM.addPass(IPSCCPPass(IPSCCPOptions(
true)));
3301#elif LLVM_VERSION_MAJOR >= 16
3302 MPM.addPass(IPSCCPPass(IPSCCPOptions(
3303 Level != OptimizationLevel::Os &&
3304 Level != OptimizationLevel::Oz)));
3306 MPM.addPass(IPSCCPPass());
3311 MPM.addPass(CalledValuePropagationPass());
3316 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3322 MPM.addPass(ReversePostOrderFunctionAttrsPass());
3326 MPM.addPass(GlobalSplitPass());
3333 if (Level == OptimizationLevel::O1) {
3338 MPM.addPass(GlobalOptPass());
3341 MPM.addPass(createModuleToFunctionPassAdaptor(PromotePass()));
3345 MPM.addPass(ConstantMergePass());
3348 MPM.addPass(DeadArgumentEliminationPass());
3354 FunctionPassManager PeepholeFPM;
3355 PeepholeFPM.addPass(InstCombinePass());
3356#if LLVM_VERSION_MAJOR >= 23
3357 if (Level > OptimizationLevel::O1)
3359 if (Level.getSpeedupLevel() > 1)
3361 PeepholeFPM.addPass(AggressiveInstCombinePass());
3363 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM),
3373 ThinOrFullLTOPhase::FullLTOPostLink));
3375 MPM.addPass(ModuleInlinerWrapperPass(
3378 InlineContext{ThinOrFullLTOPhase::FullLTOPostLink,
3379 InlinePass::CGSCCInliner}));
3388 MPM.addPass(GlobalOptPass());
3391#if LLVM_VERSION_MAJOR >= 16
3392 MPM.addPass(OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink));
3394 MPM.addPass(OpenMPOptPass());
3398 MPM.addPass(GlobalDCEPass());
3403 createModuleToPostOrderCGSCCPassAdaptor(ArgumentPromotionPass()));
3405 FunctionPassManager FPM;
3407 FPM.addPass(InstCombinePass());
3410 FPM.addPass(ConstraintEliminationPass());
3412 FPM.addPass(JumpThreadingPass());
3418 if (PGOOpt->CSAction == PGOOptions::CSIRInstr)
3419 addPGOInstrPasses(MPM, Level,
true,
3420 true, PGOOpt->CSProfileGenFile,
3421 PGOOpt->ProfileRemappingFile,
3422 ThinOrFullLTOPhase::FullLTOPostLink, PGOOpt->FS);
3423 else if (PGOOpt->CSAction == PGOOptions::CSIRUse)
3424 addPGOInstrPasses(MPM, Level,
false,
3425 true, PGOOpt->ProfileFile,
3426 PGOOpt->ProfileRemappingFile,
3427 ThinOrFullLTOPhase::FullLTOPostLink, PGOOpt->FS);
3432#if LLVM_VERSION_MAJOR >= 16
3433 FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
3435 FPM.addPass(SROAPass());
3440 FPM.addPass(TailCallElimPass());
3443 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
3447 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3451 MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
3454 auto loadLTO = [preLTOPass,
loadPass](ModulePassManager &MPM,
3455 OptimizationLevel Level) {
3456 preLTOPass(MPM, Level);
3458 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3462 MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
3467 createModuleToFunctionPassAdaptor(InvalidateAnalysisPass<AAManager>()));
3469 FunctionPassManager MainFPM;
3470#if LLVM_VERSION_MAJOR >= 22
3471 MainFPM.addPass(createFunctionToLoopPassAdaptor(
3476 MainFPM.addPass(createFunctionToLoopPassAdaptor(
3483 MainFPM.addPass(NewGVNPass());
3485 MainFPM.addPass(GVNPass());
3488 MainFPM.addPass(MemCpyOptPass());
3491 MainFPM.addPass(DSEPass());
3492#if LLVM_VERSION_MAJOR >= 17
3493 MainFPM.addPass(MoveAutoInitPass());
3495 MainFPM.addPass(MergedLoadStoreMotionPass());
3497 LoopPassManager LPM;
3498#if LLVM_VERSION_MAJOR >= 23
3503 LPM.addPass(LoopFlattenPass());
3504 LPM.addPass(IndVarSimplifyPass());
3505 LPM.addPass(LoopDeletionPass());
3508#if LLVM_VERSION_MAJOR >= 20
3509 loadPass(MPM, Level, ThinOrFullLTOPhase::None);
3514 PB.registerFullLinkTimeOptimizationEarlyEPCallback(loadLTO);
3520 bool augment =
false) {
3524 PB.registerPipelineParsingCallback(
3525 [](llvm::StringRef Name, llvm::ModulePassManager &MPM,
3526 llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
3527 if (Name ==
"enzyme") {
3534 if (Name ==
"preserve-nvvm") {
3538 if (Name ==
"preserve-nvvm-end") {
3542 if (Name ==
"print-type-analysis") {
3546 if (Name ==
"print-activity-analysis") {
3552 PB.registerPipelineParsingCallback(
3553 [](llvm::StringRef Name, llvm::FunctionPassManager &FPM,
3554 llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
3555 if (Name ==
"jl-inst-simplify") {
3559 if (Name ==
"simple-gvn") {
3568#ifdef ENZYME_RUNPASS
3575extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
3577 return {LLVM_PLUGIN_API_VERSION,
"EnzymeNewPM",
"v0.1",
registerEnzyme};
DIFFE_TYPE
Potential differentiable argument classifications.
llvm::cl::opt< bool > EnzymeEnable
static void loadPass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM)
#define EagerlyInvalidateAnalyses
#define EnableLoopFlatten
void augmentPassBuilder(llvm::PassBuilder &PB)
::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo()
ModulePass * createEnzymePass(bool PostOpt)
llvm::cl::opt< bool > EnzymeOMPOpt("enzyme-omp-opt", cl::init(false), cl::Hidden, cl::desc("Whether to enable openmp opt"))
static InlineParams getInlineParamsFromOptLevel(OptimizationLevel Level)
void AddEnzymePass(LLVMPassManagerRef PM)
llvm::cl::opt< bool > EnzymeAttributor("enzyme-attributor", cl::init(false), cl::Hidden, cl::desc("Run attributor post Enzyme"))
#define EnableModuleInliner
#define EnableConstraintElimination
llvm::cl::opt< bool > EnzymeDetectReadThrow("enzyme-detect-readthrow", cl::init(true), cl::Hidden, cl::desc("Run preprocessing detect readonly or throw optimization"))
llvm::cl::opt< bool > EnzymeLTOPreLink("enzyme-lto-prelink", cl::init(false), cl::Hidden, cl::desc("Run Enzyme in an LTO pre-link pipeline, rather than deferring " "differentiation to the post-link run"))
void registerEnzyme(llvm::PassBuilder &PB)
void registerEnzymeAndPassPipeline(llvm::PassBuilder &PB, bool augment=false)
llvm::cl::opt< std::string > EnzymeTruncateAll("enzyme-truncate-all", cl::init(""), cl::Hidden, cl::desc("Truncate all floating point operations. " "E.g. \"64to32\" or \"64to<exponent_width>-<significand_width>\"."))
static RegisterPass< EnzymeOldPM > X("enzyme", "Enzyme Pass")
llvm::cl::opt< bool > EnzymePostOpt("enzyme-postopt", cl::init(false), cl::Hidden, cl::desc("Run enzymepostprocessing optimizations"))
bool registerFixupJuliaPass(llvm::StringRef Name, llvm::ModulePassManager &MPM)
bool DetectReadonlyOrThrow(Module &M)
bool LowerSparsification(llvm::Function *F, bool replaceAll)
Lower __enzyme_todense, returning if changed.
constexpr const char * to_string(ActivityAnalyzer::UseActivity UA)
llvm::cl::opt< bool > EnzymePrint
constexpr char EnzymeFPRTPrefix[]
Value * simplifyLoad(Value *V, size_t valSz, size_t preOffset)
bool attributeKnownFunctions(llvm::Function &F)
Function * GetFunctionFromValue(Value *fn)
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
static llvm::StringRef getFuncName(llvm::Function *called)
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
@ Args
Return is a struct of all args.
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
static bool isGPUArch(const llvm::Triple &TT)
static llvm::PointerType * getUnqual(llvm::Type *T)
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 void insertBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
static llvm::Instruction * getFirstNonPHI(llvm::BasicBlock *B)
static void addCallSiteNoCapture(llvm::CallBase *call, size_t idx)
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
static void moveBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
static llvm::Instruction * createUnconditionalBranch(llvm::BasicBlock *Target, PosT InsertBefore)
static DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode, bool integersAreConstant, std::set< llvm::Type * > &seen)
Attempt to automatically detect the differentiable classification based off of a given type.
return structtype if recursive function
llvm::Type * tapeType
return structtype if recursive function
std::map< AugmentedStruct, int > returns
Map from information desired from a augmented return to its index in the returned struct.
Concrete SubType of a given value.
llvm::Function * CreateTruncateFunc(RequestContext context, llvm::Function *tobatch, FloatTruncation truncation, TruncateMode mode)
bool CreateTruncateValue(RequestContext context, llvm::Value *addr, FloatRepresentation from, FloatRepresentation to, bool isTruncate)
const AugmentedReturn & CreateAugmentedPrimal(RequestContext context, llvm::Function *todiff, DIFFE_TYPE retType, llvm::ArrayRef< DIFFE_TYPE > constant_args, TypeAnalysis &TA, bool returnUsed, bool shadowReturnUsed, const FnTypeInfo &typeInfo, bool subsequent_calls_may_write, const std::vector< bool > _overwritten_args, const std::vector< bool > &nowrite_shadows, bool forceAnonymousTape, bool runtimeActivity, bool strongZero, unsigned width, bool AtomicAdd, bool omp=false)
Create an augmented forward pass.
llvm::Function * CreateForwardDiff(RequestContext context, llvm::Function *todiff, DIFFE_TYPE retType, llvm::ArrayRef< DIFFE_TYPE > constant_args, TypeAnalysis &TA, bool returnValue, DerivativeMode mode, bool freeMemory, bool runtimeActivity, bool strongZero, unsigned width, llvm::Type *additionalArg, const FnTypeInfo &typeInfo, bool subsequent_calls_may_write, const std::vector< bool > _overwritten_args, const AugmentedReturn *augmented, bool omp=false)
Create the forward (or forward split) mode derivative function.
llvm::Function * CreateTrace(RequestContext context, llvm::Function *totrace, const llvm::SmallPtrSetImpl< llvm::Function * > &sampleFunctions, const llvm::SmallPtrSetImpl< llvm::Function * > &observeFunctions, const llvm::StringSet<> &ActiveRandomVariables, ProbProgMode mode, bool autodiff, TraceInterface *interface)
Create a traced version of a function context the instruction which requested this trace (or null).
llvm::Function * CreatePrimalAndGradient(RequestContext context, const ReverseCacheKey &&key, TypeAnalysis &TA, const AugmentedReturn *augmented, bool omp=false)
Create the reverse pass, or combined forward+reverse derivative function.
llvm::Function * CreateBatch(RequestContext context, llvm::Function *tobatch, unsigned width, llvm::ArrayRef< BATCH_TYPE > arg_types, BATCH_TYPE ret_type)
Create a function batched in its inputs.
bool PostOpt
PostOpt is whether to perform basic optimization of the function after synthesis
EnzymeNewPM(bool PostOpt=false)
llvm::PreservedAnalyses Result
Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM)
static llvm::Constant * GetOrCreateShadowConstant(RequestContext context, EnzymeLogic &Logic, llvm::TargetLibraryInfo &TLI, TypeAnalysis &TA, llvm::Constant *F, DerivativeMode mode, bool runtimeActivity, bool strongZero, unsigned width, bool AtomicAdd)
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
llvm::ModuleAnalysisManager MAM
std::map< std::pair< llvm::Function *, DerivativeMode >, llvm::Function * > cache
llvm::FunctionAnalysisManager FAM
Full interprocedural TypeAnalysis.
TypeResults analyzeFunction(const FnTypeInfo &fn)
Analyze a particular function, returning the results.
FnTypeInfo getAnalyzedTypeInfo() const
The TypeInfo calling convention.
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
TypeTree Only(int Off, llvm::Instruction *orig) const
Prepend an offset to all mappings.
static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx)
bool insert(const std::vector< int > Seq, ConcreteType CT, bool PointerIntSame=false)
Return if changed.
@ CallFlag
Configures the call rather than an argument: nothing follows it that is an argument.
std::optional< MarkerInfo > lookupEnzymeMarker(llvm::StringRef name)
The one description of the __enzyme_* argument markers.
cl::opt< unsigned > SetLicmMssaOptCap
static unsigned EnzymeLicmMssaNoAccForPromotionCap()
static unsigned EnzymeLicmMssaOptCap()
cl::opt< unsigned > SetLicmMssaNoAccForPromotionCap
Struct containing all contextual type information for a particular function call.
std::map< llvm::Argument *, TypeTree > Arguments
Types of arguments.
llvm::Function * Function
Function being analyzed.
TypeTree Return
Type of return.
std::map< llvm::Argument *, std::set< int64_t > > KnownValues
The specific constant(s) known to represented by an argument, if constant.
todiff is the function to differentiate retType is the activity info of the return.