Enzyme main
Loading...
Searching...
No Matches
Enzyme.cpp
Go to the documentation of this file.
1//===- Enzyme.cpp - Automatic Differentiation Transformation Pass -------===//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file contains Enzyme, a transformation pass that takes replaces calls
22// to function calls to *__enzyme_autodiff* with a call to the derivative of
23// the function passed as the first argument.
24//
25//===----------------------------------------------------------------------===//
26#include <llvm/Config/llvm-config.h>
27#include <memory>
28
29#if LLVM_VERSION_MAJOR >= 16
30#define private public
31#include "llvm/Analysis/ScalarEvolution.h"
32#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
33#undef private
34#else
35#include "SCEV/ScalarEvolution.h"
36#include "SCEV/ScalarEvolutionExpander.h"
37#endif
38
39#include "llvm/ADT/ArrayRef.h"
40#include "llvm/ADT/MapVector.h"
41#include <optional>
42#if LLVM_VERSION_MAJOR <= 16
43#include "llvm/ADT/Optional.h"
44#endif
45#include "llvm/ADT/SetVector.h"
46#include "llvm/ADT/SmallSet.h"
47#include "llvm/ADT/SmallVector.h"
48
49#include "llvm/Passes/PassBuilder.h"
50
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"
59
60#include "llvm/Analysis/ScalarEvolution.h"
61#include "llvm/Support/Debug.h"
62#include "llvm/Support/ErrorHandling.h"
63#include "llvm/Transforms/Scalar.h"
64
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"
75
76#include "ActivityAnalysis.h"
77#include "DiffeGradientUtils.h"
78#include "EnzymeCallMarkers.h"
79#include "EnzymeLogic.h"
80#include "GradientUtils.h"
81#include "PassUtils.h"
82#include "TraceInterface.h"
83#include "TraceUtils.h"
84#include "Utils.h"
85
86#include "InstructionBatcher.h"
87
88#include "llvm/Transforms/Utils.h"
89
90#include "llvm/Transforms/IPO/Attributor.h"
91#include "llvm/Transforms/IPO/OpenMPOpt.h"
92#include "llvm/Transforms/Utils/Mem2Reg.h"
93
94#include "CApi.h"
95using namespace llvm;
96#ifdef DEBUG_TYPE
97#undef DEBUG_TYPE
98#endif
99#define DEBUG_TYPE "lower-enzyme-intrinsic"
100
101llvm::cl::opt<bool> EnzymeEnable("enzyme-enable", cl::init(true), cl::Hidden,
102 cl::desc("Run the Enzyme pass"));
103
104llvm::cl::opt<bool> EnzymeLTOPreLink(
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"));
108
109llvm::cl::opt<bool>
110 EnzymePostOpt("enzyme-postopt", cl::init(false), cl::Hidden,
111 cl::desc("Run enzymepostprocessing optimizations"));
112
113llvm::cl::opt<bool> EnzymeAttributor("enzyme-attributor", cl::init(false),
114 cl::Hidden,
115 cl::desc("Run attributor post Enzyme"));
116
117llvm::cl::opt<bool> EnzymeOMPOpt("enzyme-omp-opt", cl::init(false), cl::Hidden,
118 cl::desc("Whether to enable openmp opt"));
119
120llvm::cl::opt<bool> EnzymeDetectReadThrow(
121 "enzyme-detect-readthrow", cl::init(true), cl::Hidden,
122 cl::desc("Run preprocessing detect readonly or throw optimization"));
123
124llvm::cl::opt<std::string> EnzymeTruncateAll(
125 "enzyme-truncate-all", cl::init(""), cl::Hidden,
126 cl::desc(
127 "Truncate all floating point operations. "
128 "E.g. \"64to32\" or \"64to<exponent_width>-<significand_width>\"."));
129
130#define addAttribute addAttributeAtIndex
131#define getAttribute getAttributeAtIndex
132
133namespace {
134static Value *
135castToDiffeFunctionArgType(IRBuilder<> &Builder, llvm::CallInst *CI,
136 llvm::FunctionType *FT, llvm::Type *destType,
137 unsigned int i, DerivativeMode mode,
138 llvm::Value *value, unsigned int truei) {
139 auto res = value;
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(
146 res, getPointerType(ptr->getPointerElementType(),
147 PT->getAddressSpace()));
148 } else {
149 res = Builder.CreateAddrSpaceCast(res, PT);
150 }
151#else
152 res = Builder.CreateAddrSpaceCast(res, PT);
153#endif
154 assert(value);
155 assert(destType);
156 assert(FT);
157 llvm::errs() << "Warning cast(2) __enzyme_autodiff argument " << i
158 << " " << *res << "|" << *res->getType() << " to argument "
159 << truei << " " << *destType << "\n"
160 << "orig: " << *FT << "\n";
161 return res;
162 }
163 }
164 }
165
166 if (!res->getType()->canLosslesslyBitCastTo(destType)) {
167 assert(value);
168 assert(value->getType());
169 assert(destType);
170 assert(FT);
171 auto loc = CI->getDebugLoc();
172 if (auto arg = dyn_cast<Instruction>(res)) {
173 loc = arg->getDebugLoc();
174 }
175 EmitFailure("IllegalArgCast", loc, CI,
176 "Cannot cast __enzyme_autodiff shadow argument ", i, ", found ",
177 *res, ", type ", *res->getType(), " - to arg ", truei, " ",
178 *destType);
179 return nullptr;
180 }
181 return Builder.CreateBitCast(value, destType);
182}
183
184#if LLVM_VERSION_MAJOR > 16
185static std::optional<StringRef> getMetadataName(llvm::Value *res);
186#else
187static Optional<StringRef> getMetadataName(llvm::Value *res);
188#endif
189
190// if all phi arms are (recursively) based on the same metaString, use that
191#if LLVM_VERSION_MAJOR > 16
192static std::optional<StringRef> recursePhiReads(PHINode *val)
193#else
194static Optional<StringRef> recursePhiReads(PHINode *val)
195#endif
196{
197#if LLVM_VERSION_MAJOR > 16
198 std::optional<StringRef> finalMetadata;
199#else
200 Optional<StringRef> finalMetadata;
201#endif
202 SmallVector<PHINode *, 1> todo = {val};
203 SmallSet<PHINode *, 1> done;
204 while (todo.size()) {
205 auto phiInst = todo.back();
206 todo.pop_back();
207 if (done.count(phiInst))
208 continue;
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)) {
213 todo.push_back(phi);
214 } else {
215 auto metaString = getMetadataName(newVal);
216 if (metaString) {
217 if (!finalMetadata) {
218 finalMetadata = metaString;
219 } else if (finalMetadata != metaString) {
220 return {};
221 }
222 }
223 }
224 }
225 }
226 return finalMetadata;
227}
228
229#if LLVM_VERSION_MAJOR > 16
230std::optional<StringRef> getMetadataName(llvm::Value *res)
231#else
232Optional<StringRef> getMetadataName(llvm::Value *res)
233#endif
234{
235 if (auto S = simplifyLoad(res))
236 return getMetadataName(S);
237
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))) {
242 GlobalVariable *gv =
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() &&
248 isa<GlobalVariable>(
249 cast<ConstantExpr>(cast<LoadInst>(res)->getOperand(0))
250 ->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));
268 }
269
270 return {};
271}
272
273static Value *adaptReturnedVector(Value *ret, Value *diffret,
274 IRBuilder<> &Builder, unsigned width) {
275 Type *returnType = ret->getType();
276
277 if (StructType *sty = dyn_cast<StructType>(returnType)) {
278 Value *agg = ConstantAggregateZero::get(sty);
279
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});
286 }
287 } else {
288 agg = Builder.CreateInsertValue(agg, elem, {i});
289 }
290 }
291 diffret = agg;
292 }
293 return diffret;
294}
295
296static bool ReplaceOriginalCall(IRBuilder<> &Builder, Value *ret,
297 Type *retElemType, Value *diffret,
298 Instruction *CI, DerivativeMode mode) {
299 Type *retType = ret->getType();
300 Type *diffretType = diffret->getType();
301 auto &DL = CI->getModule()->getDataLayout();
302
303 if (diffretType->isEmptyTy() || diffretType->isVoidTy() ||
304 retType->isEmptyTy() || retType->isVoidTy()) {
305 CI->replaceAllUsesWith(UndefValue::get(CI->getType()));
306 CI->eraseFromParent();
307 return true;
308 }
309
310 if (retType == diffretType) {
311 CI->replaceAllUsesWith(diffret);
312 CI->eraseFromParent();
313 return true;
314 }
315
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});
323 }
324 CI->replaceAllUsesWith(newStruct);
325 CI->eraseFromParent();
326 return true;
327 }
328
329 if (isa<PointerType>(retType)) {
330 retType = retElemType;
331
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);
338 }
339 CI->eraseFromParent();
340 return true;
341 }
342
343 if (DL.getTypeSizeInBits(retType) >= DL.getTypeSizeInBits(diffretType)) {
344 Builder.CreateStore(
345 diffret, Builder.CreatePointerCast(ret, getUnqual(diffretType)));
346 CI->eraseFromParent();
347 return true;
348 }
349 }
350
352 DL.getTypeSizeInBits(retType) >= DL.getTypeSizeInBits(diffretType)) ||
353 ((mode == DerivativeMode::ForwardMode ||
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();
363 return true;
364 }
365
367 diffret->getType()->isAggregateType()) {
368 auto diffreti = Builder.CreateExtractValue(diffret, {0});
369 if (diffreti->getType() == retType) {
370 CI->replaceAllUsesWith(diffreti);
371 CI->eraseFromParent();
372 return true;
373 } else if (diffretType == retType) {
374 CI->replaceAllUsesWith(diffret);
375 CI->eraseFromParent();
376 return true;
377 }
378 }
379
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");
386 return false;
387}
388
389class EnzymeBase {
390public:
391 EnzymeLogic Logic;
392 EnzymeBase(bool PostOpt)
393 : Logic(EnzymePostOpt.getNumOccurrences() ? EnzymePostOpt : PostOpt) {
394 // initializeLowerAutodiffIntrinsicPass(*PassRegistry::getPassRegistry());
395 }
396
397 Function *parseFunctionParameter(CallInst *CI) {
398 Value *fn = CI->getArgOperand(0);
399
400 // determine function to differentiate
401 if (CI->hasStructRetAttr()) {
402 fn = CI->getArgOperand(1);
403 }
404
405 Value *ofn = fn;
406 fn = GetFunctionFromValue(fn);
407
408 if (!fn || !isa<Function>(fn)) {
409 assert(ofn);
410 EmitFailure("NoFunctionToDifferentiate", CI->getDebugLoc(), CI,
411 "failed to find fn to differentiate", *CI, " - found - ",
412 *ofn);
413 return nullptr;
414 }
415 if (cast<Function>(fn)->empty()) {
416 EmitFailure("EmptyFunctionToDifferentiate", CI->getDebugLoc(), CI,
417 "failed to find fn to differentiate", *CI, " - found - ",
418 *fn);
419 return nullptr;
420 }
421
422 return cast<Function>(fn);
423 }
424
425#if LLVM_VERSION_MAJOR > 16
426 static std::optional<unsigned> parseWidthParameter(CallInst *CI)
427#else
428 static Optional<unsigned> parseWidthParameter(CallInst *CI)
429#endif
430 {
431 unsigned width = 1;
432
433 for (auto [i, found] = std::tuple{0u, false}; i < CI->arg_size(); ++i) {
434 Value *arg = CI->getArgOperand(i);
435
436 if (auto MDName = getMetadataName(arg)) {
437 if (*MDName == "enzyme_width") {
438 if (found) {
439 EmitFailure("IllegalVectorWidth", CI->getDebugLoc(), CI,
440 "vector width declared more than once",
441 *CI->getArgOperand(i), " in", *CI);
442 return {};
443 }
444
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);
449 return {};
450 }
451
452 Value *width_arg = CI->getArgOperand(i + 1);
453 if (auto cint = dyn_cast<ConstantInt>(width_arg)) {
454 width = cint->getZExtValue();
455 found = true;
456 } else {
457 EmitFailure("IllegalVectorWidth", CI->getDebugLoc(), CI,
458 "enzyme_width must be a constant integer",
459 *CI->getArgOperand(i), " in", *CI);
460 return {};
461 }
462
463 if (!found) {
464 EmitFailure("IllegalVectorWidth", CI->getDebugLoc(), CI,
465 "illegal enzyme vector argument width ",
466 *CI->getArgOperand(i), " in", *CI);
467 return {};
468 }
469 }
470 }
471 }
472 return width;
473 }
474
475 struct Options {
476 Value *differet;
477 Value *tape;
478 Value *dynamic_interface;
479 Value *trace;
480 Value *observations;
481 Value *likelihood;
482 Value *diffeLikelihood;
483 unsigned width;
484 int allocatedTapeSize;
485 bool freeMemory;
486 bool returnUsed;
487 bool tapeIsPointer;
488 bool differentialReturn;
489 bool diffeTrace;
490 DIFFE_TYPE retType;
491 bool primalReturn;
492 StringSet<> ActiveRandomVariables;
493 std::vector<bool> overwritten_args;
494 bool runtimeActivity;
495 bool strongZero;
496 bool subsequent_calls_may_write;
497 };
498
499#if LLVM_VERSION_MAJOR > 16
500 static std::optional<Options>
501 handleArguments(IRBuilder<> &Builder, CallInst *CI, Function *fn,
502 DerivativeMode mode, bool sizeOnly,
503 std::vector<DIFFE_TYPE> &constants,
504 SmallVectorImpl<Value *> &args, std::map<int, Type *> &byVal)
505#else
506 static Optional<Options>
507 handleArguments(IRBuilder<> &Builder, CallInst *CI, Function *fn,
508 DerivativeMode mode, bool sizeOnly,
509 std::vector<DIFFE_TYPE> &constants,
510 SmallVectorImpl<Value *> &args, std::map<int, Type *> &byVal)
511#endif
512 {
513 FunctionType *FT = fn->getFunctionType();
514
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;
522 unsigned width = 1;
523 int allocatedTapeSize = -1;
524 bool freeMemory = true;
525 bool tapeIsPointer = false;
526 bool diffeTrace = false;
527 unsigned truei = 0;
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;
537
538 DIFFE_TYPE retType = whatType(fn->getReturnType(), mode);
539
540 if (fn->hasParamAttribute(0, Attribute::StructRet)) {
541 Type *Ty = nullptr;
542 Ty = fn->getParamAttribute(0, Attribute::StructRet).getValueAsType();
543 if (whatType(Ty, mode) != DIFFE_TYPE::CONSTANT) {
544 retType = DIFFE_TYPE::DUP_ARG;
545 }
546 }
547
548 bool returnUsed =
549 !fn->getReturnType()->isVoidTy() && !fn->getReturnType()->isEmptyTy();
550
551 bool sret = CI->hasStructRetAttr() ||
552 fn->hasParamAttribute(0, Attribute::StructRet);
553
554 std::vector<bool> overwritten_args(
555 fn->getFunctionType()->getNumParams(),
557
558 for (unsigned i = 1 + sret; i < CI->arg_size(); ++i) {
559 Value *res = CI->getArgOperand(i);
560 auto metaString = getMetadataName(res);
561 // handle metadata
562 if (metaString && startsWith(*metaString, "enzyme_")) {
563 if (*metaString == "enzyme_const_return") {
564 retType = DIFFE_TYPE::CONSTANT;
565 continue;
566 } else if (*metaString == "enzyme_active_return") {
567 retType = DIFFE_TYPE::OUT_DIFF;
568 continue;
569 } else if (*metaString == "enzyme_dup_return") {
570 retType = DIFFE_TYPE::DUP_ARG;
571 continue;
572 } else if (*metaString == "enzyme_noret") {
573 returnUsed = false;
574 continue;
575 } else if (*metaString == "enzyme_primal_return") {
576 primalReturn = true;
577 continue;
578 }
579 }
580 }
581 bool differentialReturn = (mode == DerivativeMode::ReverseModeCombined ||
583 (retType == DIFFE_TYPE::OUT_DIFF);
584
585 // find and handle enzyme_width
586 if (auto parsedWidth = parseWidthParameter(CI)) {
587 width = *parsedWidth;
588 } else {
589 return {};
590 }
591
592 // handle different argument order for struct return.
593 if (fn->hasParamAttribute(0, Attribute::StructRet)) {
594 truei = 1;
595
596 const DataLayout &DL = CI->getParent()->getModule()->getDataLayout();
597 Type *Ty = nullptr;
598 Ty = fn->getParamAttribute(0, Attribute::StructRet).getValueAsType();
599 Type *CTy = nullptr;
600 CTy = CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
601 .getValueAsType();
602 auto FnSize = (DL.getTypeSizeInBits(Ty) / 8);
603 auto CSize = CTy ? (DL.getTypeSizeInBits(CTy) / 8) : 0;
604 auto count = ((mode == DerivativeMode::ForwardMode ||
607 (retType == DIFFE_TYPE::DUP_ARG ||
608 retType == DIFFE_TYPE::DUP_NONEED)) *
609 width +
610 primalReturn;
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);
617 }
618 Value *primal = nullptr;
619 if (primalReturn) {
620 Value *sretPt = CI->getArgOperand(0);
621 PointerType *pty = cast<PointerType>(sretPt->getType());
622 primal = Builder.CreatePointerCast(
623 sretPt, getPointerType(Ty, pty->getAddressSpace()));
624 } else {
625 AllocaInst *primalA = new AllocaInst(Ty, DL.getAllocaAddrSpace(),
626 nullptr, DL.getPrefTypeAlign(Ty));
627 insertBeforeInst(primalA, CI);
628 primal = primalA;
629 }
630
631 Value *shadow = nullptr;
632 switch (mode) {
636 if (retType != DIFFE_TYPE::CONSTANT) {
637 Value *sretPt = CI->getArgOperand(0);
638 PointerType *pty = cast<PointerType>(sretPt->getType());
639 auto shadowPtr = Builder.CreatePointerCast(
640 sretPt, getPointerType(Ty, pty->getAddressSpace()));
641 if (width == 1) {
642 if (primalReturn)
643 shadowPtr = Builder.CreateConstGEP1_64(Ty, shadowPtr, 1);
644 shadow = shadowPtr;
645 } else {
646 Value *acc = UndefValue::get(ArrayType::get(
647 getPointerType(Ty, pty->getAddressSpace()), width));
648 for (size_t i = 0; i < width; ++i) {
649 Value *elem =
650 Builder.CreateConstGEP1_64(Ty, shadowPtr, i + primalReturn);
651 acc = Builder.CreateInsertValue(acc, elem, i);
652 }
653 shadow = acc;
654 }
655 }
656 break;
657 }
661 if (retType != DIFFE_TYPE::CONSTANT)
662 shadow = CI->getArgOperand(1);
663 sret = true;
664 break;
665 }
666 }
667
668 args.push_back(primal);
669 if (retType != DIFFE_TYPE::CONSTANT)
670 args.push_back(shadow);
671 if (retType == DIFFE_TYPE::DUP_ARG && !primalReturn && isWriteOnly(fn, 0))
672 retType = DIFFE_TYPE::DUP_NONEED;
673 constants.push_back(retType);
674 retType = DIFFE_TYPE::CONSTANT;
675 primalReturn = false;
676 }
677
678 ssize_t interleaved = -1;
679
680 size_t maxsize;
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") {
688 maxsize = i;
689 interleaved = i + 1;
690 break;
691 }
692 }
693 }
694
696
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;
703#else
704 Optional<Value *> batchOffset;
705 Optional<DIFFE_TYPE> opt_ty;
706#endif
707
708 bool overwritten = !(mode == DerivativeMode::ReverseModeCombined);
709
710 bool skipArg = false;
711
712 // handle metadata
713 while (metaString && startsWith(*metaString, "enzyme_")) {
714 // What a marker names and what it takes for itself are described once,
715 // in EnzymeCallMarkers.h, so that the MLIR raising of the same call
716 // reads it the same way. Only what a marker then does with what it
717 // took is particular to it.
718 auto marker = enzyme_markers::lookupEnzymeMarker(*metaString);
719 if (!marker) {
720 EmitFailure("IllegalDiffeType", CI->getDebugLoc(), CI,
721 "illegal enzyme metadata classification ", *CI,
722 *metaString);
723 return {};
724 }
725
726 if (marker->activity)
727 opt_ty = *marker->activity;
728
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);
733 return {};
734 }
735 i += marker->extraOperands;
736 }
737
738 // A flag configures the call; nothing after it is an argument.
739 skipArg = marker->role == enzyme_markers::MarkerRole::CallFlag;
740
741 if (*metaString == "enzyme_not_overwritten") {
742 overwritten = false;
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),
747 "in", *CI);
748 return {};
749 }
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;
757 } else {
758 EmitFailure("IllegalVectorOffset", CI->getDebugLoc(), CI,
759 "enzyme_batch must be followd by an integer "
760 "offset.",
761 *CI->getArgOperand(i), " in", *CI);
762 return {};
763 }
764 } else if (*metaString == "enzyme_allocated") {
765 assert(!sizeOnly);
766 if (!isa<ConstantInt>(CI->getArgOperand(i))) {
767 EmitFailure("IllegalAllocatedSize", CI->getDebugLoc(), CI,
768 "illegal enzyme allocated size ", *CI->getArgOperand(i),
769 "in", *CI);
770 return {};
771 }
772 allocatedTapeSize =
773 cast<ConstantInt>(CI->getArgOperand(i))->getZExtValue();
774 } else if (*metaString == "enzyme_tape") {
775 assert(!sizeOnly);
776 tape = CI->getArgOperand(i);
777 tapeIsPointer = true;
778 } else if (*metaString == "enzyme_nofree") {
779 assert(!sizeOnly);
780 freeMemory = false;
781 } else if (*metaString == "enzyme_runtime_activity") {
782 runtimeActivity = true;
783 } else if (*metaString == "enzyme_strong_zero") {
784 strongZero = true;
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);
791 diffeTrace = true;
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);
804 } else {
806 "IllegalStringType", CI->getDebugLoc(), CI,
807 "active variable address must be a compile-time constant", *CI,
808 *metaString);
809 }
810 }
811 // The rest -- the activities, enzyme_interleave, enzyme_width and the
812 // *_return flags -- are wholly described by the table.
813
814 if (skipArg)
815 break;
816
817 if (sizeOnly) {
818 assert(opt_ty);
819 constants.push_back(*opt_ty);
820 truei++;
821 skipArg = true;
822 break;
823 }
824 ++i;
825 if (i == CI->arg_size()) {
826 EmitFailure("EnzymeCallingError", CI->getDebugLoc(), CI,
827 "Too few arguments to Enzyme call ", *CI);
828 return {};
829 }
830 res = CI->getArgOperand(i);
831 metaString = getMetadataName(res);
832 }
833
834 if (skipArg)
835 continue;
836
837 if (byRefSize) {
838 Type *subTy = nullptr;
839 if (truei < FT->getNumParams()) {
840 subTy = FT->getParamType(i);
841 } else if ((mode == DerivativeMode::ReverseModeGradient ||
843 if (differentialReturn && differet == nullptr) {
844 subTy = FT->getReturnType();
845 }
846 }
847
848 if (!subTy) {
849 EmitFailure("IllegalByVal", CI->getDebugLoc(), CI,
850 "illegal enzyme byval arg", truei, " ", *res);
851 return {};
852 }
853
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,
860 " (bits) in ", *CI);
861 }
862 res = Builder.CreateBitCast(
863 res,
865 subTy, cast<PointerType>(res->getType())->getAddressSpace()));
866 res = Builder.CreateLoad(subTy, res);
867 byRefSize = 0;
868 }
869
870 if (truei >= FT->getNumParams()) {
871 if (!isa<MetadataAsValue>(res) &&
874 if (differentialReturn && differet == nullptr) {
875 differet = res;
876 if (CI->paramHasAttr(i, Attribute::ByVal)) {
877 Type *T = nullptr;
878 T = CI->getParamAttr(i, Attribute::ByVal).getValueAsType();
879 differet = Builder.CreateLoad(T, differet);
880 }
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()
886 ->getParent()
887 ->getEntryBlock()
888 .front());
889 auto AI = B.CreateAlloca(ST1);
890 Builder.CreateStore(differet, Builder.CreatePointerCast(
891 AI, getUnqual(ST0)));
892 differet = Builder.CreateLoad(ST1, AI);
893 }
894
895 if (differet->getType() !=
896 GradientUtils::getShadowType(fn->getReturnType(), width)) {
897 EmitFailure("BadDiffRet", CI->getDebugLoc(), CI,
898 "Bad DiffRet type ", *differet, " expected ",
899 *fn->getReturnType());
900 return {};
901 }
902 continue;
903 } else if (tape == nullptr) {
904 tape = res;
905 if (CI->paramHasAttr(i, Attribute::ByVal)) {
906 Type *T = nullptr;
907 T = CI->getParamAttr(i, Attribute::ByVal).getValueAsType();
908 tape = Builder.CreateLoad(T, tape);
909 }
910 continue;
911 }
912 }
913 EmitFailure("TooManyArgs", CI->getDebugLoc(), CI,
914 "Had too many arguments to __enzyme_autodiff", *CI,
915 " - extra arg - ", *res);
916 return {};
917 }
918 assert(truei < FT->getNumParams());
919 overwritten_args[truei] = overwritten;
920
921 auto PTy = FT->getParamType(truei);
922 DIFFE_TYPE ty =
923 opt_ty ? *opt_ty
924 : ((interleaved == -1) ? whatType(PTy, mode) : last_ty);
925 last_ty = ty;
926
927 constants.push_back(ty);
928
929 assert(truei < FT->getNumParams());
930 // cast primal
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(
938 res, getPointerType(ptr->getPointerElementType(),
939 PT->getAddressSpace()));
940 } else {
941 res = Builder.CreateAddrSpaceCast(res, PT);
942 }
943#else
944 res = Builder.CreateAddrSpaceCast(res, PT);
945#endif
946 assert(res);
947 assert(PTy);
948 assert(FT);
949 llvm::errs() << "Warning cast(1) __enzyme_autodiff argument " << i
950 << " " << *res << "|" << *res->getType()
951 << " to argument " << truei << " " << *PTy << "\n"
952 << "orig: " << *FT << "\n";
953 }
954 }
955 }
956 if (res->getType()->canLosslesslyBitCastTo(PTy)) {
957 res = Builder.CreateBitCast(res, PTy);
958 }
959 if (res->getType() != PTy && res->getType()->isIntegerTy() &&
960 PTy->isIntegerTy(1)) {
961 res = Builder.CreateTrunc(res, PTy);
962 }
963 if (res->getType() != PTy) {
964 auto loc = CI->getDebugLoc();
965 if (auto arg = dyn_cast<Instruction>(res)) {
966 loc = arg->getDebugLoc();
967 }
968 auto S = simplifyLoad(res);
969 if (!S)
970 S = res;
971 EmitFailure("IllegalArgCast", loc, CI,
972 "Cannot cast __enzyme_autodiff primal argument ", i,
973 ", found ", *res, ", type ", *res->getType(),
974 " (simplified to ", *S, " ) ", " - to arg ", truei, ", ",
975 *PTy);
976 return {};
977 }
978 }
979 if (CI->isByValArgument(i)) {
980 byVal[args.size()] = CI->getParamByValType(i);
981 }
982
983 args.push_back(res);
984 if (ty == DIFFE_TYPE::DUP_ARG || ty == DIFFE_TYPE::DUP_NONEED) {
985 if (interleaved == -1)
986 ++i;
987
988 Value *res = nullptr;
989#if LLVM_VERSION_MAJOR >= 16
990 bool batch = batchOffset.has_value();
991#else
992 bool batch = batchOffset.hasValue();
993#endif
994
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(),
1002 " at call ", *CI);
1003 return {};
1004 }
1005
1006 // cast diffe
1007 Value *element =
1008 CI->getArgOperand((interleaved == -1) ? i : interleaved);
1009 if (batch) {
1010 if (auto elementPtrTy = dyn_cast<PointerType>(element->getType())) {
1011 element = Builder.CreateBitCast(
1012 element, getPointerType(Type::getInt8Ty(CI->getContext()),
1013 elementPtrTy->getAddressSpace()));
1014 element = Builder.CreateGEP(
1015 Type::getInt8Ty(CI->getContext()), element,
1016 Builder.CreateMul(
1017 *batchOffset,
1018 ConstantInt::get((*batchOffset)->getType(), v)));
1019 element = Builder.CreateBitCast(element, elementPtrTy);
1020 } else {
1022 "NonPointerBatch", CI->getDebugLoc(), CI,
1023 "Batched argument at index ",
1024 *((interleaved == -1) ? &i : &interleaved),
1025 " must be of pointer type, found: ", *element->getType());
1026 return {};
1027 }
1028 }
1029 if (PTy != element->getType()) {
1030 element = castToDiffeFunctionArgType(
1031 Builder, CI, FT, PTy, (interleaved == -1) ? i : interleaved,
1032 mode, element, truei);
1033 if (!element) {
1034 return {};
1035 }
1036 }
1037
1038 if (width > 1) {
1039 res =
1040 res ? Builder.CreateInsertValue(res, element, {v})
1041 : Builder.CreateInsertValue(UndefValue::get(ArrayType::get(
1042 element->getType(), width)),
1043 element, {v});
1044
1045 if (v < width - 1 && !batch && (interleaved == -1)) {
1046 ++i;
1047 }
1048
1049 } else {
1050 res = element;
1051 }
1052
1053 if (interleaved != -1)
1054 interleaved++;
1055 }
1056
1057 args.push_back(res);
1058 }
1059
1060 ++truei;
1061 }
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);
1068 return {};
1069 }
1070
1071 return Options({differet,
1072 tape,
1073 dynamic_interface,
1074 trace,
1075 observations,
1076 likelihood,
1077 diffeLikelihood,
1078 width,
1079 allocatedTapeSize,
1080 freeMemory,
1081 returnUsed,
1082 tapeIsPointer,
1083 differentialReturn,
1084 diffeTrace,
1085 retType,
1086 primalReturn,
1087 ActiveRandomVariables,
1088 overwritten_args,
1089 runtimeActivity,
1090 strongZero,
1091 subsequent_calls_may_write});
1092 }
1093
1094 static FnTypeInfo populate_type_args(TypeAnalysis &TA, llvm::Function *fn,
1095 DerivativeMode mode) {
1096 FnTypeInfo type_args(fn);
1097 for (auto &a : type_args.Function->args()) {
1098 TypeTree dt;
1099 bool parsed = false;
1100 if (fn->getAttributes().hasParamAttr(a.getArgNo(), "enzyme_type")) {
1101 auto attr =
1102 fn->getAttributes().getParamAttr(a.getArgNo(), "enzyme_type");
1103 dt = TypeTree::parse(attr.getValueAsString(), fn->getContext());
1104 parsed = true;
1105 } else if (a.getType()->isFPOrFPVectorTy()) {
1106 dt = ConcreteType(a.getType()->getScalarType());
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()) {
1112 dt = TypeTree(ConcreteType(et->getScalarType())).Only(-1, nullptr);
1113 } else if (et->isPointerTy()) {
1114 dt = TypeTree(ConcreteType(BaseType::Pointer)).Only(-1, nullptr);
1115 }
1116 }
1117#endif
1118 dt.insert({}, BaseType::Pointer);
1119 } else if (a.getType()->isIntOrIntVectorTy()) {
1121 }
1122 type_args.Arguments.insert(std::pair<Argument *, TypeTree>(
1123 &a, parsed ? dt : dt.Only(-1, nullptr)));
1124 // TODO note that here we do NOT propagate constants in type info (and
1125 // should consider whether we should)
1126 type_args.KnownValues.insert(
1127 std::pair<Argument *, std::set<int64_t>>(&a, {}));
1128 }
1129 TypeTree dt;
1130 bool parsed = false;
1131 if (fn->getAttributes().hasRetAttr("enzyme_type")) {
1132 auto attr = fn->getAttributes().getAttribute(AttributeList::ReturnIndex,
1133 "enzyme_type");
1134 dt = TypeTree::parse(attr.getValueAsString(), fn->getContext());
1135 parsed = true;
1136 } else if (fn->getReturnType()->isFPOrFPVectorTy()) {
1137 dt = ConcreteType(fn->getReturnType()->getScalarType());
1138 }
1139 type_args.Return = parsed ? dt : dt.Only(-1, nullptr);
1140
1141 type_args = TA.analyzeFunction(type_args).getAnalyzedTypeInfo();
1142 return type_args;
1143 }
1144
1145 static FloatRepresentation getDefaultFloatRepr(unsigned width) {
1146 switch (width) {
1147 case 16:
1148 return FloatRepresentation(5, 10);
1149 case 32:
1150 return FloatRepresentation(8, 23);
1151 case 64:
1152 return FloatRepresentation(11, 52);
1153 default:
1154 llvm_unreachable("Invalid float width");
1155 }
1156 };
1157
1158 bool HandleTruncateFunc(CallInst *CI, TruncateMode mode) {
1159 IRBuilder<> Builder(CI);
1160 Function *F = parseFunctionParameter(CI);
1161 if (!F)
1162 return false;
1163 unsigned ArgSize = CI->arg_size();
1164 if (ArgSize != 4 && ArgSize != 3) {
1165 EmitFailure("TooManyArgs", CI->getDebugLoc(), CI,
1166 "Had incorrect number of args to __enzyme_truncate_func", *CI,
1167 " - expected 3 or 4");
1168 return false;
1169 }
1170 FloatTruncation truncation = [&]() -> FloatTruncation {
1171 if (ArgSize == 3) {
1172 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1173 assert(Cfrom);
1174 auto Cto = cast<ConstantInt>(CI->getArgOperand(2));
1175 assert(Cto);
1176 return FloatTruncation(
1177 getDefaultFloatRepr((unsigned)Cfrom->getValue().getZExtValue()),
1178 getDefaultFloatRepr((unsigned)Cto->getValue().getZExtValue()),
1179 mode);
1180 } else if (ArgSize == 4) {
1181 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1182 assert(Cfrom);
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);
1187 return FloatTruncation(
1188 getDefaultFloatRepr((unsigned)Cfrom->getValue().getZExtValue()),
1190 (unsigned)Cto_exponent->getValue().getZExtValue(),
1191 (unsigned)Cto_significand->getValue().getZExtValue()),
1192 mode);
1193 }
1194 llvm_unreachable("??");
1195 }();
1196
1197 RequestContext context(CI, &Builder);
1198 llvm::Value *res = Logic.CreateTruncateFunc(context, F, truncation, mode);
1199 if (!res)
1200 return false;
1201 res = Builder.CreatePointerCast(res, CI->getType());
1202 CI->replaceAllUsesWith(res);
1203 CI->eraseFromParent();
1204 return true;
1205 }
1206
1207 bool HandleTruncateValue(CallInst *CI, bool isTruncate) {
1208 IRBuilder<> Builder(CI);
1209 if (CI->arg_size() != 3) {
1210 EmitFailure("TooManyArgs", CI->getDebugLoc(), CI,
1211 "Had incorrect number of args to __enzyme_truncate_value",
1212 *CI, " - expected 3");
1213 return false;
1214 }
1215 auto Cfrom = cast<ConstantInt>(CI->getArgOperand(1));
1216 assert(Cfrom);
1217 auto Cto = cast<ConstantInt>(CI->getArgOperand(2));
1218 assert(Cto);
1219 auto Addr = CI->getArgOperand(0);
1220 RequestContext context(CI, &Builder);
1221 bool res = Logic.CreateTruncateValue(
1222 context, Addr,
1223 getDefaultFloatRepr((unsigned)Cfrom->getValue().getZExtValue()),
1224 getDefaultFloatRepr((unsigned)Cto->getValue().getZExtValue()),
1225 isTruncate);
1226 if (!res)
1227 return false;
1228 return true;
1229 }
1230
1231 bool HandleBatch(CallInst *CI) {
1232 unsigned width = 1;
1233 unsigned truei = 0;
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);
1239 if (!F)
1240 return false;
1241
1242 assert(F);
1243 FunctionType *FT = F->getFunctionType();
1244
1245 // find and handle enzyme_width
1246 if (auto parsedWidth = parseWidthParameter(CI)) {
1247 width = *parsedWidth;
1248 } else {
1249 return false;
1250 }
1251
1252 // handle different argument order for struct return.
1253 bool sret =
1254 CI->hasStructRetAttr() || F->hasParamAttribute(0, Attribute::StructRet);
1255
1256 if (F->hasParamAttribute(0, Attribute::StructRet)) {
1257 truei = 1;
1258 Value *sretPt = CI->getArgOperand(0);
1259
1260 args.push_back(sretPt);
1261 arg_types.push_back(BATCH_TYPE::VECTOR);
1262 }
1263
1264 for (unsigned i = 1 + sret; i < CI->arg_size(); ++i) {
1265 Value *res = CI->getArgOperand(i);
1266
1267 if (truei >= FT->getNumParams()) {
1268 EmitFailure("TooManyArgs", CI->getDebugLoc(), CI,
1269 "Had too many arguments to __enzyme_batch", *CI,
1270 " - extra arg - ", *res);
1271 return false;
1272 }
1273 assert(truei < FT->getNumParams());
1274 auto PTy = FT->getParamType(truei);
1275
1277 auto metaString = getMetadataName(res);
1278
1279 // handle metadata
1280 if (metaString && startsWith(*metaString, "enzyme_")) {
1281 if (*metaString == "enzyme_scalar") {
1282 ty = BATCH_TYPE::SCALAR;
1283 } else if (*metaString == "enzyme_vector") {
1284 ty = BATCH_TYPE::VECTOR;
1285 } else if (*metaString == "enzyme_buffer") {
1286 ty = BATCH_TYPE::VECTOR;
1287 ++i;
1288 Value *offset_arg = CI->getArgOperand(i);
1289 if (offset_arg->getType()->isIntegerTy()) {
1290 batchOffset[i + 1] = offset_arg;
1291 } else {
1292 EmitFailure("IllegalVectorOffset", CI->getDebugLoc(), CI,
1293 "enzyme_batch must be followd by an integer "
1294 "offset.",
1295 *CI->getArgOperand(i), " in", *CI);
1296 return false;
1297 }
1298 continue;
1299 } else if (*metaString == "enzyme_width") {
1300 ++i;
1301 continue;
1302 } else {
1303 EmitFailure("IllegalDiffeType", CI->getDebugLoc(), CI,
1304 "illegal enzyme metadata classification ", *CI,
1305 *metaString);
1306 return false;
1307 }
1308 ++i;
1309 res = CI->getArgOperand(i);
1310 }
1311
1312 arg_types.push_back(ty);
1313
1314 // wrap vector
1315 if (ty == BATCH_TYPE::VECTOR) {
1316 Value *res = nullptr;
1317 bool batch = batchOffset.count(i - 1) != 0;
1318
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);
1324 return false;
1325 }
1326
1327 // vectorize pointer
1328 Value *element = CI->getArgOperand(i);
1329 if (batch) {
1330 if (auto elementPtrTy = dyn_cast<PointerType>(element->getType())) {
1331 element = Builder.CreateBitCast(
1332 element, getPointerType(Type::getInt8Ty(CI->getContext()),
1333 elementPtrTy->getAddressSpace()));
1334 element = Builder.CreateGEP(
1335 Type::getInt8Ty(CI->getContext()), element,
1336 Builder.CreateMul(
1337 batchOffset[i - 1],
1338 ConstantInt::get(batchOffset[i - 1]->getType(), v)));
1339 element = Builder.CreateBitCast(element, elementPtrTy);
1340 } else {
1341 return false;
1342 }
1343 }
1344
1345 if (width > 1) {
1346 res =
1347 res ? Builder.CreateInsertValue(res, element, {v})
1348 : Builder.CreateInsertValue(UndefValue::get(ArrayType::get(
1349 element->getType(), width)),
1350 element, {v});
1351
1352 if (v < width - 1 && !batch) {
1353 ++i;
1354 }
1355
1356 } else {
1357 res = element;
1358 }
1359 }
1360
1361 args.push_back(res);
1362
1363 } else if (ty == BATCH_TYPE::SCALAR) {
1364 args.push_back(res);
1365 }
1366
1367 truei++;
1368 }
1369
1370 BATCH_TYPE ret_type = (F->getReturnType()->isVoidTy() || width == 1)
1373
1374 auto newFunc = Logic.CreateBatch(RequestContext(CI, &Builder), F, width,
1375 arg_types, ret_type);
1376
1377 if (!newFunc)
1378 return false;
1379
1380 Value *batch =
1381 Builder.CreateCall(newFunc->getFunctionType(), newFunc, args);
1382
1383 batch = adaptReturnedVector(CI, batch, Builder, width);
1384
1385 Value *ret = CI;
1386 Type *retElemType = nullptr;
1387 if (CI->hasStructRetAttr()) {
1388 ret = CI->getArgOperand(0);
1389 retElemType =
1390 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1391 .getValueAsType();
1392 }
1393 ReplaceOriginalCall(Builder, ret, retElemType, batch, CI,
1395
1396 return true;
1397 }
1398
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,
1403 DerivativeMode mode, Options &options, bool sizeOnly,
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;
1417
1418 auto TT = Triple(CI->getModule()->getTargetTriple());
1419 bool AtomicAdd = isGPUArch(TT);
1420
1421 TypeAnalysis TA(Logic);
1422 FnTypeInfo type_args = populate_type_args(TA, fn, mode);
1423
1424 std::vector<bool> nowrite_shadows(fn->arg_size(), false);
1425
1426 IRBuilder Builder(CI);
1427 RequestContext context(CI, &Builder);
1428
1429 // differentiate fn
1430 Function *newFunc = nullptr;
1431 Type *tapeType = nullptr;
1432 const AugmentedReturn *aug;
1433 switch (mode) {
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);
1441 } else
1442 newFunc = Logic.CreateForwardDiff(
1443 context, fn, retType, constants, TA,
1444 /*should return*/ primalReturn, mode, freeMemory,
1445 options.runtimeActivity, options.strongZero, width,
1446 /*addedType*/ nullptr, type_args, subsequent_calls_may_write,
1447 overwritten_args,
1448 /*augmented*/ nullptr);
1449 break;
1451 bool forceAnonymousTape = !sizeOnly && allocatedTapeSize == -1;
1452 aug = &Logic.CreateAugmentedPrimal(
1453 context, fn, retType, constants, TA,
1454 /*returnUsed*/ false, /*shadowReturnUsed*/ false, type_args,
1455 subsequent_calls_may_write, overwritten_args, nowrite_shadows,
1456 forceAnonymousTape, options.runtimeActivity, options.strongZero,
1457 width, /*atomicAdd*/ AtomicAdd);
1458 auto &DL = fn->getParent()->getDataLayout();
1459 if (!forceAnonymousTape) {
1460 assert(!aug->tapeType);
1461 if (aug->returns.find(AugmentedStruct::Tape) != aug->returns.end()) {
1462 auto tapeIdx = aug->returns.find(AugmentedStruct::Tape)->second;
1463 tapeType = (tapeIdx == -1)
1464 ? aug->fn->getReturnType()
1465 : cast<StructType>(aug->fn->getReturnType())
1466 ->getElementType(tapeIdx);
1467 } else {
1468 if (sizeOnly) {
1469 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), 0, false));
1470 CI->eraseFromParent();
1471 return true;
1472 }
1473 }
1474 if (sizeOnly) {
1475 auto size = DL.getTypeSizeInBits(tapeType) / 8;
1476 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), size, false));
1477 CI->eraseFromParent();
1478 return true;
1479 }
1480 if (tapeType &&
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,
1485 " bytes");
1486 }
1487 } else {
1488 tapeType = getInt8PtrTy(fn->getContext());
1489 }
1490 newFunc = Logic.CreateForwardDiff(
1491 context, fn, retType, constants, TA,
1492 /*should return*/ primalReturn, mode, freeMemory,
1493 options.runtimeActivity, options.strongZero, width,
1494 /*addedType*/ tapeType, type_args, subsequent_calls_may_write,
1495 overwritten_args, aug);
1496 break;
1497 }
1499 assert(freeMemory);
1500 newFunc = Logic.CreatePrimalAndGradient(
1501 context,
1502 (ReverseCacheKey){.todiff = fn,
1503 .retType = retType,
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,
1510 .mode = mode,
1511 .width = width,
1512 .freeMemory = freeMemory,
1513 .AtomicAdd = AtomicAdd,
1514 .additionalType = nullptr,
1515 .forceAnonymousTape = false,
1516 .typeInfo = type_args,
1517 .runtimeActivity = options.runtimeActivity,
1518 .strongZero = options.strongZero},
1519 TA, /*augmented*/ nullptr);
1520 break;
1523 if (primalReturn) {
1525 "SplitPrimalRet", CI->getDebugLoc(), CI,
1526 "Option enzyme_primal_return not available in reverse split mode");
1527 }
1528 bool forceAnonymousTape = !sizeOnly && allocatedTapeSize == -1;
1529 bool shadowReturnUsed = returnUsed && (retType == DIFFE_TYPE::DUP_ARG ||
1530 retType == DIFFE_TYPE::DUP_NONEED);
1531 aug = &Logic.CreateAugmentedPrimal(
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*/ AtomicAdd);
1536 auto &DL = fn->getParent()->getDataLayout();
1537 if (!forceAnonymousTape) {
1538 assert(!aug->tapeType);
1539 if (aug->returns.find(AugmentedStruct::Tape) != aug->returns.end()) {
1540 auto tapeIdx = aug->returns.find(AugmentedStruct::Tape)->second;
1541 tapeType = (tapeIdx == -1)
1542 ? aug->fn->getReturnType()
1543 : cast<StructType>(aug->fn->getReturnType())
1544 ->getElementType(tapeIdx);
1545 } else {
1546 if (sizeOnly) {
1547 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), 0, false));
1548 CI->eraseFromParent();
1549 return true;
1550 }
1551 }
1552 if (sizeOnly) {
1553 auto size = DL.getTypeSizeInBits(tapeType) / 8;
1554 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), size, false));
1555 CI->eraseFromParent();
1556 return true;
1557 }
1558 if (tapeType &&
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,
1563 " bytes");
1564 }
1565 } else {
1566 tapeType = getInt8PtrTy(fn->getContext());
1567 }
1569 newFunc = aug->fn;
1570 else
1571 newFunc = Logic.CreatePrimalAndGradient(
1572 context,
1573 (ReverseCacheKey){.todiff = fn,
1574 .retType = retType,
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,
1581 .mode = mode,
1582 .width = width,
1583 .freeMemory = freeMemory,
1584 .AtomicAdd = AtomicAdd,
1585 .additionalType = tapeType,
1586 .forceAnonymousTape = forceAnonymousTape,
1587 .typeInfo = type_args,
1588 .runtimeActivity = options.runtimeActivity,
1589 .strongZero = options.strongZero},
1590 TA, aug);
1591 }
1592 }
1593
1594 if (!newFunc) {
1595 StringRef n = fn->getName();
1596 EmitFailure("FailedToDifferentiate", fn->getSubprogram(),
1597 &*fn->getEntryBlock().begin(),
1598 "Could not generate derivative function of ", n);
1599 return false;
1600 }
1601
1602 if (differentialReturn) {
1603 if (differet)
1604 args.push_back(differet);
1605 else if (fn->getReturnType()->isFPOrFPVectorTy()) {
1606 Constant *seed = ConstantFP::get(fn->getReturnType(), 1.0);
1607 if (width == 1) {
1608 args.push_back(seed);
1609 } else {
1610 ArrayType *arrayType = ArrayType::get(fn->getReturnType(), width);
1611 args.push_back(ConstantArray::get(
1612 arrayType, SmallVector<Constant *, 3>(width, seed)));
1613 }
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));
1618 }
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));
1624 } else {
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");
1629 return false;
1630 }
1631 }
1632
1635 tape && tapeType) {
1636 auto &DL = fn->getParent()->getDataLayout();
1637 if (tapeIsPointer) {
1638 tape = Builder.CreateBitCast(
1639 tape, getPointerType(
1640 tapeType,
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)));
1651 }
1652 assert(tape->getType() == tapeType);
1653 args.push_back(tape);
1654 }
1655
1656 if (EnzymePrint) {
1657 llvm::errs() << "postfn:\n" << *newFunc << "\n";
1658 }
1659 Builder.setFastMathFlags(getFast());
1660
1661 // call newFunc with the provided arguments.
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";
1667 }
1668 auto modestr = to_string(mode);
1670 "TooFewArguments", CI->getDebugLoc(), CI,
1671 "Too few arguments passed to __enzyme_autodiff mode=", modestr);
1672 return false;
1673 }
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";
1681 }
1682 auto modestr = to_string(mode);
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());
1688 return false;
1689 }
1690 }
1691 CallInst *diffretc = cast<CallInst>(Builder.CreateCall(newFunc, args));
1692 diffretc->setCallingConv(CallingConv);
1693 diffretc->setDebugLoc(CI->getDebugLoc());
1694
1695 for (auto &&[attr, ty] : byVal) {
1696 diffretc->addParamAttr(
1697 attr, Attribute::getWithByValType(diffretc->getContext(), ty));
1698 }
1699
1700 Value *diffret = diffretc;
1701 if (mode == DerivativeMode::ReverseModePrimal && tape) {
1702 if (aug->returns.find(AugmentedStruct::Tape) != aug->returns.end()) {
1703 auto tapeIdx = aug->returns.find(AugmentedStruct::Tape)->second;
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)
1709 ? diffret
1710 : Builder.CreateExtractValue(diffret, idxs);
1711 Builder.CreateStore(
1712 tapeRes, Builder.CreateBitCast(
1713 tape, getPointerType(tapeRes->getType(),
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});
1726 }
1727 diffret = out;
1728 } else {
1729 auto ST0 = StructType::get(tape->getContext(), {});
1730 diffret = UndefValue::get(ST0);
1731 }
1732 }
1733 }
1734
1735 // Adapt the returned vector type to the struct type expected by our calling
1736 // convention.
1737 if (width > 1 && !diffret->getType()->isEmptyTy() &&
1738 !diffret->getType()->isVoidTy() &&
1739 (mode == DerivativeMode::ForwardMode ||
1741
1742 diffret = adaptReturnedVector(ret, diffret, Builder, width);
1743 }
1744
1745 ReplaceOriginalCall(Builder, ret, retElemType, diffret, CI, mode);
1746 calls.push_back(diffretc);
1747 return diffret;
1748 }
1749
1750 /// Return whether successful
1751 bool HandleAutoDiffArguments(CallInst *CI, DerivativeMode mode, bool sizeOnly,
1752 SmallVectorImpl<CallInst *> &calls) {
1753
1754 // determine function to differentiate
1755 Function *fn = parseFunctionParameter(CI);
1756 if (!fn)
1757 return false;
1758
1759 IRBuilder<> Builder(CI);
1760
1761 if (EnzymePrint)
1762 llvm::errs() << "prefn:\n" << *fn << "\n";
1763
1764 std::map<int, Type *> byVal;
1765 std::vector<DIFFE_TYPE> constants;
1766 SmallVector<Value *, 2> args;
1767
1768 auto options = handleArguments(Builder, CI, fn, mode, sizeOnly, constants,
1769 args, byVal);
1770
1771 if (!options) {
1772 return false;
1773 }
1774
1775 Value *ret = CI;
1776 Type *retElemType = nullptr;
1777 if (CI->hasStructRetAttr()) {
1778 ret = CI->getArgOperand(0);
1779 retElemType =
1780 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1781 .getValueAsType();
1782 }
1783
1784 return HandleAutoDiff(CI, CI->getCallingConv(), ret, retElemType, args,
1785 byVal, constants, fn, mode, *options, sizeOnly,
1786 calls);
1787 }
1788
1789 bool HandleProbProg(CallInst *CI, ProbProgMode mode,
1790 SmallVectorImpl<CallInst *> &calls) {
1791 IRBuilder<> Builder(CI);
1792 Function *F = parseFunctionParameter(CI);
1793 if (!F)
1794 return false;
1795
1796 assert(F);
1797
1798 std::vector<DIFFE_TYPE> constants;
1799 std::map<int, Type *> byVal;
1800 SmallVector<Value *, 4> args;
1801
1802 auto diffeMode = DerivativeMode::ReverseModeCombined;
1803
1804 auto opt = handleArguments(Builder, CI, F, diffeMode, false, constants,
1805 args, byVal);
1806
1807 SmallVector<Value *, 6> dargs(args.begin(), args.end());
1808
1809#if LLVM_VERSION_MAJOR >= 16
1810 if (!opt.has_value())
1811 return false;
1812#else
1813 if (!opt.hasValue())
1814 return false;
1815#endif
1816
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;
1823
1824 // Interface
1825 bool has_dynamic_interface = dynamic_interface != nullptr;
1826 bool needs_interface =
1828 std::unique_ptr<TraceInterface> interface;
1829 if (has_dynamic_interface) {
1830 interface = std::make_unique<DynamicTraceInterface>(dynamic_interface,
1831 CI->getFunction());
1832 } else if (needs_interface) {
1833 interface = std::make_unique<StaticTraceInterface>(F->getParent());
1834 }
1835
1836 // Find sample function
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);
1846 }
1847 }
1848
1849 assert(!sampleFunctions.empty() || !observeFunctions.empty());
1850
1851 bool autodiff = dtrace || dlikelihood;
1852 IRBuilder<> AllocaBuilder(getFirstNonPHI(CI->getParent()));
1853
1854 if (!likelihood) {
1855 likelihood = AllocaBuilder.CreateAlloca(AllocaBuilder.getDoubleTy(),
1856 nullptr, "likelihood");
1857 Builder.CreateStore(ConstantFP::getNullValue(Builder.getDoubleTy()),
1858 likelihood);
1859 }
1860 args.push_back(likelihood);
1861
1862 if (autodiff && !dlikelihood) {
1863 dlikelihood = AllocaBuilder.CreateAlloca(AllocaBuilder.getDoubleTy(),
1864 nullptr, "dlikelihood");
1865 Builder.CreateStore(ConstantFP::get(Builder.getDoubleTy(), 1.0),
1866 dlikelihood);
1867 }
1868
1869 if (autodiff) {
1870 dargs.push_back(likelihood);
1871 dargs.push_back(dlikelihood);
1872 constants.push_back(DIFFE_TYPE::DUP_ARG);
1873 opt->overwritten_args.push_back(false);
1874 } else {
1875 constants.push_back(DIFFE_TYPE::CONSTANT);
1876 opt->overwritten_args.push_back(false);
1877 }
1878
1879 if (mode == ProbProgMode::Condition) {
1880 opt->overwritten_args.push_back(false);
1881 args.push_back(observations);
1882 dargs.push_back(observations);
1883 constants.push_back(DIFFE_TYPE::CONSTANT);
1884 }
1885
1886 if (mode == ProbProgMode::Trace || mode == ProbProgMode::Condition) {
1887 opt->overwritten_args.push_back(false);
1888 args.push_back(trace);
1889 dargs.push_back(trace);
1890 constants.push_back(DIFFE_TYPE::CONSTANT);
1891 }
1892
1893 auto newFunc = Logic.CreateTrace(
1894 RequestContext(CI, &Builder), F, sampleFunctions, observeFunctions,
1895 opt->ActiveRandomVariables, mode, autodiff, interface.get());
1896
1897 if (!autodiff) {
1898 auto call = CallInst::Create(newFunc->getFunctionType(), newFunc, args);
1899 ReplaceInstWithInst(CI, call);
1900 return true;
1901 }
1902
1903 Value *ret = CI;
1904 Type *retElemType = nullptr;
1905 if (CI->hasStructRetAttr()) {
1906 ret = CI->getArgOperand(0);
1907 retElemType =
1908 CI->getAttribute(AttributeList::FirstArgIndex, Attribute::StructRet)
1909 .getValueAsType();
1910 }
1911
1912 bool status = HandleAutoDiff(
1913 CI, CI->getCallingConv(), ret, retElemType, dargs, byVal, constants,
1914 newFunc, DerivativeMode::ReverseModeCombined, *opt, false, calls);
1915
1916 return status;
1917 }
1918
1919 bool handleFullModuleTrunc(Function &F) {
1920 if (startsWith(F.getName(), EnzymeFPRTPrefix))
1921 return false;
1922 typedef std::vector<FloatTruncation> TruncationsTy;
1923 static TruncationsTy FullModuleTruncs = []() -> TruncationsTy {
1924 StringRef ConfigStr(EnzymeTruncateAll);
1925 auto Invalid = [=]() {
1926 // TODO emit better diagnostic
1927 llvm::report_fatal_error("error: invalid format for truncation config");
1928 };
1929
1930 // "64" or "11-52"
1931 auto parseFloatRepr = [&]() -> std::optional<FloatRepresentation> {
1932 unsigned Tmp = 0;
1933 if (ConfigStr.consumeInteger(10, Tmp))
1934 return {};
1935 if (ConfigStr.consume_front("-")) {
1936 unsigned Tmp2 = 0;
1937 if (ConfigStr.consumeInteger(10, Tmp2))
1938 Invalid();
1939 return FloatRepresentation(Tmp, Tmp2);
1940 }
1941 return getDefaultFloatRepr(Tmp);
1942 };
1943
1944 // Parse "64to32;32to16;5-10to4-9"
1945 TruncationsTy Tmp;
1946 while (true) {
1947 auto From = parseFloatRepr();
1948 if (!From && !ConfigStr.empty())
1949 Invalid();
1950 if (!From)
1951 break;
1952 if (!ConfigStr.consume_front("to"))
1953 Invalid();
1954 auto To = parseFloatRepr();
1955 if (!To)
1956 Invalid();
1957 Tmp.push_back({*From, *To, TruncOpFullModuleMode});
1958 ConfigStr.consume_front(";");
1959 }
1960 return Tmp;
1961 }();
1962
1963 if (FullModuleTruncs.empty())
1964 return false;
1965
1966 // TODO sort truncations (64to32, then 32to16 will make everything 16)
1967 for (auto Truncation : FullModuleTruncs) {
1968 IRBuilder<> Builder(F.getContext());
1969 RequestContext context(&*F.getEntryBlock().begin(), &Builder);
1970 Function *TruncatedFunc = Logic.CreateTruncateFunc(
1971 context, &F, Truncation, TruncOpFullModuleMode);
1972
1973 ValueToValueMapTy Mapping;
1974 for (auto &&[Arg, TArg] : llvm::zip(F.args(), TruncatedFunc->args()))
1975 Mapping[&TArg] = &Arg;
1976
1977 // Move the truncated body into the original function
1978 F.deleteBody();
1979#if LLVM_VERSION_MAJOR >= 16
1980 F.splice(F.begin(), TruncatedFunc);
1981#else
1982 F.getBasicBlockList().splice(F.begin(),
1983 TruncatedFunc->getBasicBlockList());
1984#endif
1985 RemapFunction(F, Mapping,
1986 RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
1987 TruncatedFunc->deleteBody();
1988 }
1989 return true;
1990 }
1991
1992 bool lowerEnzymeCalls(Function &F, std::set<Function *> &done) {
1993 if (done.count(&F))
1994 return false;
1995 done.insert(&F);
1996
1997 if (F.empty())
1998 return false;
1999
2000 if (handleFullModuleTrunc(F))
2001 return true;
2002
2003 bool Changed = false;
2004
2005 for (BasicBlock &BB : F)
2006 if (InvokeInst *II = dyn_cast<InvokeInst>(BB.getTerminator())) {
2007
2008 Function *Fn = II->getCalledFunction();
2009
2010 if (auto castinst = dyn_cast<ConstantExpr>(II->getCalledOperand())) {
2011 if (castinst->isCast())
2012 if (auto fn = dyn_cast<Function>(castinst->getOperand(0)))
2013 Fn = fn;
2014 }
2015 if (!Fn)
2016 continue;
2017
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")))
2035 continue;
2036
2037 SmallVector<Value *, 16> CallArgs(II->arg_begin(), II->arg_end());
2038 SmallVector<OperandBundleDef, 1> OpBundles;
2039 II->getOperandBundlesAsDefs(OpBundles);
2040 // Insert a normal call instruction...
2041#if LLVM_VERSION_MAJOR >= 24
2042 CallInst *NewCall =
2043 CallInst::Create(II->getFunctionType(), II->getCalledOperand(),
2044 CallArgs, OpBundles, "", II->getIterator());
2045#else
2046 CallInst *NewCall =
2047 CallInst::Create(II->getFunctionType(), II->getCalledOperand(),
2048 CallArgs, OpBundles, "", II);
2049#endif
2050 NewCall->takeName(II);
2051 NewCall->setCallingConv(II->getCallingConv());
2052 NewCall->setAttributes(II->getAttributes());
2053 NewCall->setDebugLoc(II->getDebugLoc());
2054 II->replaceAllUsesWith(NewCall);
2055
2056 // Insert an unconditional branch to the normal destination.
2057#if LLVM_VERSION_MAJOR >= 24
2058 createUnconditionalBranch(II->getNormalDest(), II->getIterator());
2059#else
2060 createUnconditionalBranch(II->getNormalDest(), II);
2061#endif
2062
2063 // Remove any PHI node entries from the exception destination.
2064 II->getUnwindDest()->removePredecessor(&BB);
2065
2066 II->eraseFromParent();
2067 Changed = true;
2068 }
2069
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;
2081 retry:;
2082 for (BasicBlock &BB : F) {
2083 for (Instruction &I : BB) {
2084 CallInst *CI = dyn_cast<CallInst>(&I);
2085
2086 if (!CI)
2087 continue;
2088
2089 Function *Fn = nullptr;
2090
2091 Value *FnOp = CI->getCalledOperand();
2092 while (true) {
2093 if ((Fn = dyn_cast<Function>(FnOp)))
2094 break;
2095 if (auto castinst = dyn_cast<ConstantExpr>(FnOp)) {
2096 if (castinst->isCast()) {
2097 FnOp = castinst->getOperand(0);
2098 continue;
2099 }
2100 }
2101 break;
2102 }
2103
2104 if (!Fn)
2105 continue;
2106
2107 size_t num_args = CI->arg_size();
2108
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();
2114#else
2115 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2116#endif
2117 }
2118 if (Fn->getName().contains("__enzyme_float")) {
2119#if LLVM_VERSION_MAJOR >= 16
2120 CI->setOnlyReadsMemory();
2121 CI->setOnlyWritesMemory();
2122#else
2123 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2124#endif
2125 for (size_t i = 0; i < num_args; ++i) {
2126 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2127 CI->addParamAttr(i, Attribute::ReadNone);
2128 addCallSiteNoCapture(CI, i);
2129 }
2130 }
2131 }
2132 if (Fn->getName().contains("__enzyme_integer")) {
2133#if LLVM_VERSION_MAJOR >= 16
2134 CI->setOnlyReadsMemory();
2135 CI->setOnlyWritesMemory();
2136#else
2137 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2138#endif
2139 for (size_t i = 0; i < num_args; ++i) {
2140 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2141 CI->addParamAttr(i, Attribute::ReadNone);
2142 addCallSiteNoCapture(CI, i);
2143 }
2144 }
2145 }
2146 if (Fn->getName().contains("__enzyme_double")) {
2147#if LLVM_VERSION_MAJOR >= 16
2148 CI->setOnlyReadsMemory();
2149 CI->setOnlyWritesMemory();
2150#else
2151 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2152#endif
2153 for (size_t i = 0; i < num_args; ++i) {
2154 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2155 CI->addParamAttr(i, Attribute::ReadNone);
2156 addCallSiteNoCapture(CI, i);
2157 }
2158 }
2159 }
2160 if (Fn->getName().contains("__enzyme_pointer")) {
2161#if LLVM_VERSION_MAJOR >= 16
2162 CI->setOnlyReadsMemory();
2163 CI->setOnlyWritesMemory();
2164#else
2165 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2166#endif
2167 for (size_t i = 0; i < num_args; ++i) {
2168 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2169 CI->addParamAttr(i, Attribute::ReadNone);
2170 addCallSiteNoCapture(CI, i);
2171 }
2172 }
2173 }
2174 if (Fn->getName().contains("__enzyme_virtualreverse")) {
2175#if LLVM_VERSION_MAJOR >= 16
2176 CI->setOnlyReadsMemory();
2177 CI->setOnlyWritesMemory();
2178#else
2179 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2180#endif
2181 }
2182 if (Fn->getName().contains("__enzyme_iter")) {
2183#if LLVM_VERSION_MAJOR >= 16
2184 CI->setOnlyReadsMemory();
2185 CI->setOnlyWritesMemory();
2186#else
2187 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2188#endif
2189 }
2190 if (Fn->getName().contains("__enzyme_call_inactive")) {
2191 InactiveCalls.insert(CI);
2192 }
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();
2200#else
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);
2206#endif
2207 }
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();
2214#else
2215 Fn->addFnAttr(Attribute::ArgMemOnly);
2216 Fn->addFnAttr(Attribute::ReadOnly);
2217 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly);
2218#endif
2219 CI->addParamAttr(1, Attribute::ReadOnly);
2220 addCallSiteNoCapture(CI, 1);
2221 CI->addParamAttr(3, Attribute::ReadOnly);
2222 addCallSiteNoCapture(CI, 3);
2223 }
2224 if (Fn->getName() == "frexp" || Fn->getName() == "frexpf" ||
2225 Fn->getName() == "frexpl") {
2226#if LLVM_VERSION_MAJOR >= 16
2227 CI->setOnlyAccessesArgMemory();
2228#else
2229 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ArgMemOnly);
2230#endif
2231 CI->addParamAttr(1, Attribute::WriteOnly);
2232 }
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();
2238#else
2239 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
2240#endif
2241 }
2242 if (getFuncName(Fn) == "strcmp") {
2243 Fn->addParamAttr(0, Attribute::ReadOnly);
2244 Fn->addParamAttr(1, Attribute::ReadOnly);
2245#if LLVM_VERSION_MAJOR >= 16
2246 Fn->setOnlyReadsMemory();
2247 CI->setOnlyReadsMemory();
2248#else
2249 Fn->addFnAttr(Attribute::ReadOnly);
2250 CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly);
2251#endif
2252 }
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();
2258#else
2259 Fn->addFnAttr(Attribute::InaccessibleMemOnly);
2260 CI->addAttribute(AttributeList::FunctionIndex,
2261 Attribute::InaccessibleMemOnly);
2262#endif
2263 }
2264 if (Fn->getName() == "f90io_open2003a") {
2265#if LLVM_VERSION_MAJOR >= 16
2266 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2267 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2268#else
2269 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2270 CI->addAttribute(AttributeList::FunctionIndex,
2271 Attribute::InaccessibleMemOrArgMemOnly);
2272#endif
2273 for (size_t i : {0, 1, 2, 3, 4, 5, 6, 7, /*8, */ 9, 10, 11, 12, 13}) {
2274 if (i < num_args &&
2275 CI->getArgOperand(i)->getType()->isPointerTy()) {
2276 CI->addParamAttr(i, Attribute::ReadOnly);
2277 }
2278 }
2279 // todo more
2280 for (size_t i : {0, 1}) {
2281 if (i < num_args &&
2282 CI->getArgOperand(i)->getType()->isPointerTy()) {
2283 addCallSiteNoCapture(CI, i);
2284 }
2285 }
2286 }
2287 if (Fn->getName() == "f90io_fmtw_inita") {
2288#if LLVM_VERSION_MAJOR >= 16
2289 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2290 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2291#else
2292 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2293 CI->addAttribute(AttributeList::FunctionIndex,
2294 Attribute::InaccessibleMemOrArgMemOnly);
2295#endif
2296 // todo more
2297 for (size_t i : {0, 2}) {
2298 if (i < num_args &&
2299 CI->getArgOperand(i)->getType()->isPointerTy()) {
2300 CI->addParamAttr(i, Attribute::ReadOnly);
2301 }
2302 }
2303
2304 // todo more
2305 for (size_t i : {0, 2}) {
2306 if (i < num_args &&
2307 CI->getArgOperand(i)->getType()->isPointerTy()) {
2308 addCallSiteNoCapture(CI, i);
2309 }
2310 }
2311 }
2312
2313 if (Fn->getName() == "f90io_unf_init") {
2314#if LLVM_VERSION_MAJOR >= 16
2315 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2316 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2317#else
2318 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2319 CI->addAttribute(AttributeList::FunctionIndex,
2320 Attribute::InaccessibleMemOrArgMemOnly);
2321#endif
2322 // todo more
2323 for (size_t i : {0, 1, 2, 3}) {
2324 if (i < num_args &&
2325 CI->getArgOperand(i)->getType()->isPointerTy()) {
2326 CI->addParamAttr(i, Attribute::ReadOnly);
2327 }
2328 }
2329
2330 // todo more
2331 for (size_t i : {0, 1, 2, 3}) {
2332 if (i < num_args &&
2333 CI->getArgOperand(i)->getType()->isPointerTy()) {
2334 addCallSiteNoCapture(CI, i);
2335 }
2336 }
2337 }
2338
2339 if (Fn->getName() == "f90io_src_info03a") {
2340#if LLVM_VERSION_MAJOR >= 16
2341 Fn->setOnlyAccessesInaccessibleMemOrArgMem();
2342 CI->setOnlyAccessesInaccessibleMemOrArgMem();
2343#else
2344 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2345 CI->addAttribute(AttributeList::FunctionIndex,
2346 Attribute::InaccessibleMemOrArgMemOnly);
2347#endif
2348 // todo more
2349 for (size_t i : {0, 1}) {
2350 if (i < num_args &&
2351 CI->getArgOperand(i)->getType()->isPointerTy()) {
2352 CI->addParamAttr(i, Attribute::ReadOnly);
2353 }
2354 }
2355
2356 // todo more
2357 for (size_t i : {0}) {
2358 if (i < num_args &&
2359 CI->getArgOperand(i)->getType()->isPointerTy()) {
2360 addCallSiteNoCapture(CI, i);
2361 }
2362 }
2363 }
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();
2374#else
2375 Fn->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
2376 CI->addAttribute(AttributeList::FunctionIndex,
2377 Attribute::InaccessibleMemOrArgMemOnly);
2378#endif
2379 for (size_t i = 0; i < num_args; ++i) {
2380 if (CI->getArgOperand(i)->getType()->isPointerTy()) {
2381 CI->addParamAttr(i, Attribute::ReadOnly);
2382 addCallSiteNoCapture(CI, i);
2383 }
2384 }
2385 }
2386
2387 bool enableEnzyme = false;
2388 bool virtualCall = false;
2389 bool sizeOnly = false;
2390 bool batch = false;
2391 bool truncateFuncOp = false;
2392 bool truncateFuncMem = false;
2393 bool truncateValue = false;
2394 bool expandValue = false;
2395 bool probProg = false;
2396 DerivativeMode derivativeMode;
2397 ProbProgMode probProgMode;
2398 if (Fn->getName().contains("__enzyme_autodiff")) {
2399 enableEnzyme = true;
2400 derivativeMode = DerivativeMode::ReverseModeCombined;
2401 } else if (Fn->getName().contains("__enzyme_fwddiff")) {
2402 enableEnzyme = true;
2403 derivativeMode = DerivativeMode::ForwardMode;
2404 } else if (Fn->getName().contains("__enzyme_error_estimate")) {
2405 enableEnzyme = true;
2406 derivativeMode = DerivativeMode::ForwardModeError;
2407 } else if (Fn->getName().contains("__enzyme_fwdsplit")) {
2408 enableEnzyme = true;
2409 derivativeMode = DerivativeMode::ForwardModeSplit;
2410 } else if (Fn->getName().contains("__enzyme_augmentfwd")) {
2411 enableEnzyme = true;
2412 derivativeMode = DerivativeMode::ReverseModePrimal;
2413 } else if (Fn->getName().contains("__enzyme_augmentsize")) {
2414 enableEnzyme = true;
2415 sizeOnly = true;
2416 derivativeMode = DerivativeMode::ReverseModePrimal;
2417 } else if (Fn->getName().contains("__enzyme_reverse")) {
2418 enableEnzyme = true;
2419 derivativeMode = DerivativeMode::ReverseModeGradient;
2420 } else if (Fn->getName().contains("__enzyme_virtualreverse")) {
2421 enableEnzyme = true;
2422 virtualCall = true;
2423 derivativeMode = DerivativeMode::ReverseModeCombined;
2424 } else if (Fn->getName().contains("__enzyme_batch")) {
2425 enableEnzyme = true;
2426 batch = 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;
2438 expandValue = true;
2439 } else if (Fn->getName().contains("__enzyme_likelihood")) {
2440 enableEnzyme = true;
2441 probProgMode = ProbProgMode::Likelihood;
2442 probProg = true;
2443 } else if (Fn->getName().contains("__enzyme_trace")) {
2444 enableEnzyme = true;
2445 probProgMode = ProbProgMode::Trace;
2446 probProg = true;
2447 } else if (Fn->getName().contains("__enzyme_condition")) {
2448 enableEnzyme = true;
2449 probProgMode = ProbProgMode::Condition;
2450 probProg = true;
2451 }
2452
2453 if (enableEnzyme) {
2454
2455 Value *fn = CI->getArgOperand(0);
2456 while (auto ci = dyn_cast<CastInst>(fn)) {
2457 fn = ci->getOperand(0);
2458 }
2459 while (auto ci = dyn_cast<BlockAddress>(fn)) {
2460 fn = ci->getFunction();
2461 }
2462 while (auto ci = dyn_cast<ConstantExpr>(fn)) {
2463 fn = ci->getOperand(0);
2464 }
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());
2475 insertBeforeInst(cloned, B1);
2476 cloned->setOperand(0, si->getTrueValue());
2477 IRBuilder<> S2(sel2);
2478 auto B2 = S2.CreateBr(post);
2479 moveBeforeInst(CI, B2);
2480 CI->setOperand(0, si->getFalseValue());
2481 if (CI->getNumUses() != 0) {
2482 IRBuilder<> P(getFirstNonPHI(post));
2483 auto merge = P.CreatePHI(CI->getType(), 2);
2484 merge->addIncoming(cloned, sel1);
2485 merge->addIncoming(CI, sel2);
2486 CI->replaceAllUsesWith(merge);
2487 }
2488 goto retry;
2489 }
2490 if (virtualCall)
2491 toVirtual[CI] = derivativeMode;
2492 else if (sizeOnly)
2493 toSize[CI] = derivativeMode;
2494 else if (batch)
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;
2506 } else
2507 toLower[CI] = derivativeMode;
2508
2509 if (auto dc = dyn_cast<Function>(fn)) {
2510 // Force postopt on any inner functions in the nested
2511 // AD case.
2512 bool tmp = Logic.PostOpt;
2513 Logic.PostOpt = true;
2514 Changed |= lowerEnzymeCalls(*dc, done);
2515 Logic.PostOpt = tmp;
2516 }
2517 }
2518 }
2519 }
2520
2521 for (auto CI : InactiveCalls) {
2522 IRBuilder<> B(CI);
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());
2529 }
2530 auto FT = FunctionType::get(CI->getType(), ArgTypes, /*varargs*/ false);
2531 if (fn->getType() != FT) {
2532 fn = B.CreatePointerCast(fn, getUnqual(FT));
2533 }
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();
2539 Changed = true;
2540 }
2541
2542 SmallVector<CallInst *, 1> calls;
2543
2544 // Perform all the size replacements first to create constants
2545 for (auto pair : toSize) {
2546 bool successful = HandleAutoDiffArguments(pair.first, pair.second,
2547 /*sizeOnly*/ true, calls);
2548 Changed = true;
2549 if (!successful)
2550 break;
2551 }
2552 for (auto pair : toLower) {
2553 bool successful = HandleAutoDiffArguments(pair.first, pair.second,
2554 /*sizeOnly*/ false, calls);
2555 Changed = true;
2556 if (!successful)
2557 break;
2558 }
2559
2560 for (auto pair : toVirtual) {
2561 auto CI = pair.first;
2562 Constant *fn = dyn_cast<Constant>(CI->getArgOperand(0));
2563 if (!fn) {
2564 EmitFailure("IllegalVirtual", CI->getDebugLoc(), CI,
2565 "Cannot create virtual version of non-constant value ", *CI,
2566 *CI->getArgOperand(0));
2567 return false;
2568 }
2569 TypeAnalysis TA(Logic);
2570
2571 auto TT = llvm::Triple(
2572 CI->getParent()->getParent()->getParent()->getTargetTriple());
2573
2574 bool AtomicAdd = isGPUArch(TT);
2575
2576 IRBuilder<> Builder(CI);
2578 RequestContext(CI, &Builder), Logic,
2579 Logic.PPC.FAM.getResult<TargetLibraryAnalysis>(F), TA, fn,
2580 pair.second, /*runtimeActivity*/ false, /*strongZero*/ false,
2581 /*width*/ 1, AtomicAdd);
2582 CI->replaceAllUsesWith(ConstantExpr::getPointerCast(val, CI->getType()));
2583 CI->eraseFromParent();
2584 Changed = true;
2585 }
2586
2587 for (auto call : toBatch) {
2588 HandleBatch(call);
2589 }
2590 for (auto call : toTruncateFuncMem) {
2591 HandleTruncateFunc(call, TruncMemMode);
2592 }
2593 for (auto call : toTruncateFuncOp) {
2594 HandleTruncateFunc(call, TruncOpMode);
2595 }
2596 for (auto call : toTruncateValue) {
2597 HandleTruncateValue(call, true);
2598 }
2599 for (auto call : toExpandValue) {
2600 HandleTruncateValue(call, false);
2601 }
2602
2603 for (auto &&[call, mode] : toProbProg) {
2604 HandleProbProg(call, mode, calls);
2605 }
2606
2607 if (Logic.PostOpt) {
2608 auto Params = llvm::getInlineParams();
2609
2610 llvm::SetVector<CallInst *> Q;
2611 for (auto call : calls)
2612 Q.insert(call);
2613 while (Q.size()) {
2614 auto cur = *Q.begin();
2615 Function *outerFunc = cur->getParent()->getParent();
2616 llvm::OptimizationRemarkEmitter ORE(outerFunc);
2617 Q.erase(Q.begin());
2618 if (auto F = cur->getCalledFunction()) {
2619 if (!F->empty()) {
2620 // Garbage collect AC's created
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();
2626 };
2627 auto GetTLI =
2628 [&](llvm::Function &F) -> const llvm::TargetLibraryInfo & {
2629 return Logic.PPC.FAM.getResult<TargetLibraryAnalysis>(F);
2630 };
2631
2632 TargetTransformInfo TTI(F->getParent()->getDataLayout());
2633 auto GetInlineCost = [&](CallBase &CB) {
2634 auto cst = llvm::getInlineCost(CB, Params, TTI, getAC, GetTLI);
2635 return cst;
2636 };
2637#if LLVM_VERSION_MAJOR >= 20
2638 if (llvm::shouldInline(*cur, TTI, GetInlineCost, ORE))
2639#else
2640 if (llvm::shouldInline(*cur, GetInlineCost, ORE))
2641#endif
2642 {
2643 InlineFunctionInfo IFI;
2644 InlineResult IR = InlineFunction(*cur, IFI);
2645 if (IR.isSuccess()) {
2646 LowerSparsification(outerFunc, /*replaceAll*/ false);
2647 for (auto U : outerFunc->users()) {
2648 if (auto CI = dyn_cast<CallInst>(U)) {
2649 if (CI->getCalledFunction() == outerFunc) {
2650 Q.insert(CI);
2651 }
2652 }
2653 }
2654 }
2655 }
2656 }
2657 }
2658 }
2659 }
2660
2661 if (Changed && EnzymeAttributor) {
2662 // TODO consider enabling when attributor does not delete
2663 // dead internal functions, which invalidates Enzyme's cache
2664 // code left here to re-enable upon Attributor patch
2665
2666#if !defined(FLANG) && !defined(ROCM)
2667
2668 AnalysisGetter AG(Logic.PPC.FAM);
2669 SetVector<Function *> Functions;
2670 for (Function &F2 : *F.getParent()) {
2671 Functions.insert(&F2);
2672 }
2673
2674 CallGraphUpdater CGUpdater;
2675 BumpPtrAllocator Allocator;
2676 InformationCache InfoCache(*F.getParent(), AG, Allocator,
2677 /* CGSCC */ nullptr);
2678
2679 DenseSet<const char *> Allowed = {
2680 &AAHeapToStack::ID,
2681 &AANoCapture::ID,
2682
2683 &AAMemoryBehavior::ID,
2684 &AAMemoryLocation::ID,
2685 &AANoUnwind::ID,
2686 &AANoSync::ID,
2687 &AANoRecurse::ID,
2688 &AAWillReturn::ID,
2689 &AANoReturn::ID,
2690 &AANonNull::ID,
2691 &AANoAlias::ID,
2692 &AADereferenceable::ID,
2693 &AAAlign::ID,
2694#if LLVM_VERSION_MAJOR < 17
2695 &AAReturnedValues::ID,
2696#endif
2697 &AANoFree::ID,
2698 &AANoUndef::ID,
2699
2700 //&AAValueSimplify::ID,
2701 //&AAReachability::ID,
2702 //&AAValueConstantRange::ID,
2703 //&AAUndefinedBehavior::ID,
2704 //&AAPotentialValues::ID,
2705 };
2706
2707 AttributorConfig aconfig(CGUpdater);
2708 aconfig.Allowed = &Allowed;
2709 aconfig.DeleteFns = false;
2710 Attributor A(Functions, InfoCache, aconfig);
2711 for (Function *F : Functions) {
2712 // Populate the Attributor with abstract attribute opportunities in
2713 // the function and the information cache with IR information.
2714 A.identifyDefaultAbstractAttributes(*F);
2715 }
2716 A.run();
2717#endif
2718 }
2719
2720 return Changed;
2721 }
2722
2723 bool run(Module &M) {
2724 Logic.clear();
2725
2726 for (Function &F : make_early_inc_range(M)) {
2728 }
2729
2730 bool changed = false;
2731 for (Function &F : M) {
2732 if (F.empty())
2733 continue;
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();
2738 if (auto castinst =
2739 dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
2740 if (castinst->isCast())
2741 if (auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
2742 F = fn;
2743 }
2744 }
2745 if (F && F->getName() == "f90_mzero8") {
2746 IRBuilder<> B(CI);
2747
2748 Value *args[3];
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());
2755
2756 CI->eraseFromParent();
2757 }
2758 }
2759 }
2760 }
2761 }
2762
2763 if (Logic.PostOpt && EnzymeOMPOpt) {
2764 OpenMPOptPass().run(M, Logic.PPC.MAM);
2765 /// Attributor is run second time for promoted args to get attributes.
2766 AttributorPass().run(M, Logic.PPC.MAM);
2767 for (auto &F : M)
2768 if (!F.empty())
2769 PromotePass().run(F, Logic.PPC.FAM);
2770 changed = true;
2771 }
2772
2774 changed = true;
2775 }
2776
2777 std::set<Function *> done;
2778 for (Function &F : M) {
2779 if (F.empty())
2780 continue;
2781
2782 changed |= lowerEnzymeCalls(F, done);
2783 }
2784
2785 for (Function &F : M) {
2786 if (F.empty())
2787 continue;
2788
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();
2793 if (auto castinst =
2794 dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
2795 if (castinst->isCast())
2796 if (auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
2797 F = fn;
2798 }
2799 }
2800 if (F) {
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();
2806 changed = true;
2807 }
2808 if (F->getName().contains("__enzyme_iter") ||
2809 F->getName().contains("__enzyme_ignore_derivatives")) {
2810 CI->replaceAllUsesWith(CI->getArgOperand(0));
2811 CI->eraseFromParent();
2812 changed = true;
2813 }
2814 }
2815 }
2816 }
2817 }
2818 }
2819
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();
2827 if (!fun)
2828 continue;
2829
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");
2835 }
2836 Function *samplefn = GetFunctionFromValue(CI->getOperand(0));
2837 unsigned expected =
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 "
2843 "__enzyme_sample.",
2844 " Expected: ", expected, " got: ", actual);
2845 }
2846 Function *pdf = GetFunctionFromValue(CI->getArgOperand(1));
2847
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;
2853
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 "
2859 "function: ",
2860 *samplefn, " at: ", i, " (", *sample_arg->getType(), ")");
2861 }
2862 if (ci_arg->getType() != pdf_arg->getType()) {
2863 EmitFailure("IllegalSampleType", CI->getDebugLoc(), CI,
2864 "Type of: ", *ci_arg, " (", *ci_arg->getType(),
2865 ")",
2866 " does not match the argument type of the "
2867 "density function: ",
2868 *pdf, " at: ", i, " (", *pdf_arg->getType(), ")");
2869 }
2870 }
2871
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 "
2879 "function: ",
2880 *pdf, " (", *(pdf->arg_end() - 1)->getType(), ")");
2881 }
2882 sample_calls.insert(CI);
2883
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");
2889 }
2890 Value *observed = CI->getOperand(0);
2891 Function *pdf = GetFunctionFromValue(CI->getArgOperand(1));
2892 unsigned expected = pdf->getFunctionType()->getNumParams() - 1;
2893
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);
2900 }
2901
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;
2906
2907 if (ci_arg->getType() != pdf_arg->getType()) {
2908 EmitFailure("IllegalSampleType", CI->getDebugLoc(), CI,
2909 "Type of: ", *ci_arg, " (", *ci_arg->getType(),
2910 ")",
2911 " does not match the argument type of the "
2912 "density function: ",
2913 *pdf, " at: ", i, " (", *pdf_arg->getType(), ")");
2914 }
2915 }
2916
2917 if ((pdf->arg_end() - 1)->getType() != observed->getType()) {
2919 "IllegalSampleType", CI->getDebugLoc(), CI,
2920 "Return type of ", *observed, " (", *observed->getType(),
2921 ")",
2922 " does not match the last argument type of the density "
2923 "function: ",
2924 *pdf, " (", *(pdf->arg_end() - 1)->getType(), ")");
2925 }
2926 observe_calls.insert(CI);
2927 }
2928 }
2929 }
2930 }
2931 }
2932
2933 // Replace calls to __enzyme_sample with the actual sample calls after
2934 // running prob prog
2935 for (auto call : sample_calls) {
2936 Function *samplefn = GetFunctionFromValue(call->getArgOperand(0));
2937
2938 SmallVector<Value *, 2> args;
2939 for (auto it = call->arg_begin() + 3; it != call->arg_end(); it++) {
2940 args.push_back(*it);
2941 }
2942 CallInst *choice =
2943 CallInst::Create(samplefn->getFunctionType(), samplefn, args);
2944
2945 ReplaceInstWithInst(call, choice);
2946 }
2947
2948 for (auto call : observe_calls) {
2949 Value *observed = call->getArgOperand(0);
2950
2951 if (!call->getType()->isVoidTy())
2952 call->replaceAllUsesWith(observed);
2953 call->eraseFromParent();
2954 }
2955
2956 for (const auto &pair : Logic.PPC.cache)
2957 pair.second->eraseFromParent();
2958 Logic.clear();
2959
2960 if (changed && Logic.PostOpt) {
2961 TimeTraceScope timeScope("Enzyme PostOpt", M.getName());
2962
2963 PassBuilder PB;
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);
2975 PM.run(M, MAM);
2976 if (EnzymeOMPOpt) {
2977 OpenMPOptPass().run(M, MAM);
2978 /// Attributor is run second time for promoted args to get attributes.
2979 AttributorPass().run(M, MAM);
2980 for (auto &F : M)
2981 if (!F.empty())
2982 PromotePass().run(F, FAM);
2983 }
2984 }
2985
2986 for (auto &F : M) {
2987 if (!F.empty())
2988 changed |= LowerSparsification(&F);
2989 }
2990 return changed;
2991 }
2992};
2993
2994class EnzymeOldPM : public EnzymeBase, public ModulePass {
2995public:
2996 static char ID;
2997 EnzymeOldPM(bool PostOpt = false) : EnzymeBase(PostOpt), ModulePass(ID) {}
2998
2999 void getAnalysisUsage(AnalysisUsage &AU) const override {
3000 AU.addRequired<TargetLibraryInfoWrapperPass>();
3001
3002 // AU.addRequiredID(LCSSAID);
3003
3004 // LoopInfo is required to ensure that all loops have preheaders
3005 // AU.addRequired<LoopInfoWrapperPass>();
3006
3007 // AU.addRequiredID(llvm::LoopSimplifyID);//<LoopSimplifyWrapperPass>();
3008 }
3009 bool runOnModule(Module &M) override { return run(M); }
3010};
3011
3012} // namespace
3013
3014char EnzymeOldPM::ID = 0;
3015
3016static RegisterPass<EnzymeOldPM> X("enzyme", "Enzyme Pass");
3017
3018ModulePass *createEnzymePass(bool PostOpt) { return new EnzymeOldPM(PostOpt); }
3019
3020#include <llvm-c/Core.h>
3021#include <llvm-c/Types.h>
3022
3023#include "llvm/IR/LegacyPassManager.h"
3024
3025extern "C" void AddEnzymePass(LLVMPassManagerRef PM) {
3026 unwrap(PM)->add(createEnzymePass(/*PostOpt*/ false));
3027}
3028
3029#if LLVM_VERSION_MAJOR >= 22
3030#include "llvm/Plugins/PassPlugin.h"
3031#else
3032#include "llvm/Passes/PassPlugin.h"
3033#endif
3034
3035class EnzymeNewPM final : public EnzymeBase, public PassParent<EnzymeNewPM> {
3037
3038private:
3039 static llvm::AnalysisKey Key;
3040
3041public:
3042 using Result = llvm::PreservedAnalyses;
3043 EnzymeNewPM(bool PostOpt = false) : EnzymeBase(PostOpt) {}
3044
3045 Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
3046 return EnzymeBase::run(M) ? PreservedAnalyses::none()
3047 : PreservedAnalyses::all();
3048 }
3049
3050 static bool isRequired() { return true; }
3051};
3052
3053#undef DEBUG_TYPE
3054AnalysisKey EnzymeNewPM::Key;
3055
3057#include "JLInstSimplify.h"
3058#include "PreserveNVVM.h"
3059#include "SimpleGVN.h"
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"
3083// #include "llvm/Transforms/IPO/MemProfContextDisambiguation.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"
3093#endif
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"
3098
3099static InlineParams getInlineParamsFromOptLevel(OptimizationLevel Level) {
3100#if LLVM_VERSION_MAJOR >= 23
3101 return getInlineParamsFromOptLevel(static_cast<unsigned>(Level));
3102#else
3103 return getInlineParams(Level.getSpeedupLevel(), Level.getSizeLevel());
3104#endif
3105}
3106
3107#include "llvm/Transforms/Scalar/LowerConstantIntrinsics.h"
3108#include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h"
3109namespace llvm {
3110#if LLVM_VERSION_MAJOR >= 24
3111// LLVM 24 replaced the exported -licm-mssa-* cl::opt globals with accessors.
3112static unsigned EnzymeLicmMssaOptCap() { return getLicmMssaOptCap(); }
3113static unsigned EnzymeLicmMssaNoAccForPromotionCap() {
3114 return getLicmMssaNoAccForPromotionCap();
3115}
3116#else
3117extern cl::opt<unsigned> SetLicmMssaNoAccForPromotionCap;
3118extern cl::opt<unsigned> SetLicmMssaOptCap;
3119static unsigned EnzymeLicmMssaOptCap() { return SetLicmMssaOptCap; }
3123#endif
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
3130// extern cl::opt<bool> EnableMatrix;
3131#define EnableMatrix false
3132#define EnableModuleInliner false
3133} // namespace llvm
3134
3135void augmentPassBuilder(llvm::PassBuilder &PB) {
3136
3137 auto prePass = [](ModulePassManager &MPM, OptimizationLevel Level) {
3138 FunctionPassManager OptimizePM;
3139 OptimizePM.addPass(Float2IntPass());
3140 OptimizePM.addPass(LowerConstantIntrinsicsPass());
3141
3142 if (EnableMatrix) {
3143 OptimizePM.addPass(LowerMatrixIntrinsicsPass());
3144 OptimizePM.addPass(EarlyCSEPass());
3145 }
3146
3147 LoopPassManager LPM;
3148 bool LTOPreLink = false;
3149 // First rotate loops that may have been un-rotated by prior passes.
3150 // Disable header duplication at -Oz.
3151#if LLVM_VERSION_MAJOR >= 23
3152 LPM.addPass(LoopRotatePass(/*EnableLoopHeaderDuplication=*/true, LTOPreLink,
3153 /*CheckExitCount=*/true));
3154#else
3155 LPM.addPass(LoopRotatePass(Level != OptimizationLevel::Oz, LTOPreLink));
3156#endif
3157 // Some loops may have become dead by now. Try to delete them.
3158 // FIXME: see discussion in https://reviews.llvm.org/D112851,
3159 // this may need to be revisited once we run GVN before
3160 // loop deletion in the simplification pipeline.
3161 LPM.addPass(LoopDeletionPass());
3162
3163 LPM.addPass(llvm::LoopFullUnrollPass());
3164 OptimizePM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
3165
3166 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizePM)));
3167 };
3168
3169#if LLVM_VERSION_MAJOR >= 20
3170 auto loadPass = [prePass](ModulePassManager &MPM, OptimizationLevel Level,
3171 ThinOrFullLTOPhase Phase)
3172#else
3173 auto loadPass = [prePass](ModulePassManager &MPM, OptimizationLevel Level)
3174#endif
3175 {
3176 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ true));
3177
3178 if (!EnzymeEnable)
3179 return;
3180
3181#if LLVM_VERSION_MAJOR >= 20
3182 // An LTO pre-link pipeline only ever sees one translation unit, so a
3183 // __enzyme_autodiff call here may name a function whose body lives in
3184 // another object file. Differentiating now fails on exactly those calls,
3185 // while the post-link run sees the whole program: for full LTO that is the
3186 // FullLinkTimeOptimizationEarly callback below, for ThinLTO this same
3187 // callback re-entered with ThinLTOPostLink. Defer to it, restoring the
3188 // linkage PreserveNVVM changed so the bitcode exports nothing new, while
3189 // still keeping the relevant functions alive until then.
3190 //
3191 // A ThinLTO post-link pipeline at O0 invokes no callbacks at all, so that
3192 // run would never come. The pre-link run cannot know the post-link level,
3193 // but the driver passes the compile -O level on to the linker, so an O0
3194 // ThinLTO compile still differentiates here.
3195 //
3196 // This requires the plugin to be loaded by the linker as well, e.g.
3197 // -Wl,--load-pass-plugin=LLDEnzyme-<N>.so. Pass -enzyme-lto-prelink=1 to
3198 // restore the old behaviour when that is not possible.
3199 if (!EnzymeLTOPreLink && (Phase == ThinOrFullLTOPhase::FullLTOPreLink ||
3200 (Phase == ThinOrFullLTOPhase::ThinLTOPreLink &&
3201 Level != OptimizationLevel::O0))) {
3202 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ false,
3203 /*PreserveCustomRuleLinkage*/ true,
3204 /*LTOPreLink*/ true));
3205 return;
3206 }
3207#endif
3208
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));
3217#else
3218 OptimizerPM.addPass(llvm::GVNPass());
3219 OptimizerPM.addPass(llvm::SROAPass());
3220#endif
3221 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizerPM)));
3222 MPM.addPass(EnzymeNewPM(/*PostOpt=*/true));
3223 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ false));
3224#if LLVM_VERSION_MAJOR >= 16
3225 OptimizerPM2.addPass(llvm::GVNPass());
3226 OptimizerPM2.addPass(llvm::SROAPass(llvm::SROAOptions::PreserveCFG));
3227#else
3228 OptimizerPM2.addPass(llvm::GVNPass());
3229 OptimizerPM2.addPass(llvm::SROAPass());
3230#endif
3231
3232 LoopPassManager LPM1;
3233 LPM1.addPass(LoopDeletionPass());
3234 OptimizerPM2.addPass(createFunctionToLoopPassAdaptor(std::move(LPM1)));
3235
3236 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizerPM2)));
3237 MPM.addPass(GlobalOptPass());
3238 };
3239 // TODO need for perf reasons to move Enzyme pass to the pre vectorization.
3240 PB.registerOptimizerEarlyEPCallback(loadPass);
3241
3242 auto loadNVVM = [](ModulePassManager &MPM, OptimizationLevel) {
3243 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ true));
3244 };
3245
3246 // We should register at vectorizer start for consistency, however,
3247 // that requires a functionpass, and we have a modulepass.
3248 // PB.registerVectorizerStartEPCallback(loadPass);
3249 PB.registerPipelineStartEPCallback(loadNVVM);
3250 PB.registerFullLinkTimeOptimizationEarlyEPCallback(loadNVVM);
3251
3252 auto preLTOPass = [](ModulePassManager &MPM, OptimizationLevel Level) {
3253 // Create a function that performs CFI checks for cross-DSO calls with
3254 // targets in the current module.
3255 MPM.addPass(CrossDSOCFIPass());
3256
3257 if (Level == OptimizationLevel::O0) {
3258 return;
3259 }
3260
3261 // Try to run OpenMP optimizations, quick no-op if no OpenMP metadata
3262 // present.
3263#if LLVM_VERSION_MAJOR >= 16
3264 MPM.addPass(OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink));
3265#else
3266 MPM.addPass(OpenMPOptPass());
3267#endif
3268
3269 // Remove unused virtual tables to improve the quality of code
3270 // generated by whole-program devirtualization and bitset lowering.
3271 MPM.addPass(GlobalDCEPass());
3272
3273 // Do basic inference of function attributes from known properties of
3274 // system libraries and other oracles.
3275 MPM.addPass(InferFunctionAttrsPass());
3276
3277#if LLVM_VERSION_MAJOR >= 23
3278 if (Level > OptimizationLevel::O1)
3279#else
3280 if (Level.getSpeedupLevel() > 1)
3281#endif
3282 {
3283 MPM.addPass(createModuleToFunctionPassAdaptor(CallSiteSplittingPass(),
3285
3286 // Indirect call promotion. This should promote all the targets that
3287 // are left by the earlier promotion pass that promotes intra-module
3288 // targets. This two-step promotion is to save the compile time. For
3289 // LTO, it should produce the same result as if we only do promotion
3290 // here.
3291 // MPM.addPass(PGOIndirectCallPromotion(
3292 // true /* InLTO */, PGOOpt && PGOOpt->Action ==
3293 // PGOOptions::SampleUse));
3294
3295 // Propagate constants at call sites into the functions they call.
3296 // This opens opportunities for globalopt (and inlining) by
3297 // substituting function pointers passed as arguments to direct uses
3298 // of functions.
3299#if LLVM_VERSION_MAJOR >= 23
3300 MPM.addPass(IPSCCPPass(IPSCCPOptions(/*AllowFuncSpec=*/true)));
3301#elif LLVM_VERSION_MAJOR >= 16
3302 MPM.addPass(IPSCCPPass(IPSCCPOptions(/*AllowFuncSpec=*/
3303 Level != OptimizationLevel::Os &&
3304 Level != OptimizationLevel::Oz)));
3305#else
3306 MPM.addPass(IPSCCPPass());
3307#endif
3308
3309 // Attach metadata to indirect call sites indicating the set of
3310 // functions they may target at run-time. This should follow IPSCCP.
3311 MPM.addPass(CalledValuePropagationPass());
3312 }
3313
3314 // Now deduce any function attributes based in the current code.
3315 MPM.addPass(
3316 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3317
3318 // Do RPO function attribute inference across the module to
3319 // forward-propagate attributes where applicable.
3320 // FIXME: Is this really an optimization rather than a
3321 // canonicalization?
3322 MPM.addPass(ReversePostOrderFunctionAttrsPass());
3323
3324 // Use in-range annotations on GEP indices to split globals where
3325 // beneficial.
3326 MPM.addPass(GlobalSplitPass());
3327
3328 // Run whole program optimization of virtual call when the list of
3329 // callees is fixed. MPM.addPass(WholeProgramDevirtPass(ExportSummary,
3330 // nullptr));
3331
3332 // Stop here at -O1.
3333 if (Level == OptimizationLevel::O1) {
3334 return;
3335 }
3336
3337 // Optimize globals to try and fold them into constants.
3338 MPM.addPass(GlobalOptPass());
3339
3340 // Promote any localized globals to SSA registers.
3341 MPM.addPass(createModuleToFunctionPassAdaptor(PromotePass()));
3342
3343 // Linking modules together can lead to duplicate global constant,
3344 // only keep one copy of each constant.
3345 MPM.addPass(ConstantMergePass());
3346
3347 // Remove unused arguments from functions.
3348 MPM.addPass(DeadArgumentEliminationPass());
3349
3350 // Reduce the code after globalopt and ipsccp. Both can open up
3351 // significant simplification opportunities, and both can propagate
3352 // functions through function pointers. When this happens, we often
3353 // have to resolve varargs calls, etc, so let instcombine do this.
3354 FunctionPassManager PeepholeFPM;
3355 PeepholeFPM.addPass(InstCombinePass());
3356#if LLVM_VERSION_MAJOR >= 23
3357 if (Level > OptimizationLevel::O1)
3358#else
3359 if (Level.getSpeedupLevel() > 1)
3360#endif
3361 PeepholeFPM.addPass(AggressiveInstCombinePass());
3362
3363 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM),
3365
3366 // Note: historically, the PruneEH pass was run first to deduce
3367 // nounwind and generally clean up exception handling overhead. It
3368 // isn't clear this is valuable as the inliner doesn't currently care
3369 // whether it is inlining an invoke or a call. Run the inliner now.
3370 if (EnableModuleInliner) {
3371 MPM.addPass(ModuleInlinerPass(getInlineParamsFromOptLevel(Level),
3373 ThinOrFullLTOPhase::FullLTOPostLink));
3374 } else {
3375 MPM.addPass(ModuleInlinerWrapperPass(
3377 /* MandatoryFirst */ true,
3378 InlineContext{ThinOrFullLTOPhase::FullLTOPostLink,
3379 InlinePass::CGSCCInliner}));
3380 }
3381
3382 // Perform context disambiguation after inlining, since that would
3383 // reduce the amount of additional cloning required to distinguish the
3384 // allocation contexts. if (EnableMemProfContextDisambiguation)
3385 // MPM.addPass(MemProfContextDisambiguation());
3386
3387 // Optimize globals again after we ran the inliner.
3388 MPM.addPass(GlobalOptPass());
3389
3390 // Run the OpenMPOpt pass again after global optimizations.
3391#if LLVM_VERSION_MAJOR >= 16
3392 MPM.addPass(OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink));
3393#else
3394 MPM.addPass(OpenMPOptPass());
3395#endif
3396
3397 // Garbage collect dead functions.
3398 MPM.addPass(GlobalDCEPass());
3399
3400 // If we didn't decide to inline a function, check to see if we can
3401 // transform it to pass arguments by value instead of by reference.
3402 MPM.addPass(
3403 createModuleToPostOrderCGSCCPassAdaptor(ArgumentPromotionPass()));
3404
3405 FunctionPassManager FPM;
3406 // The IPO Passes may leave cruft around. Clean up after them.
3407 FPM.addPass(InstCombinePass());
3408
3410 FPM.addPass(ConstraintEliminationPass());
3411
3412 FPM.addPass(JumpThreadingPass());
3413
3414 // Do a post inline PGO instrumentation and use pass. This is a context
3415 // sensitive PGO pass.
3416#if 0
3417 if (PGOOpt) {
3418 if (PGOOpt->CSAction == PGOOptions::CSIRInstr)
3419 addPGOInstrPasses(MPM, Level, /* RunProfileGen */ true,
3420 /* IsCS */ true, PGOOpt->CSProfileGenFile,
3421 PGOOpt->ProfileRemappingFile,
3422 ThinOrFullLTOPhase::FullLTOPostLink, PGOOpt->FS);
3423 else if (PGOOpt->CSAction == PGOOptions::CSIRUse)
3424 addPGOInstrPasses(MPM, Level, /* RunProfileGen */ false,
3425 /* IsCS */ true, PGOOpt->ProfileFile,
3426 PGOOpt->ProfileRemappingFile,
3427 ThinOrFullLTOPhase::FullLTOPostLink, PGOOpt->FS);
3428 }
3429#endif
3430
3431 // Break up allocas
3432#if LLVM_VERSION_MAJOR >= 16
3433 FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
3434#else
3435 FPM.addPass(SROAPass());
3436#endif
3437
3438 // LTO provides additional opportunities for tailcall elimination due
3439 // to link-time inlining, and visibility of nocapture attribute.
3440 FPM.addPass(TailCallElimPass());
3441
3442 // Run a few AA driver optimizations here and now to cleanup the code.
3443 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
3445
3446 MPM.addPass(
3447 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3448
3449 // Require the GlobalsAA analysis for the module so we can query it
3450 // within MainFPM.
3451 MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
3452 };
3453
3454 auto loadLTO = [preLTOPass, loadPass](ModulePassManager &MPM,
3455 OptimizationLevel Level) {
3456 preLTOPass(MPM, Level);
3457 MPM.addPass(
3458 createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
3459
3460 // Require the GlobalsAA analysis for the module so we can query it
3461 // within MainFPM.
3462 MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
3463
3464 // Invalidate AAManager so it can be recreated and pick up the newly
3465 // available GlobalsAA.
3466 MPM.addPass(
3467 createModuleToFunctionPassAdaptor(InvalidateAnalysisPass<AAManager>()));
3468
3469 FunctionPassManager MainFPM;
3470#if LLVM_VERSION_MAJOR >= 22
3471 MainFPM.addPass(createFunctionToLoopPassAdaptor(
3473 /*AllowSpeculation=*/true),
3474 /*USeMemorySSA=*/true));
3475#else
3476 MainFPM.addPass(createFunctionToLoopPassAdaptor(
3478 /*AllowSpeculation=*/true),
3479 /*USeMemorySSA=*/true, /*UseBlockFrequencyInfo=*/false));
3480#endif
3481
3482 if (RunNewGVN)
3483 MainFPM.addPass(NewGVNPass());
3484 else
3485 MainFPM.addPass(GVNPass());
3486
3487 // Remove dead memcpy()'s.
3488 MainFPM.addPass(MemCpyOptPass());
3489
3490 // Nuke dead stores.
3491 MainFPM.addPass(DSEPass());
3492#if LLVM_VERSION_MAJOR >= 17
3493 MainFPM.addPass(MoveAutoInitPass());
3494#endif
3495 MainFPM.addPass(MergedLoadStoreMotionPass());
3496
3497 LoopPassManager LPM;
3498#if LLVM_VERSION_MAJOR >= 23
3499 if (EnableLoopFlatten && Level > OptimizationLevel::O1)
3500#else
3501 if (EnableLoopFlatten && Level.getSpeedupLevel() > 1)
3502#endif
3503 LPM.addPass(LoopFlattenPass());
3504 LPM.addPass(IndVarSimplifyPass());
3505 LPM.addPass(LoopDeletionPass());
3506 // FIXME: Add loop interchange.
3507
3508#if LLVM_VERSION_MAJOR >= 20
3509 loadPass(MPM, Level, ThinOrFullLTOPhase::None);
3510#else
3511 loadPass(MPM, Level);
3512#endif
3513 };
3514 PB.registerFullLinkTimeOptimizationEarlyEPCallback(loadLTO);
3515}
3516
3517bool registerFixupJuliaPass(llvm::StringRef Name, llvm::ModulePassManager &MPM);
3518
3519extern "C" void registerEnzymeAndPassPipeline(llvm::PassBuilder &PB,
3520 bool augment = false) {
3521 if (augment) {
3523 }
3524 PB.registerPipelineParsingCallback(
3525 [](llvm::StringRef Name, llvm::ModulePassManager &MPM,
3526 llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
3527 if (Name == "enzyme") {
3528 MPM.addPass(EnzymeNewPM());
3529 return true;
3530 }
3531 if (registerFixupJuliaPass(Name, MPM)) {
3532 return true;
3533 }
3534 if (Name == "preserve-nvvm") {
3535 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ true));
3536 return true;
3537 }
3538 if (Name == "preserve-nvvm-end") {
3539 MPM.addPass(PreserveNVVMNewPM(/*Begin*/ false));
3540 return true;
3541 }
3542 if (Name == "print-type-analysis") {
3543 MPM.addPass(TypeAnalysisPrinterNewPM());
3544 return true;
3545 }
3546 if (Name == "print-activity-analysis") {
3547 MPM.addPass(ActivityAnalysisPrinterNewPM());
3548 return true;
3549 }
3550 return false;
3551 });
3552 PB.registerPipelineParsingCallback(
3553 [](llvm::StringRef Name, llvm::FunctionPassManager &FPM,
3554 llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
3555 if (Name == "jl-inst-simplify") {
3556 FPM.addPass(JLInstSimplifyNewPM());
3557 return true;
3558 }
3559 if (Name == "simple-gvn") {
3560 FPM.addPass(SimpleGVNNewPM());
3561 return true;
3562 }
3563 return false;
3564 });
3565}
3566
3567extern "C" void registerEnzyme(llvm::PassBuilder &PB) {
3568#ifdef ENZYME_RUNPASS
3569 registerEnzymeAndPassPipeline(PB, /*augment*/ true);
3570#else
3571 registerEnzymeAndPassPipeline(PB, /*augment*/ false);
3572#endif
3573}
3574
3575extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
3577 return {LLVM_PLUGIN_API_VERSION, "EnzymeNewPM", "v0.1", registerEnzyme};
3578}
DIFFE_TYPE
Potential differentiable argument classifications.
llvm::cl::opt< bool > EnzymeEnable
static void loadPass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM)
#define EagerlyInvalidateAnalyses
Definition Enzyme.cpp:3125
#define EnableLoopFlatten
Definition Enzyme.cpp:3124
void augmentPassBuilder(llvm::PassBuilder &PB)
Definition Enzyme.cpp:3135
::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo()
Definition Enzyme.cpp:3576
ModulePass * createEnzymePass(bool PostOpt)
Definition Enzyme.cpp:3018
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)
Definition Enzyme.cpp:3099
void AddEnzymePass(LLVMPassManagerRef PM)
Definition Enzyme.cpp:3025
llvm::cl::opt< bool > EnzymeAttributor("enzyme-attributor", cl::init(false), cl::Hidden, cl::desc("Run attributor post Enzyme"))
#define EnableModuleInliner
Definition Enzyme.cpp:3132
#define EnableConstraintElimination
Definition Enzyme.cpp:3127
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)
Definition Enzyme.cpp:3567
#define UseInlineAdvisor
Definition Enzyme.cpp:3128
void registerEnzymeAndPassPipeline(llvm::PassBuilder &PB, bool augment=false)
Definition Enzyme.cpp:3519
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"))
#define EnableMatrix
Definition Enzyme.cpp:3131
bool registerFixupJuliaPass(llvm::StringRef Name, llvm::ModulePassManager &MPM)
#define RunNewGVN
Definition Enzyme.cpp:3126
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[]
Definition EnzymeLogic.h:57
TruncateMode
@ TruncOpFullModuleMode
@ TruncOpMode
@ TruncMemMode
Value * simplifyLoad(Value *V, size_t valSz, size_t preOffset)
Definition Utils.cpp:3664
bool attributeKnownFunctions(llvm::Function &F)
Definition Utils.cpp:139
Function * GetFunctionFromValue(Value *fn)
Definition Utils.cpp:3811
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
Definition Utils.cpp:4026
static llvm::StringRef getFuncName(llvm::Function *called)
Definition Utils.h:1461
BATCH_TYPE
Definition Utils.h:519
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
@ Args
Return is a struct of all args.
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
Definition Utils.h:847
static bool isGPUArch(const llvm::Triple &TT)
Definition Utils.h:162
static llvm::PointerType * getUnqual(llvm::Type *T)
Definition Utils.h:1329
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
Definition Utils.h:1324
static void insertBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
Definition Utils.h:2562
static llvm::Instruction * getFirstNonPHI(llvm::BasicBlock *B)
Definition Utils.h:2550
ProbProgMode
Definition Utils.h:533
static void addCallSiteNoCapture(llvm::CallBase *call, size_t idx)
Definition Utils.h:2599
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
Definition Utils.h:2033
static void moveBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
Definition Utils.h:2571
DerivativeMode
Definition Utils.h:524
static llvm::Instruction * createUnconditionalBranch(llvm::BasicBlock *Target, PosT InsertBefore)
Definition Utils.h:127
static DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode, bool integersAreConstant, std::set< llvm::Type * > &seen)
Attempt to automatically detect the differentiable classification based off of a given type.
Definition Utils.h:653
return structtype if recursive function
llvm::Function * fn
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).
PreProcessCache PPC
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
static bool isRequired()
Definition Enzyme.cpp:3050
EnzymeNewPM(bool PostOpt=false)
Definition Enzyme.cpp:3043
llvm::PreservedAnalyses Result
Definition Enzyme.cpp:3042
Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM)
Definition Enzyme.cpp:3045
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.
Definition TypeTree.h:72
TypeTree Only(int Off, llvm::Instruction *orig) const
Prepend an offset to all mappings.
Definition TypeTree.h:473
static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx)
Definition TypeTree.h:86
bool insert(const std::vector< int > Seq, ConcreteType CT, bool PointerIntSame=false)
Return if changed.
Definition TypeTree.h:236
@ 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()
Definition Enzyme.cpp:3120
static unsigned EnzymeLicmMssaOptCap()
Definition Enzyme.cpp:3119
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.