32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/ADT/SmallVector.h"
36#include "llvm/IR/BasicBlock.h"
37#include "llvm/IR/DebugInfoMetadata.h"
38#include "llvm/IR/Dominators.h"
39#include "llvm/IR/IRBuilder.h"
40#include "llvm/IR/Instructions.h"
41#include "llvm/IR/Type.h"
42#include "llvm/IR/Value.h"
44#include "llvm/Transforms/Utils/BasicBlockUtils.h"
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/ErrorHandling.h"
55bool elementwiseReadForContext(
const Instruction *orig,
const Value *origptr) {
57 if (
const Function *F = orig->getFunction()) {
58 if (F->hasFnAttribute(
"enzyme_elementwise_read")) {
64 if (
auto *arg = dyn_cast<Argument>(base)) {
65 if (
const Function *F = arg->getParent()) {
66 return F->getAttributes().hasParamAttr(arg->getArgNo(),
67 "enzyme_elementwise_read");
74DiffeGradientUtils::DiffeGradientUtils(
75 EnzymeLogic &Logic, Function *newFunc_, Function *oldFunc_,
77 ValueToValueMapTy &invertedPointers_,
78 const SmallPtrSetImpl<Value *> &constantvalues_,
79 const SmallPtrSetImpl<Value *> &returnvals_,
DIFFE_TYPE ActiveReturn,
80 bool shadowReturnUsed, ArrayRef<DIFFE_TYPE> constant_values,
81 llvm::ValueMap<const llvm::Value *, AssertingReplacingVH> &origToNew_,
82 DerivativeMode mode,
bool runtimeActivity,
bool strongZero,
unsigned width,
84 :
GradientUtils(Logic, newFunc_, oldFunc_, TLI, TA, TR, invertedPointers_,
85 constantvalues_, returnvals_, ActiveReturn,
86 shadowReturnUsed, constant_values, origToNew_, mode,
87 runtimeActivity, strongZero, width, omp) {
88 if (oldFunc_->empty())
90 assert(reverseBlocks.size() == 0);
96 for (BasicBlock *BB : originalBlocks) {
97 if (BB == inversionAllocs)
100 BasicBlock::Create(BB->getContext(),
"invert" + BB->getName(), newFunc);
101 reverseBlocks[BB].push_back(RBB);
102 reverseBlockToPrimal[RBB] = BB;
104 assert(reverseBlocks.size() != 0);
109 bool strongZero,
unsigned width, Function *todiff, TargetLibraryInfo &TLI,
111 bool shadowReturn,
bool diffeReturnArg, ArrayRef<DIFFE_TYPE> constant_args,
112 bool returnTape,
bool returnPrimal, Type *additionalArg,
bool omp) {
120 SmallPtrSet<Instruction *, 4> constants;
121 SmallPtrSet<Instruction *, 20> nonconstant;
122 SmallPtrSet<Value *, 2> returnvals;
123 llvm::ValueMap<const llvm::Value *, AssertingReplacingVH> originalToNew;
125 SmallPtrSet<Value *, 4> constant_values;
126 SmallPtrSet<Value *, 4> nonconstant_values;
141 llvm_unreachable(
"invalid DerivativeMode: ReverseModePrimal\n");
145 prefix += std::to_string(width);
149 nonconstant_values, returnvals, returnTape, returnPrimal,
151 prefix +
oldFunc->getName(), &originalToNew,
152 diffeReturnArg, additionalArg);
159 auto toarg = todiff->arg_begin();
160 auto olarg =
oldFunc->arg_begin();
161 for (; toarg != todiff->arg_end(); ++toarg, ++olarg) {
164 auto fd = oldTypeInfo.
Arguments.find(toarg);
165 assert(fd != oldTypeInfo.
Arguments.end());
167 std::pair<Argument *, TypeTree>(olarg, fd->second));
174 std::pair<Argument *, std::set<int64_t>>(olarg, cfd->second));
186 nonconstant_values, retType, shadowReturn, constant_args, originalToNew,
198 if (
auto arg = dyn_cast<Argument>(val))
199 assert(arg->getParent() ==
oldFunc);
200 if (
auto inst = dyn_cast<Instruction>(val))
201 assert(inst->getParent()->getParent() ==
oldFunc);
208 entryBuilder.setFastMathFlags(
getFast());
210 entryBuilder.CreateAlloca(type,
nullptr, val->getName() +
"'de");
212 oldFunc->getParent()->getDataLayout().getPrefTypeAlign(type);
217#if LLVM_VERSION_MAJOR < 17
218 if (val->getContext().supportsTypedPointers()) {
219 assert(
differentials[val]->getType()->getPointerElementType() == type);
227 if (
auto arg = dyn_cast<Argument>(val))
228 assert(arg->getParent() ==
oldFunc);
229 if (
auto inst = dyn_cast<Instruction>(val))
230 assert(inst->getParent()->getParent() ==
oldFunc);
234 llvm::errs() << *
newFunc <<
"\n";
235 llvm::errs() << *val <<
"\n";
236 assert(0 &&
"getting diffe of constant value");
242 if (val->getType()->isPointerTy()) {
243 llvm::errs() << *
newFunc <<
"\n";
244 llvm::errs() << *val <<
"\n";
246 assert(!val->getType()->isPointerTy());
247 assert(!val->getType()->isVoidTy());
253 Value *val, Value *dif, IRBuilder<> &BuilderM, Type *addingType,
254 unsigned start,
unsigned size, llvm::ArrayRef<llvm::Value *> idxs,
255 llvm::Value *mask,
size_t ignoreFirstSlicesOfDif) {
257 auto &DL =
oldFunc->getParent()->getDataLayout();
258 Type *VT = val->getType();
259 for (
auto cv : idxs) {
260 auto i = dyn_cast<ConstantInt>(cv)->getSExtValue();
261 if (
auto ST = dyn_cast<StructType>(VT)) {
262 VT = ST->getElementType(i);
265 if (
auto AT = dyn_cast<ArrayType>(VT)) {
266 assert((
size_t)i < AT->getNumElements());
267 VT = AT->getElementType();
270 assert(0 &&
"illegal indexing type");
272 auto storeSize = (DL.getTypeSizeInBits(VT) + 7) / 8;
274 assert(start < storeSize);
275 assert(start + size <= storeSize);
279 if (start == 0 && size == storeSize && !isa<StructType>(VT)) {
281 SmallVector<unsigned, 1> eidxs;
282 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
283 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
286 addingType, idxs, mask);
288 SmallVector<SelectInst *, 4> res;
289 for (
unsigned j = 0; j <
getWidth(); j++) {
290 SmallVector<Value *, 1> lidxs;
291 SmallVector<unsigned, 1> eidxs = {(unsigned)j};
293 ConstantInt::get(Type::getInt32Ty(val->getContext()), j));
294 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
295 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
297 for (
auto idx : idxs) {
298 lidxs.push_back(idx);
301 BuilderM, addingType, lidxs, mask))
307 if (
auto ST = dyn_cast<StructType>(VT)) {
308 auto SL = DL.getStructLayout(ST);
309 auto left_idx = SL->getElementContainingOffset(start);
310 auto right_idx = ST->getNumElements();
311 if (storeSize != start + size) {
312 right_idx = SL->getElementContainingOffset(start + size);
315 if (SL->getElementOffset(right_idx) != start + size)
318 SmallVector<SelectInst *, 4> res;
319 for (
auto i = left_idx; i < right_idx; i++) {
320 auto subType = ST->getElementType(i);
321 SmallVector<Value *, 1> lidxs(idxs.begin(), idxs.end());
322 lidxs.push_back(ConstantInt::get(Type::getInt32Ty(val->getContext()), i));
324 (i == left_idx) ? (start - (
unsigned)SL->getElementOffset(i)) : 0;
325 auto subTypeSize = (DL.getTypeSizeInBits(subType) + 7) / 8;
326 auto sub_end = (i == right_idx - 1)
327 ?
min(start + size - (
unsigned)SL->getElementOffset(i),
328 (
unsigned)subTypeSize)
331 addToDiffe(val, dif, BuilderM, addingType, sub_start,
332 sub_end - sub_start, lidxs, mask, ignoreFirstSlicesOfDif))
338 if (
auto AT = dyn_cast<ArrayType>(VT)) {
339 auto subType = AT->getElementType();
340 auto subTypeSize = (DL.getTypeSizeInBits(subType) + 7) / 8;
341 auto left_idx = start / subTypeSize;
342 auto right_idx = AT->getNumElements();
343 if (storeSize != start + size) {
344 right_idx = (start + size) / subTypeSize;
347 if (right_idx * subTypeSize != start + size)
350 SmallVector<SelectInst *, 4> res;
351 for (
auto i = left_idx; i < right_idx; i++) {
352 SmallVector<Value *, 1> lidxs(idxs.begin(), idxs.end());
353 lidxs.push_back(ConstantInt::get(Type::getInt32Ty(val->getContext()), i));
354 auto sub_start = (i == left_idx) ? (start - (i * subTypeSize)) : 0;
355 auto sub_end = (i == right_idx - 1)
356 ?
min(start + size - (
unsigned)(i * subTypeSize),
357 (
unsigned)subTypeSize)
360 addToDiffe(val, dif, BuilderM, addingType, sub_start,
361 sub_end - sub_start, lidxs, mask, ignoreFirstSlicesOfDif))
367 if (
auto VecT = dyn_cast<VectorType>(VT)) {
368 if (!VecT->getElementCount().isScalable()) {
369 Type *elemTy = VecT->getElementType();
370 auto elemBytes = (DL.getTypeSizeInBits(elemTy) + 7) / 8;
373 if (elemBytes != 0 && start % elemBytes == 0 && size % elemBytes == 0) {
374 unsigned left_idx = start / elemBytes;
375 unsigned right_idx = (start + size) / elemBytes;
377 unsigned numElts = VecT->getElementCount().getFixedValue();
378 if (left_idx > numElts)
380 if (right_idx > numElts)
383 auto maskVec = [&](Value *dsub) -> Value * {
384 if (left_idx == 0 && right_idx == numElts)
386 Value *masked = Constant::getNullValue(VT);
387 for (
unsigned i = left_idx; i < right_idx; i++) {
389 ConstantInt::get(Type::getInt32Ty(val->getContext()), i);
390 Value *el = BuilderM.CreateExtractElement(dsub, vidx);
391 masked = BuilderM.CreateInsertElement(masked, el, vidx);
397 SmallVector<unsigned, 1> eidxs;
398 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif))
399 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
402 return addToDiffe(val, maskVec(subdif), BuilderM, addingType, idxs,
405 SmallVector<SelectInst *, 4> res;
406 for (
unsigned j = 0; j <
getWidth(); j++) {
407 SmallVector<Value *, 1> lidxs;
408 SmallVector<unsigned, 1> eidxs = {(unsigned)j};
411 ConstantInt::get(Type::getInt32Ty(val->getContext()), j));
412 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif))
413 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
414 for (
auto idx : idxs)
415 lidxs.push_back(idx);
418 for (
auto v :
addToDiffe(val, maskVec(subdif), BuilderM, addingType,
428 auto addingSize = addingType ? (DL.getTypeSizeInBits(addingType) + 7) / 8 : 0;
429 if (size < storeSize && addingType && !mask && addingSize != 0 &&
430 size % addingSize == 0 && start % addingSize == 0) {
432 SmallVector<unsigned, 1> eidxs;
433 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
434 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
438 auto Al = A.CreateAlloca(subdif->getType());
439 BuilderM.CreateStore(subdif, Al);
441 Value *ptr_i8 = BuilderM.CreatePointerCast(
442 Al,
getUnqual(Type::getInt8Ty(val->getContext())));
443 BuilderM.CreateMemSet(ptr_i8, BuilderM.getInt8(0), start,
446 if (storeSize > start + size) {
447 Value *ptr_i8 = BuilderM.CreatePointerCast(
448 Al,
getUnqual(Type::getInt8Ty(val->getContext())));
449 Value *gep = BuilderM.CreateInBoundsGEP(
450 Type::getInt8Ty(val->getContext()), ptr_i8,
451 ConstantInt::get(Type::getInt64Ty(val->getContext()),
453 BuilderM.CreateMemSet(gep, BuilderM.getInt8(0),
454 storeSize - start - size, MaybeAlign(1));
456 Value *new_dif = BuilderM.CreateLoad(subdif->getType(), Al);
457 return addToDiffe(val, new_dif, BuilderM, addingType, 0, storeSize, idxs,
460 SmallVector<unsigned, 1> eidxs;
461 for (
auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
462 eidxs.push_back((
unsigned)cast<ConstantInt>(idx)->getZExtValue());
466 auto Al = A.CreateAlloca(subdif->getType());
467 BuilderM.CreateStore(subdif, Al);
469 for (
unsigned j = 0; j <
getWidth(); j++) {
471 ConstantInt::get(Type ::getInt64Ty(val->getContext()), 0),
472 ConstantInt::get(Type::getInt32Ty(val->getContext()), j)};
473 Value *Alj = BuilderM.CreateInBoundsGEP(subdif->getType(), Al, Idxs);
474 Value *ptr_i8 = BuilderM.CreatePointerCast(
475 Alj,
getUnqual(Type::getInt8Ty(val->getContext())));
476 BuilderM.CreateMemSet(ptr_i8, BuilderM.getInt8(0), start,
480 if (storeSize > start + size) {
481 for (
unsigned j = 0; j <
getWidth(); j++) {
483 ConstantInt::get(Type ::getInt64Ty(val->getContext()), 0),
484 ConstantInt::get(Type::getInt32Ty(val->getContext()), j)};
485 Value *Alj = BuilderM.CreateInBoundsGEP(subdif->getType(), Al, Idxs);
486 Value *ptr_i8 = BuilderM.CreatePointerCast(
487 Alj,
getUnqual(Type::getInt8Ty(val->getContext())));
488 Value *gep = BuilderM.CreateInBoundsGEP(
489 Type::getInt8Ty(val->getContext()), ptr_i8,
490 ConstantInt::get(Type::getInt64Ty(val->getContext()),
492 BuilderM.CreateMemSet(gep, BuilderM.getInt8(0),
493 storeSize - start - size, MaybeAlign(1));
496 Value *new_dif = BuilderM.CreateLoad(subdif->getType(), Al);
497 return addToDiffe(val, new_dif, BuilderM, addingType, 0, storeSize, idxs,
503 llvm::raw_string_ostream ss(s);
504 ss <<
"Unhandled accumulate with partial sizes:\n";
505 ss <<
" val: " << *val <<
"\n";
506 ss <<
" VT: " << *VT <<
" idxs:{";
507 for (
auto idx : idxs)
509 ss <<
"} start=" << start <<
" size=" << size <<
" storeSize=" << storeSize
510 <<
" val=" << *val <<
"\n";
512 ss <<
" addingType: " << *addingType <<
"\n";
514 ss <<
" addingType: null\n";
517 nullptr,
nullptr, wrap(&BuilderM));
520 if (
auto inst = dyn_cast<Instruction>(val))
521 EmitFailure(
"UnhandledAccumulate", inst->getDebugLoc(), inst, ss.str());
523 llvm::errs() << ss.str() <<
"\n";
524 llvm_unreachable(
"UnhandledAccumulate");
531#if LLVM_VERSION_MAJOR >= 22
532 return cst->isNullValue() || cst->isNegativeZeroValue();
534 return cst->isZeroValue();
537SmallVector<SelectInst *, 4>
539 Type *addingType, ArrayRef<Value *> idxs,
545 if (
auto arg = dyn_cast<Argument>(val))
546 assert(arg->getParent() ==
oldFunc);
547 if (
auto inst = dyn_cast<Instruction>(val))
548 assert(inst->getParent()->getParent() ==
oldFunc);
551 SmallVector<SelectInst *, 4> addedSelects;
553 auto faddForNeg = [&](Value *old, Value *inc,
bool san) {
554 if (
auto bi = dyn_cast<BinaryOperator>(inc)) {
555 if (
auto ci = dyn_cast<ConstantFP>(bi->getOperand(0))) {
556 if (bi->getOpcode() == BinaryOperator::FSub && ci->isZero()) {
557 Value *res = BuilderM.CreateFSub(old, bi->getOperand(1));
564 Value *res = BuilderM.CreateFAdd(old, inc);
570 auto faddForSelect = [&](Value *old, Value *dif) -> Value * {
572 if (SelectInst *select = dyn_cast<SelectInst>(dif)) {
573 if (Constant *ci = dyn_cast<Constant>(select->getTrueValue())) {
575 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
576 select->getCondition(), old,
577 faddForNeg(old, select->getFalseValue(),
false)));
578 addedSelects.push_back(res);
582 if (Constant *ci = dyn_cast<Constant>(select->getFalseValue())) {
584 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
585 select->getCondition(),
586 faddForNeg(old, select->getTrueValue(),
false), old));
587 addedSelects.push_back(res);
594 if (BitCastInst *bc = dyn_cast<BitCastInst>(dif)) {
595 if (SelectInst *select = dyn_cast<SelectInst>(bc->getOperand(0))) {
596 if (Constant *ci = dyn_cast<Constant>(select->getTrueValue())) {
598 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
599 select->getCondition(), old,
601 BuilderM.CreateCast(bc->getOpcode(),
602 select->getFalseValue(),
605 addedSelects.push_back(res);
609 if (Constant *ci = dyn_cast<Constant>(select->getFalseValue())) {
611 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
612 select->getCondition(),
614 BuilderM.CreateCast(bc->getOpcode(),
615 select->getTrueValue(),
619 addedSelects.push_back(res);
627 return faddForNeg(old, dif,
true);
630 if (val->getType()->isPointerTy()) {
631 llvm::errs() << *
newFunc <<
"\n";
632 llvm::errs() << *val <<
"\n";
635 llvm::errs() << *
newFunc <<
"\n";
636 llvm::errs() << *val <<
"\n";
638 assert(!val->getType()->isPointerTy());
644 if (idxs.size() != 0) {
645 SmallVector<Value *, 4> sv = {
646 ConstantInt::get(Type::getInt32Ty(val->getContext()), 0)};
649 ptr = BuilderM.CreateGEP(
getShadowType(val->getType()), ptr, sv);
650 cast<GetElementPtrInst>(ptr)->setIsInBounds(
true);
651 old = BuilderM.CreateLoad(
652 GetElementPtrInst::getIndexedType(
getShadowType(val->getType()), sv),
655 old = BuilderM.CreateLoad(
getShadowType(val->getType()), ptr);
657 if (dif->getType() != old->getType()) {
658 if (
auto inst = dyn_cast<Instruction>(val)) {
659 EmitFailure(
"IllegalAddingType", inst->getDebugLoc(), inst,
"val ", *val,
660 " dif ", *dif,
" old ", *old);
663 llvm::errs() <<
" IllegalAddingType val: " << *val <<
" dif: " << *dif
664 <<
" old: " << *old <<
"\n";
665 llvm_unreachable(
"IllegalAddingType");
668 assert(dif->getType() == old->getType());
669 Value *res =
nullptr;
670 if (old->getType()->isIntOrIntVectorTy() || old->getType()->isPointerTy()) {
673 if (old->getType()->isIntegerTy(64))
674 addingType = Type::getDoubleTy(old->getContext());
675 else if (old->getType()->isIntegerTy(32))
676 addingType = Type::getFloatTy(old->getContext());
681 llvm::raw_string_ostream ss(s);
682 ss <<
"Cannot deduce adding type of: " << *val <<
"\n";
684 for (
auto idx : idxs)
687 if (
auto inst = dyn_cast<Instruction>(val)) {
696 llvm::errs() << ss.str() <<
"\n";
697 llvm_unreachable(
"Cannot deduce adding type");
702 assert(addingType->isFPOrFPVectorTy());
705 oldFunc->getParent()->getDataLayout().getTypeSizeInBits(old->getType());
707 oldFunc->getParent()->getDataLayout().getTypeSizeInBits(addingType);
709 if (oldBitSize == newBitSize) {
710 }
else if (oldBitSize > newBitSize && oldBitSize % newBitSize == 0) {
711 if (!addingType->isVectorTy())
713 VectorType::get(addingType, oldBitSize / newBitSize,
false);
716 llvm::raw_string_ostream ss(s);
717 ss <<
"Illegal intermediate when adding to: " << *val
718 <<
" with addingType: " << *addingType <<
"\n"
719 <<
" old: " << *old <<
" dif: " << *dif <<
"\n"
720 <<
" oldBitSize: " << oldBitSize <<
" newBitSize: " << newBitSize
728 if (
auto inst = dyn_cast<Instruction>(val))
729 EmitFailure(
"CannotDeduceType", inst->getDebugLoc(), inst, ss.str());
731 llvm::errs() << ss.str() <<
"\n";
732 llvm_unreachable(
"Cannot deduce adding type");
740 Type *intTy =
nullptr;
741 if (old->getType()->isPointerTy()) {
742 auto &DL =
oldFunc->getParent()->getDataLayout();
743 intTy = Type::getIntNTy(old->getContext(), DL.getPointerSizeInBits());
744 bcold = BuilderM.CreatePtrToInt(bcold, intTy);
745 bcdif = BuilderM.CreatePtrToInt(bcdif, intTy);
747 intTy = old->getType();
750 bcold = BuilderM.CreateBitCast(bcold, addingType);
751 bcdif = BuilderM.CreateBitCast(bcdif, addingType);
753 res = faddForSelect(bcold, bcdif);
754 if (SelectInst *select = dyn_cast<SelectInst>(res)) {
755 assert(addedSelects.back() == select);
756 addedSelects.erase(addedSelects.end() - 1);
758 Value *tval = BuilderM.CreateBitCast(select->getTrueValue(), intTy);
759 Value *fval = BuilderM.CreateBitCast(select->getFalseValue(), intTy);
760 if (old->getType()->isPointerTy()) {
761 tval = BuilderM.CreateIntToPtr(tval, old->getType());
762 fval = BuilderM.CreateIntToPtr(fval, old->getType());
764 res = BuilderM.CreateSelect(select->getCondition(), tval, fval);
765 assert(select->getNumUses() == 0);
767 res = BuilderM.CreateBitCast(res, intTy);
768 if (old->getType()->isPointerTy())
769 res = BuilderM.CreateIntToPtr(res, old->getType());
772 BuilderM.CreateStore(res, ptr);
775 Type *tys[] = {res->getType(), ptr->getType()};
777 Intrinsic::masked_store, tys);
778 auto align = cast<AllocaInst>(ptr)->getAlign().value();
781 ConstantInt::get(Type::getInt32Ty(mask->getContext()), align);
782 Value *args[] = {res, ptr, alignv, mask};
783 BuilderM.CreateCall(F, args);
786 }
else if (old->getType()->isFPOrFPVectorTy()) {
788 res = faddForSelect(old, dif);
791 BuilderM.CreateStore(res, ptr);
794 Type *tys[] = {res->getType(), ptr->getType()};
796 Intrinsic::masked_store, tys);
797 auto align = cast<AllocaInst>(ptr)->getAlign().value();
800 ConstantInt::get(Type::getInt32Ty(mask->getContext()), align);
801 Value *args[] = {res, ptr, alignv, mask};
802 BuilderM.CreateCall(F, args);
805 }
else if (
auto st = dyn_cast<StructType>(old->getType())) {
808 llvm_unreachable(
"cannot handle recursive addToDiffe with mask");
809 for (
unsigned i = 0; i < st->getNumElements(); ++i) {
811 if (st->getElementType(i)->isPointerTy())
813 if (st->getElementType(i)->isIntegerTy(8) ||
814 st->getElementType(i)->isIntegerTy(1))
816 Value *v = ConstantInt::get(Type::getInt32Ty(st->getContext()), i);
817 SmallVector<Value *, 2> idx2(idxs.begin(), idxs.end());
822 for (
auto select : selects) {
823 addedSelects.push_back(select);
827 }
else if (
auto at = dyn_cast<ArrayType>(old->getType())) {
830 llvm_unreachable(
"cannot handle recursive addToDiffe with mask");
831 if (at->getElementType()->isPointerTy())
833 for (
unsigned i = 0; i < at->getNumElements(); ++i) {
835 Value *v = ConstantInt::get(Type::getInt32Ty(at->getContext()), i);
836 SmallVector<Value *, 2> idx2(idxs.begin(), idxs.end());
840 for (
auto select : selects) {
841 addedSelects.push_back(select);
846 llvm::errs() <<
" idx: {";
848 llvm::errs() << *i <<
", ";
849 llvm::errs() <<
"}\n";
851 llvm::errs() <<
" addingType: " << *addingType <<
"\n";
853 llvm::errs() <<
" addingType: null\n";
854 llvm::errs() <<
" oldType:" << *old->getType() <<
" old:" << *old <<
"\n";
855 llvm_unreachable(
"unknown type to add to diffe");
861 IRBuilder<> &BuilderM) {
863 if (
auto arg = dyn_cast<Argument>(val))
864 assert(arg->getParent() ==
oldFunc);
865 if (
auto inst = dyn_cast<Instruction>(val))
866 assert(inst->getParent()->getParent() ==
oldFunc);
868 llvm::errs() << *
newFunc <<
"\n";
869 llvm::errs() << *val <<
"\n";
880 auto placeholder0 = &*found->second;
881 auto placeholder = cast<PHINode>(placeholder0);
884 placeholder->replaceAllUsesWith(toset);
891#if LLVM_VERSION_MAJOR < 17
892 if (toset->getContext().supportsTypedPointers()) {
893 if (toset->getType() != tostore->getType()->getPointerElementType()) {
894 llvm::errs() <<
"toset:" << *toset <<
"\n";
895 llvm::errs() <<
"tostore:" << *tostore <<
"\n";
897 assert(toset->getType() == tostore->getType()->getPointerElementType());
900 BuilderM.CreateStore(toset, tostore);
905 AllocaInst *alloc, llvm::Type *T,
906 ConstantInt *byteSizeOfType,
907 Value *storeInto, MDNode *InvariantMD) {
913 tbuild.setFastMathFlags(
getFast());
917 tbuild.SetInsertPoint(tbuild.GetInsertBlock()->getTerminator());
920 ValueToValueMapTy antimap;
921 for (
int j = sublimits.size() - 1; j >= i; j--) {
922 auto &innercontainedloops = sublimits[j].second;
923 for (
auto riter = innercontainedloops.rbegin(),
924 rend = innercontainedloops.rend();
925 riter != rend; ++riter) {
926 const auto &idx = riter->first;
929 tbuild.CreateLoad(idx.var->getType(), idx.antivaralloc);
934 Value *metaforfree =
unwrapM(storeInto, tbuild, antimap,
937#if LLVM_VERSION_MAJOR < 17
938 if (metaforfree->getContext().supportsTypedPointers()) {
939 assert(T == metaforfree->getType()->getPointerElementType());
943 LoadInst *forfree = cast<LoadInst>(tbuild.CreateLoad(T, metaforfree));
944 forfree->setMetadata(LLVMContext::MD_invariant_group, InvariantMD);
945 forfree->setMetadata(LLVMContext::MD_dereferenceable,
946 MDNode::get(forfree->getContext(),
947 ArrayRef<Metadata *>(ConstantAsMetadata::get(
949 forfree->setName(
"forfree");
951 (
unsigned)
newFunc->getParent()->getDataLayout().getPointerSize());
952 forfree->setAlignment(Align(align));
957 ci->setDebugLoc(DILocation::get(
newFunc->getContext(), 0, 0,
965 Value *origVal, Type *addingType,
966 unsigned start,
unsigned size,
967 Value *origptr, Value *dif,
968 IRBuilder<> &BuilderM,
969 MaybeAlign align, Value *mask) {
970 auto &DL =
oldFunc->getParent()->getDataLayout();
972 auto addingSize = (DL.getTypeSizeInBits(addingType) + 1) / 8;
973 if (addingSize != size) {
974 assert(size > addingSize);
976 VectorType::get(addingType, size / addingSize,
false);
977 size = (size / addingSize) * addingSize;
989 assert(
false &&
"Invalid derivative mode (ReverseModePrimal)");
997 bool needsCast =
false;
998#if LLVM_VERSION_MAJOR < 17
999 if (isa<PointerType>(origptr->getType()) &&
1000 origptr->getContext().supportsTypedPointers()) {
1001 needsCast = origptr->getType()->getPointerElementType() != addingType;
1006 if (start != 0 || needsCast || !isa<PointerType>(origptr->getType())) {
1007 auto rule = [&](Value *ptr) {
1008 if (!isa<PointerType>(origptr->getType())) {
1009 ptr = BuilderM.CreateIntToPtr(ptr,
getUnqual(addingType));
1012 auto i8 = Type::getInt8Ty(ptr->getContext());
1013 ptr = BuilderM.CreatePointerCast(
1015 i8, cast<PointerType>(ptr->getType())->getAddressSpace()));
1016 auto off = ConstantInt::get(Type::getInt64Ty(ptr->getContext()), start);
1017 ptr = BuilderM.CreateInBoundsGEP(i8, ptr, off);
1020 ptr = BuilderM.CreatePointerCast(
1023 cast<PointerType>(ptr->getType())->getAddressSpace()));
1030 isa<PointerType>(origptr->getType())
1031 ? cast<PointerType>(origptr->getType())->getAddressSpace()
1033 BuilderM, rule, ptr);
1037 needsCast = dif->getType() != addingType;
1038 else if (
auto AT = cast<ArrayType>(dif->getType()))
1039 needsCast = AT->getElementType() != addingType;
1042 cast<VectorType>(dif->getType())->getElementType() != addingType;
1044 if (start != 0 || needsCast) {
1045 auto rule = [&](Value *dif) {
1048 auto i8 = Type::getInt8Ty(ptr->getContext());
1049 auto prevSize = (DL.getTypeSizeInBits(dif->getType()) + 1) / 8;
1050 Type *tys[] = {ArrayType::get(i8, start), addingType,
1051 ArrayType::get(i8, prevSize - start - size)};
1052 auto ST = StructType::get(i8->getContext(), tys,
true);
1053 auto Al = A.CreateAlloca(ST,
nullptr,
"gep.alloca");
1054 auto SI = BuilderM.CreateStore(
1055 dif, BuilderM.CreatePointerCast(Al,
getUnqual(dif->getType())));
1057 Al->setAlignment(*align);
1058 SI->setAlignment(*align);
1061 ConstantInt::get(Type::getInt64Ty(ptr->getContext()), 0),
1062 ConstantInt::get(Type::getInt32Ty(ptr->getContext()), 1)};
1064 auto difp = BuilderM.CreateInBoundsGEP(ST, Al, idxs,
"gep.ptr");
1065 auto LI = BuilderM.CreateLoad(addingType, difp,
"gep.load");
1066 LI->setAlignment(commonAlignment(Al->getAlign(), start));
1069 if (dif->getType() != addingType) {
1070 auto difSize = (DL.getTypeSizeInBits(dif->getType()) + 1) / 8;
1071 if (difSize < size) {
1072 llvm::errs() <<
" ds: " << difSize <<
" as: " << size <<
"\n";
1073 llvm::errs() <<
" dif: " << *dif <<
" adding: " << *addingType
1076 assert(difSize >= size);
1077 if (CastInst::castIsValid(Instruction::CastOps::BitCast, dif,
1079 dif = BuilderM.CreateBitCast(dif, addingType);
1082 auto Al = A.CreateAlloca(dif->getType(),
nullptr,
"cast.alloca");
1083 BuilderM.CreateStore(dif, Al);
1084 auto LI = BuilderM.CreateLoad(
1085 addingType, BuilderM.CreatePointerCast(Al,
getUnqual(addingType)),
1087 LI->setAlignment(Al->getAlign());
1100 auto TT = llvm::Triple(
newFunc->getParent()->getTargetTriple());
1101 auto Arch = TT.getArch();
1104 if (isa<AllocaInst>(TmpOrig) &&
isGPUArch(TT)) {
1112 if (Atomic && elementwiseReadForContext(orig, origptr))
1118 if (Arch == Triple::amd_target &&
1119 cast<PointerType>(origptr->getType())->getAddressSpace() == 4) {
1120 auto rule = [&](Value *ptr) {
1121 return BuilderM.CreateAddrSpaceCast(ptr,
getPointerType(addingType, 1));
1128 llvm::raw_string_ostream ss(s);
1129 ss <<
"Unimplemented masked atomic fadd for ptr:" << *ptr
1130 <<
" dif:" << *dif <<
" mask: " << *mask <<
" orig: " << *orig <<
"\n";
1137 EmitFailure(
"NoDerivative", orig->getDebugLoc(), orig, ss.str());
1152 AtomicRMWInst::BinOp op = AtomicRMWInst::FAdd;
1153 if (
auto vt = dyn_cast<VectorType>(addingType)) {
1154 assert(!vt->getElementCount().isScalable());
1155 size_t numElems = vt->getElementCount().getKnownMinValue();
1156 auto rule = [&](Value *dif, Value *ptr) {
1157 for (
size_t i = 0; i < numElems; ++i) {
1158 auto vdif = BuilderM.CreateExtractElement(dif, i);
1161 ConstantInt::get(Type::getInt64Ty(vt->getContext()), 0),
1162 ConstantInt::get(Type::getInt32Ty(vt->getContext()), i)};
1163 auto vptr = BuilderM.CreateGEP(addingType, ptr, Idxs);
1164 MaybeAlign alignv = align;
1168 assert((*alignv).value() != 0);
1169 if (start % (*alignv).value() != 0) {
1174 BuilderM.CreateAtomicRMW(op, vptr, vdif, alignv,
1175 AtomicOrdering::Monotonic,
1181 auto rule = [&](Value *dif, Value *ptr) {
1183 MaybeAlign alignv = align;
1187 assert((*alignv).value() != 0);
1188 if (start % (*alignv).value() != 0) {
1193 BuilderM.CreateAtomicRMW(op, ptr, dif, alignv,
1194 AtomicOrdering::Monotonic, SyncScope::System);
1204 auto rule = [&](Value *ptr, Value *dif) {
1205 auto LI = BuilderM.CreateLoad(addingType, ptr);
1207 Value *res = BuilderM.CreateFAdd(
LI, dif);
1209 StoreInst *st = BuilderM.CreateStore(res, ptr);
1211 SmallVector<Metadata *, 1> scopeMD = {
1213 if (
auto origValI = dyn_cast_or_null<Instruction>(origVal))
1214 if (
auto MD = origValI->getMetadata(LLVMContext::MD_alias_scope)) {
1215 auto MDN = cast<MDNode>(MD);
1216 for (
auto &o : MDN->operands())
1217 scopeMD.push_back(o);
1219 auto scope = MDNode::get(
LI->getContext(), scopeMD);
1220 LI->setMetadata(LLVMContext::MD_alias_scope, scope);
1221 st->setMetadata(LLVMContext::MD_alias_scope, scope);
1223 SmallVector<Metadata *, 1> MDs;
1224 for (ssize_t j = -1; j <
getWidth(); j++) {
1225 if (j != (ssize_t)idx)
1228 if (
auto origValI = dyn_cast_or_null<Instruction>(origVal))
1229 if (
auto MD = origValI->getMetadata(LLVMContext::MD_noalias)) {
1230 auto MDN = cast<MDNode>(MD);
1231 for (
auto &o : MDN->operands())
1235 auto noscope = MDNode::get(ptr->getContext(), MDs);
1236 LI->setMetadata(LLVMContext::MD_noalias, noscope);
1237 st->setMetadata(LLVMContext::MD_noalias, noscope);
1239 if (origVal && isa<Instruction>(origVal) && start == 0 &&
1240 size == (DL.getTypeSizeInBits(origVal->getType()) + 7) / 8) {
1241 auto origValI = cast<Instruction>(origVal);
1243 unsigned int StoreData[] = {LLVMContext::MD_tbaa,
1244 LLVMContext::MD_tbaa_struct};
1245 for (
auto MD : StoreData)
1246 st->setMetadata(MD, origValI->getMetadata(MD));
1253 auto alignv = align ? (*align).value() : 0;
1257 if (start % alignv != 0) {
1262 LI->setAlignment(Align(alignv));
1263 st->setAlignment(Align(alignv));
1269 Type *tys[] = {addingType, origptr->getType()};
1271 Intrinsic::masked_load, tys);
1273 Intrinsic::masked_store, tys);
1274 unsigned aligni = align ? align->value() : 0;
1279 if (start % aligni != 0) {
1284 ConstantInt::get(Type::getInt32Ty(mask->getContext()), aligni);
1285 auto rule = [&](Value *ptr, Value *dif) {
1286 Value *largs[] = {ptr, alignv, mask,
1287 Constant::getNullValue(dif->getType())};
1288 Value *
LI = BuilderM.CreateCall(LF, largs);
1289 Value *res = BuilderM.CreateFAdd(
LI, dif);
1291 Value *sargs[] = {res, ptr, alignv, mask};
1292 BuilderM.CreateCall(SF, sargs);
1299 llvm::Instruction *orig, llvm::Value *origVal,
TypeTree vd,
1300 unsigned LoadSize, llvm::Value *origptr, llvm::Value *prediff,
1301 llvm::IRBuilder<> &Builder2, MaybeAlign alignment, llvm::Value *premask)
1306 unsigned size = LoadSize;
1310 BasicBlock *merge =
nullptr;
1313 unsigned nextStart = size;
1316 for (
size_t i = start; i < size; ++i) {
1318 dt.checkedOrIn(vd[{(int)i}],
true, Legal);
1324 if (!dt.isKnown()) {
1326 llvm::errs() <<
" vd:" << vd.
str() <<
" start:" << start
1327 <<
" size: " << size <<
" dt:" << dt.str() <<
"\n";
1329 assert(dt.isKnown());
1331 if (Type *isfloat = dt.isFloat()) {
1334 if (start == 0 && nextStart == LoadSize) {
1341 auto i8 = Type::getInt8Ty(tostore->getContext());
1343 tostore = Builder2.CreatePointerCast(
1347 cast<PointerType>(tostore->getType())->getAddressSpace()));
1348 auto off = ConstantInt::get(Type::getInt64Ty(tostore->getContext()),
1350 tostore = Builder2.CreateInBoundsGEP(i8, tostore, off);
1352 auto AT = ArrayType::get(i8, nextStart - start);
1353 tostore = Builder2.CreatePointerCast(
1354 tostore,
getPointerType(AT, cast<PointerType>(tostore->getType())
1355 ->getAddressSpace()));
1356 Builder2.CreateStore(Constant::getNullValue(AT), tostore);
1372 shadow_val =
extractMeta(Builder2, shadow_val, 0);
1374 Value *shadow = Builder2.CreateICmpNE(primal_val, shadow_val);
1376 BasicBlock *current = Builder2.GetInsertBlock();
1377 BasicBlock *conditional =
1380 Builder2.CreateCondBr(shadow, conditional, merge);
1381 Builder2.SetInsertPoint(conditional);
1385 assert(start == 0 && nextStart == LoadSize);
1387 origptr, prediff, Builder2, alignment, premask);
1391 if (nextStart == size)
1396 Builder2.CreateBr(merge);
1397 Builder2.SetInsertPoint(merge);
@ AttemptFullUnwrapWithLookup
static bool isZero(llvm::Constant *cst)
DIFFE_TYPE
Potential differentiable argument classifications.
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
llvm::cl::opt< bool > looseTypeAnalysis
SmallVector< unsigned int, 9 > MD_ToCopy
CallInst * CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree)
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
void EmitNoTypeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2)
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask)
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
static bool isGPUArch(const llvm::Triple &TT)
static llvm::PointerType * getUnqual(llvm::Type *T)
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
static T min(T a, T b)
Pick the maximum value.
static bool hasTerminator(llvm::BasicBlock *BB)
llvm::Function *const newFunc
The function whose instructions we are caching.
std::map< llvm::AllocaInst *, std::set< llvm::AssertingVH< llvm::CallInst > > > scopeFrees
A map of allocations to a set of instructions which free memory as part of the cache.
llvm::TargetLibraryInfo & TLI
Various analysis results of newFunc.
llvm::BasicBlock * inversionAllocs
llvm::SmallVector< std::pair< llvm::Value *, llvm::SmallVector< std::pair< LoopContext, llvm::Value * >, 4 > >, 0 > SubLimitType
Given a LimitContext ctx, representing a location inside a loop nest, break each of the loops up into...
unsigned getCacheAlignment(unsigned bsize) const
llvm::ValueMap< const llvm::Value *, llvm::TrackingVH< llvm::AllocaInst > > differentials
void addToInvertedPtrDiffe(llvm::Instruction *orig, llvm::Value *origVal, llvm::Type *addingType, unsigned start, unsigned size, llvm::Value *origptr, llvm::Value *dif, llvm::IRBuilder<> &BuilderM, llvm::MaybeAlign align=llvm::MaybeAlign(), llvm::Value *mask=nullptr)
align is the alignment that should be specified for load/store to pointer
llvm::Value * diffe(llvm::Value *val, llvm::IRBuilder<> &BuilderM)
llvm::AllocaInst * getDifferential(llvm::Value *val)
bool FreeMemory
Whether to free memory in reverse pass or split forward.
static DiffeGradientUtils * CreateFromClone(EnzymeLogic &Logic, DerivativeMode mode, bool runtimeActivity, bool strongZero, unsigned width, llvm::Function *todiff, llvm::TargetLibraryInfo &TLI, TypeAnalysis &TA, FnTypeInfo &oldTypeInfo, DIFFE_TYPE retType, bool shadowReturnArg, bool diffeReturnArg, llvm::ArrayRef< DIFFE_TYPE > constant_args, bool returnTape, bool returnPrimal, llvm::Type *additionalArg, bool omp)
llvm::CallInst * freeCache(llvm::BasicBlock *forwardPreheader, const SubLimitType &sublimits, int i, llvm::AllocaInst *alloc, llvm::Type *myType, llvm::ConstantInt *byteSizeOfType, llvm::Value *storeInto, llvm::MDNode *InvariantMD) override
If an allocation is requested to be freed, this subclass will be called to chose how and where to fre...
void setDiffe(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM)
llvm::SmallVector< llvm::SelectInst *, 4 > addToDiffe(llvm::Value *val, llvm::Value *dif, llvm::IRBuilder<> &BuilderM, llvm::Type *addingType, llvm::ArrayRef< llvm::Value * > idxs={}, llvm::Value *mask=nullptr)
Returns created select instructions, if any.
llvm::ValueMap< const llvm::Value *, InvertedPointerVH > invertedPointers
llvm::DebugLoc getNewFromOriginal(const llvm::DebugLoc L) const
static llvm::Value * extractMeta(llvm::IRBuilder<> &Builder, llvm::Value *Agg, unsigned off, const llvm::Twine &name="")
Helper routine to extract a nested element from a struct/array. This is.
llvm::Value * applyChainRule(llvm::Type *diffType, llvm::IRBuilder<> &Builder, Func rule, Args... args)
Unwraps a vector derivative from its internal representation and applies a function f to each element...
llvm::BasicBlock * addReverseBlock(llvm::BasicBlock *currentBlock, llvm::Twine const &name, bool forkCache=true, bool push=true)
std::map< llvm::BasicBlock *, llvm::SmallVector< llvm::BasicBlock *, 4 > > reverseBlocks
Map of primal block to corresponding block(s) in reverse.
void replaceAWithB(llvm::Value *A, llvm::Value *B, bool storeInCache=false) override
Replace this instruction both in LLVM modules and any local data-structures.
llvm::Value * unwrapM(llvm::Value *const val, llvm::IRBuilder<> &BuilderM, const llvm::ValueToValueMapTy &available, UnwrapMode unwrapMode, llvm::BasicBlock *scope=nullptr, bool permitCache=true) override final
if full unwrap, don't just unwrap this instruction, but also its operands, etc
llvm::Value * lookupM(llvm::Value *val, llvm::IRBuilder<> &BuilderM, const llvm::ValueToValueMapTy &incoming_availalble=llvm::ValueToValueMapTy(), bool tryLegalRecomputeCheck=true, llvm::BasicBlock *scope=nullptr) override
High-level utility to get the value an instruction at a new location specified by BuilderM.
bool isAtomic(llvm::Value *origptr) const
llvm::MDNode * getDerivativeAliasScope(const llvm::Value *origptr, ssize_t newptr)
llvm::Value * invertPointerM(llvm::Value *val, llvm::IRBuilder<> &BuilderM)
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
llvm::ValueMap< llvm::Value *, ShadowRematerializer > backwardsOnlyShadows
Only loaded from and stored to (not captured), mapped to the stores (and memset).
bool isConstantValue(llvm::Value *val) const
void erase(llvm::Instruction *I) override
Erase this instruction both from LLVM modules and any local data-structures.
llvm::Function * CloneFunctionWithReturns(DerivativeMode mode, unsigned width, llvm::Function *&F, llvm::ValueToValueMapTy &ptrInputs, llvm::ArrayRef< DIFFE_TYPE > constant_args, llvm::SmallPtrSetImpl< llvm::Value * > &constants, llvm::SmallPtrSetImpl< llvm::Value * > &nonconstant, llvm::SmallPtrSetImpl< llvm::Value * > &returnvals, bool returnTape, bool returnPrimal, bool returnShadow, const llvm::Twine &name, llvm::ValueMap< const llvm::Value *, AssertingReplacingVH > *VMapO, bool diffeReturnArg, llvm::Type *additionalArg=nullptr)
Full interprocedural TypeAnalysis.
TypeResults analyzeFunction(const FnTypeInfo &fn)
Analyze a particular function, returning the results.
A holder class representing the results of running TypeAnalysis on a given function.
void dump(llvm::raw_ostream &ss=llvm::errs()) const
Prints all known information.
llvm::Function * getFunction() const
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
std::string str() const
Returns a string representation of this TypeTree.
Struct containing all contextual type information for a particular function call.
std::map< llvm::Argument *, TypeTree > Arguments
Types of arguments.
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.