27#ifndef ENZYME_ADJOINT_GENERATOR_H
28#define ENZYME_ADJOINT_GENERATOR_H
30#include "llvm/ADT/ArrayRef.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallSet.h"
33#include "llvm/ADT/SmallVector.h"
34#include "llvm/Analysis/ValueTracking.h"
35#include "llvm/IR/Constants.h"
36#include "llvm/IR/DerivedTypes.h"
37#include "llvm/IR/IntrinsicsX86.h"
38#include "llvm/IR/Value.h"
39#include "llvm/Transforms/Utils/BasicBlockUtils.h"
40#include "llvm/Transforms/Utils/Cloning.h"
44#include "EnzymeLogic.h"
46#include "GradientUtils.h"
51#define DEBUG_TYPE "enzyme"
60 llvm::ArrayRef<DIFFE_TYPE> constant_args;
63 std::function<unsigned(llvm::Instruction *,
CacheType, llvm::IRBuilder<> &)>
65 const std::map<llvm::CallInst *, std::pair<bool, const std::vector<bool>>>
68 const std::map<llvm::ReturnInst *, llvm::StoreInst *> *replacedReturns;
70 const llvm::SmallPtrSetImpl<const llvm::Value *> &unnecessaryValues;
71 const llvm::SmallPtrSetImpl<const llvm::Instruction *>
72 &unnecessaryInstructions;
73 const llvm::SmallPtrSetImpl<const llvm::Instruction *> &unnecessaryStores;
74 const llvm::SmallPtrSetImpl<llvm::BasicBlock *> &oldUnreachable;
79 llvm::ArrayRef<DIFFE_TYPE> constant_args,
DIFFE_TYPE retType,
80 std::function<
unsigned(llvm::Instruction *,
CacheType,
83 const std::map<llvm::CallInst *, std::pair<
bool,
const std::vector<bool>>>
86 const std::map<llvm::ReturnInst *, llvm::StoreInst *> *replacedReturns,
87 const llvm::SmallPtrSetImpl<const llvm::Value *> &unnecessaryValues,
88 const llvm::SmallPtrSetImpl<const llvm::Instruction *>
89 &unnecessaryInstructions,
90 const llvm::SmallPtrSetImpl<const llvm::Instruction *> &unnecessaryStores,
91 const llvm::SmallPtrSetImpl<llvm::BasicBlock *> &oldUnreachable)
92 : Mode(Mode), gutils(gutils), constant_args(constant_args),
93 retType(retType), getIndex(getIndex),
94 overwritten_args_map(overwritten_args_map),
95 augmentedReturn(augmentedReturn), replacedReturns(replacedReturns),
96 unnecessaryValues(unnecessaryValues),
97 unnecessaryInstructions(unnecessaryInstructions),
98 unnecessaryStores(unnecessaryStores), oldUnreachable(oldUnreachable) {
103 if (
auto in = dyn_cast<Instruction>(pair.first)) {
104 if (in->getParent()->getParent() != gutils->
oldFunc) {
105 llvm::errs() <<
"inf: " << *in->getParent()->getParent() <<
"\n";
106 llvm::errs() <<
"gutils->oldFunc: " << *gutils->
oldFunc <<
"\n";
107 llvm::errs() <<
"in: " << *in <<
"\n";
109 assert(in->getParent()->getParent() == gutils->
oldFunc);
116 using namespace llvm;
119 unnecessaryInstructions.find(&I) == unnecessaryInstructions.end();
131 if (
auto newi = dyn_cast<Instruction>(iload))
141 bool &forwardsShadow,
bool &backwardsShadow) {
142 backwardsShadow =
false;
143 forwardsShadow =
true;
145 if (pair.second.stores.count(&I)) {
146 backwardsShadow =
true;
147 forwardsShadow = pair.second.primalInitialize;
148 if (
auto inst = llvm::dyn_cast<llvm::Instruction>(pair.first))
149 if (!forwardsShadow && pair.second.LI &&
150 pair.second.LI->contains(inst->getParent()))
151 backwardsShadow =
false;
155 if (
auto arg = llvm::dyn_cast<llvm::Argument>(
getBaseObject(orig_ptr))) {
156 unsigned argNo = arg->getArgNo();
157 if (argNo < gutils->nowrite_shadows.size() &&
159 forwardsShadow =
false;
160 backwardsShadow =
false;
169 llvm::Value *shadow =
nullptr) {
170 using namespace llvm;
175 shadow = Constant::getNullValue(gutils->
getShadowType(I.getType()));
176 auto placeholder = cast<PHINode>(&*ifound->second);
179 gutils->
erase(placeholder);
187 if (order == llvm::AtomicOrdering::Release)
188 return llvm::AtomicOrdering::Monotonic;
189 if (order == llvm::AtomicOrdering::AcquireRelease)
190 return llvm::AtomicOrdering::Acquire;
201 llvm::Value *val, llvm::Type *loadTy,
202 llvm::Type *FT,
bool negate, llvm::Align align,
203 llvm::AtomicOrdering order,
204 llvm::SyncScope::ID ssid,
bool isVolatile) {
205 using namespace llvm;
207 IRBuilder<> Builder2(&I);
210 ip =
lookup(ip, Builder2);
213 auto rule = [&](Value *ip) -> Value * {
214 LoadInst *dif1 = Builder2.CreateLoad(loadTy, ip, isVolatile);
215 dif1->setAlignment(align);
216 dif1->setOrdering(order);
217 dif1->setSyncScopeID(ssid);
220 res = Builder2.CreateBitCast(res, FT);
221 res = Builder2.CreateFNeg(res);
223 return Builder2.CreateBitCast(res, val->getType());
227 addToDiffe(val, diff, Builder2, FT->getScalarType());
231 llvm::Type *intType, llvm::Function *caller) {
232 using namespace llvm;
237 Type *i32 = Type::getInt32Ty(DT->getContext());
241 FunctionType::get(Type::getVoidTy(DT->getContext()), pargs,
false);
243 auto alloc = AllocaBuilder.CreateAlloca(i32);
244 auto ierr = AllocaBuilder.CreateAlloca(i32);
245 llvm::Value *args[] = {DT, alloc, ierr};
246 if (DT->getType() != pargs[0])
247 args[0] = B.CreateBitCast(args[0], pargs[0]);
249 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
252 return B.CreateLoad(i32, alloc);
255 if (DT->getType()->isIntegerTy())
256 DT = B.CreateIntToPtr(DT,
getInt8PtrTy(DT->getContext()));
258 if (Constant *C = dyn_cast<Constant>(DT)) {
259 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
260 C = CE->getOperand(0);
262 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
263 if (GV->getName() ==
"ompi_mpi_double") {
264 return ConstantInt::get(intType, 8,
false);
265 }
else if (GV->getName() ==
"ompi_mpi_float") {
266 return ConstantInt::get(intType, 4,
false);
271 auto FT = FunctionType::get(intType, pargs,
false);
272 auto alloc = IRBuilder<>(gutils->
inversionAllocs).CreateAlloca(intType);
273 llvm::Value *args[] = {DT, alloc};
274 if (DT->getType() != pargs[0])
275 args[0] = B.CreateBitCast(args[0], pargs[0]);
277 AL = AL.addParamAttribute(DT->getContext(), 0,
278 Attribute::AttrKind::ReadOnly);
281 AL.addParamAttribute(DT->getContext(), 0, Attribute::AttrKind::NoAlias);
283 AL.addParamAttribute(DT->getContext(), 0, Attribute::AttrKind::NonNull);
284 AL = AL.addParamAttribute(DT->getContext(), 1,
285 Attribute::AttrKind::WriteOnly);
288 AL.addParamAttribute(DT->getContext(), 1, Attribute::AttrKind::NoAlias);
290 AL.addParamAttribute(DT->getContext(), 1, Attribute::AttrKind::NonNull);
291 AL = AL.addAttributeAtIndex(DT->getContext(), AttributeList::FunctionIndex,
292 Attribute::AttrKind::NoUnwind);
293 AL = AL.addAttributeAtIndex(DT->getContext(), AttributeList::FunctionIndex,
294 Attribute::AttrKind::NoFree);
295 AL = AL.addAttributeAtIndex(DT->getContext(), AttributeList::FunctionIndex,
296 Attribute::AttrKind::NoSync);
297 AL = AL.addAttributeAtIndex(DT->getContext(), AttributeList::FunctionIndex,
298 Attribute::AttrKind::WillReturn);
299 auto CI = B.CreateCall(
300 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
304#if LLVM_VERSION_MAJOR >= 16
305 CI->setOnlyAccessesArgMemory();
307 CI->addAttributeAtIndex(AttributeList::FunctionIndex,
308 Attribute::ArgMemOnly);
310 return B.CreateLoad(intType, alloc);
316 llvm::Type *rankTy, llvm::Function *caller) {
317 using namespace llvm;
322 Type *i32 = Type::getInt32Ty(comm->getContext());
326 FunctionType::get(Type::getVoidTy(comm->getContext()), pargs,
false);
328 auto alloc = AllocaBuilder.CreateAlloca(i32);
329 auto ierr = AllocaBuilder.CreateAlloca(i32);
330 llvm::Value *args[] = {comm, alloc, ierr};
331 if (comm->getType() != pargs[0])
332 args[0] = B.CreateBitCast(args[0], pargs[0]);
334 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
337 return B.CreateLoad(i32, alloc);
340 Type *pargs[] = {comm->getType(),
getUnqual(rankTy)};
341 auto FT = FunctionType::get(rankTy, pargs,
false);
342 auto &context = comm->getContext();
343 auto alloc = IRBuilder<>(gutils->
inversionAllocs).CreateAlloca(rankTy);
345 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::ReadOnly);
347 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::NoAlias);
348 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::NonNull);
349 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::WriteOnly);
351 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::NoAlias);
352 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::NonNull);
353 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
354 Attribute::AttrKind::NoUnwind);
355 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
356 Attribute::AttrKind::NoFree);
357 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
358 Attribute::AttrKind::NoSync);
359 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
360 Attribute::AttrKind::WillReturn);
361 llvm::Value *args[] = {comm, alloc};
363 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
367 return B.CreateLoad(rankTy, alloc);
371 llvm::Type *rankTy, llvm::Function *caller) {
372 using namespace llvm;
377 Type *i32 = Type::getInt32Ty(comm->getContext());
381 FunctionType::get(Type::getVoidTy(comm->getContext()), pargs,
false);
383 auto alloc = AllocaBuilder.CreateAlloca(i32);
384 auto ierr = AllocaBuilder.CreateAlloca(i32);
385 llvm::Value *args[] = {comm, alloc, ierr};
386 if (comm->getType() != pargs[0])
387 args[0] = B.CreateBitCast(args[0], pargs[0]);
389 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
392 return B.CreateLoad(i32, alloc);
395 Type *pargs[] = {comm->getType(),
getUnqual(rankTy)};
396 auto FT = FunctionType::get(rankTy, pargs,
false);
397 auto &context = comm->getContext();
398 auto alloc = IRBuilder<>(gutils->
inversionAllocs).CreateAlloca(rankTy);
400 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::ReadOnly);
402 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::NoAlias);
403 AL = AL.addParamAttribute(context, 0, Attribute::AttrKind::NonNull);
404 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::WriteOnly);
406 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::NoAlias);
407 AL = AL.addParamAttribute(context, 1, Attribute::AttrKind::NonNull);
408 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
409 Attribute::AttrKind::NoUnwind);
410 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
411 Attribute::AttrKind::NoFree);
412 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
413 Attribute::AttrKind::NoSync);
414 AL = AL.addAttributeAtIndex(context, AttributeList::FunctionIndex,
415 Attribute::AttrKind::WillReturn);
416 llvm::Value *args[] = {comm, alloc};
418 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
422 return B.CreateLoad(rankTy, alloc);
426 using namespace llvm;
431 switch (inst.getOpcode()) {
432#include "InstructionDerivatives.inc"
445 llvm::raw_string_ostream ss(s);
446 ss <<
"in Mode: " <<
to_string(Mode) <<
"\n";
447 ss <<
"cannot handle unknown instruction\n" << inst;
448 IRBuilder<> Builder2(&inst);
456 Constant::getNullValue(gutils->
getShadowType(inst.getType())),
459 if (!inst.getType()->isVoidTy()) {
462 U.set(UndefValue::get(inst.getType()));
472 using namespace llvm;
482 auto placeholder = cast<PHINode>(&*found->second);
486 gutils, &I, Mode, oldUnreachable)) {
487 gutils->
erase(placeholder);
491 IRBuilder<> Builder2(&I);
496 assert(toset != placeholder);
499 placeholder->replaceAllUsesWith(toset);
500 gutils->
erase(placeholder);
525 bool constantval, llvm::Value *mask =
nullptr,
526 llvm::Value *orig_maskInit =
nullptr) {
527 using namespace llvm;
529 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
530 auto LoadSize = (DL.getTypeSizeInBits(I.getType()) + 1) / 8;
549 SmallVector<Metadata *, 1> scopeMD = {
551 if (
auto prev = I.getMetadata(LLVMContext::MD_alias_scope)) {
552 for (
auto &M : cast<MDNode>(prev)->operands()) {
553 scopeMD.push_back(M);
556 auto scope = MDNode::get(I.getContext(), scopeMD);
557 newi->setMetadata(LLVMContext::MD_alias_scope, scope);
559 SmallVector<Metadata *, 1> MDs;
560 for (
size_t j = 0; j < gutils->
getWidth(); j++) {
563 if (
auto prev = I.getMetadata(LLVMContext::MD_noalias)) {
564 for (
auto &M : cast<MDNode>(prev)->operands()) {
568 auto noscope = MDNode::get(I.getContext(), MDs);
569 newi->setMetadata(LLVMContext::MD_noalias, noscope);
571 auto vd = TR.
query(&I);
573 IRBuilder<> BuilderZ(newi);
575 auto ET = I.getType();
580 raw_string_ostream ss(
str);
581 ss <<
"Cannot deduce type of load " << I <<
", assumed " << vd.str()
589 raw_string_ostream ss(
str);
590 ss <<
"Cannot deduce type of load " << I;
602 Instruction *placeholder = cast<Instruction>(&*found->second);
603 assert(placeholder->getType() == type);
609 gutils->
erase(placeholder);
614 if (vd[{-1}].isPossiblePointer()) {
616 BuilderZ, placeholder,
618 assert(newip->getType() == type);
622 gutils->
erase(placeholder);
630 auto rule = [&](Value *inop, Value *newip) -> Value * {
631 Value *shadow = BuilderZ.CreateICmpNE(
634 Constant::getNullValue(newip->getType()));
638 I.getType(), BuilderZ, rule,
641 assert(newip->getType() == type);
642 placeholder->replaceAllUsesWith(newip);
643 gutils->
erase(placeholder);
654 if (vd[{-1}].isPossiblePointer()) {
657 Instruction *placeholder = cast<Instruction>(&*found->second);
658 assert(placeholder->getType() == type);
662 Value *newip =
nullptr;
674 gutils->erase(placeholder);
676 newip = gutils->invertPointerM(&I, BuilderZ);
677 assert(newip->getType() == type);
683 gutils->cacheForReverse(
686 placeholder->replaceAllUsesWith(newip);
687 gutils->erase(placeholder);
688 gutils->invertedPointers.insert(std::make_pair(
696 assert(0 &&
"impossible branch");
701 gutils->erase(placeholder);
705 newip = gutils->cacheForReverse(
706 BuilderZ, placeholder,
708 assert(newip->getType() == type);
709 gutils->invertedPointers.insert(std::make_pair(
712 newip = gutils->invertPointerM(&I, BuilderZ);
713 assert(newip->getType() == type);
714 placeholder->replaceAllUsesWith(newip);
715 gutils->erase(placeholder);
716 gutils->invertedPointers.insert(std::make_pair(
725 gutils->
erase(placeholder);
745 bool primalNeededInReverse =
false;
749 if (!found->second) {
750 primalNeededInReverse =
true;
757 primalNeededInReverse |=
760 if (primalNeededInReverse) {
761 inst = gutils->cacheForReverse(BuilderZ, newi,
764 assert(inst->getType() == I.getType());
769 assert(inst != newi);
771 assert(inst == newi);
789 if (
auto arg = dyn_cast<GlobalVariable>(I.getOperand(0))) {
804 assert(0 &&
"impossible branch");
810 IRBuilder<> Builder2(&I);
813 Value *prediff =
nullptr;
815 for (ssize_t i = -1; i < (ssize_t)LoadSize; ++i) {
816 if (vd[{(int)i}].isFloat()) {
817 prediff =
diffe(&I, Builder2);
822 Value *premask =
nullptr;
824 if (prediff && mask) {
825 premask =
lookup(mask, Builder2);
830 ->addToInvertedPtrDiffe(&I, &I, vd, LoadSize, I.getOperand(0),
831 prediff, Builder2, alignment, premask);
834 unsigned size = LoadSize;
837 unsigned nextStart = size;
840 for (
size_t i = start; i < size; ++i) {
842 dt.checkedOrIn(vd[{(int)i}],
true, Legal);
850 raw_string_ostream ss(
str);
851 ss <<
"Cannot deduce type of load " << I;
852 ss <<
" vd:" << vd.str() <<
" start:" << start <<
" size: " << size
853 <<
" dt:" << dt.str() <<
"\n";
857 assert(dt.isKnown());
859 if (Type *isfloat = dt.isFloat()) {
863 assert(start == 0 && nextStart == LoadSize);
864 addToDiffe(orig_maskInit, prediff, Builder2, isfloat,
865 Builder2.CreateNot(premask));
869 if (nextStart == size)
882 using namespace llvm;
886 for (
auto U : LI.getPointerOperand()->users()) {
887 if (
auto CI = dyn_cast<CallInst>(U)) {
888 if (
auto F = CI->getCalledFunction()) {
889 if (F->getName() ==
"__kmpc_for_static_init_4" ||
890 F->getName() ==
"__kmpc_for_static_init_4u" ||
891 F->getName() ==
"__kmpc_for_static_init_8" ||
892 F->getName() ==
"__kmpc_for_static_init_8u") {
900 auto alignment = LI.getAlign();
901 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
903 bool constantval =
parseTBAA(LI, DL,
nullptr)[{-1}].isIntegral();
909 using namespace llvm;
921 IRBuilder<> BuilderZ(&I);
924 switch (I.getOperation()) {
925 case AtomicRMWInst::FAdd:
926 case AtomicRMWInst::FSub: {
931 auto rule = [&](Value *ptr, Value *dif) -> Value * {
933 dif = Constant::getNullValue(I.getType());
936 AtomicRMWInst *rmw =
nullptr;
937 rmw = BuilderZ.CreateAtomicRMW(I.getOperation(), ptr, dif,
938 I.getAlign(), I.getOrdering(),
940 rmw->setVolatile(I.isVolatile());
942 return Constant::getNullValue(dif->getType());
947 return Constant::getNullValue(dif->getType());
952 I.getType(), BuilderZ, rule,
973 I, I.getPointerOperand(), I.getValOperand(), I.getType(),
974 I.getType(), I.getOperation() == AtomicRMWInst::FSub,
975 I.getAlign(), I.getOrdering(), I.getSyncScopeID(),
991 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
992 auto valType = I.getValOperand()->getType();
993 auto storeSize = DL.getTypeSizeInBits(valType) / 8;
994 auto fp = TR.
firstPointer(storeSize, I.getPointerOperand(), &I, gutils,
997 if (!fp.isKnown() && valType->isIntOrIntVectorTy()) {
1007 llvm::raw_string_ostream ss(s);
1008 ss << *I.getParent()->getParent() <<
"\n" << I <<
"\n";
1009 ss <<
" Active atomic inst not yet handled";
1018 if (!I.getType()->isVoidTy()) {
1021 U.set(UndefValue::get(I.getType()));
1029 using namespace llvm;
1033 for (
auto U : SI.getPointerOperand()->users()) {
1034 if (
auto CI = dyn_cast<CallInst>(U)) {
1035 if (
auto F = CI->getCalledFunction()) {
1036 if (F->getName() ==
"__kmpc_for_static_init_4" ||
1037 F->getName() ==
"__kmpc_for_static_init_4u" ||
1038 F->getName() ==
"__kmpc_for_static_init_8" ||
1039 F->getName() ==
"__kmpc_for_static_init_8u") {
1045 auto align = SI.getAlign();
1048 SI.isVolatile(), SI.getOrdering(), SI.getSyncScopeID(),
1051 bool forceErase =
false;
1061 pair.second.stores.count(&SI) &&
1062 (!pair.second.LI || !pair.second.LI->contains(&SI))) {
1074 llvm::Value *orig_val, llvm::MaybeAlign prevalign,
1075 bool isVolatile, llvm::AtomicOrdering ordering,
1076 llvm::SyncScope::ID syncScope, llvm::Value *mask) {
1077 using namespace llvm;
1080 Type *valType = orig_val->getType();
1082 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
1084 if (unnecessaryStores.count(&I)) {
1092 SmallVector<Metadata *, 1> scopeMD = {
1094 SmallVector<Metadata *, 1> prevScopes;
1095 if (
auto prev = I.getMetadata(LLVMContext::MD_alias_scope)) {
1096 for (
auto &M : cast<MDNode>(prev)->operands()) {
1097 scopeMD.push_back(M);
1098 prevScopes.push_back(M);
1101 auto scope = MDNode::get(I.getContext(), scopeMD);
1103 NewI->setMetadata(LLVMContext::MD_alias_scope, scope);
1105 SmallVector<Metadata *, 1> MDs;
1106 SmallVector<Metadata *, 1> prevNoAlias;
1107 for (
size_t j = 0; j < gutils->
getWidth(); j++) {
1110 if (
auto prev = I.getMetadata(LLVMContext::MD_noalias)) {
1111 for (
auto &M : cast<MDNode>(prev)->operands()) {
1113 prevNoAlias.push_back(M);
1116 auto noscope = MDNode::get(I.getContext(), MDs);
1117 NewI->setMetadata(LLVMContext::MD_noalias, noscope);
1120 parseTBAA(I, DL,
nullptr)[{-1}].isIntegral();
1122 IRBuilder<> BuilderZ(NewI);
1123 BuilderZ.setFastMathFlags(
getFast());
1127 auto storeSize = (DL.getTypeSizeInBits(valType) + 7) / 8;
1129 auto vd = TR.
query(orig_ptr).
Lookup(storeSize, DL);
1131 if (!vd.isKnown()) {
1136 raw_string_ostream ss(
str);
1137 ss <<
"Cannot deduce type of store " << I <<
", assumed " << vd.str()
1145 raw_string_ostream ss(
str);
1146 ss <<
"Cannot deduce type of store " << I;
1156 bool anyPointer =
false;
1164 for (
size_t i = 0; i < storeSize;) {
1167 ct = valTT[{(int)i}];
1168 if (
auto flt = ct.
isFloat()) {
1169 i += DL.getTypeSizeInBits(flt) / 8;
1174 i += DL.getPointerSizeInBits() / 8;
1182 raw_string_ostream ss(
str);
1183 ss <<
"Cannot deduce type of store " << I << vd.str()
1184 <<
" size: " << storeSize <<
" at " << i;
1190 Value *diff =
nullptr;
1191 bool needs_writebarrier =
false;
1194 if (!isa<UndefValue>(orig_val) &&
1195 !isa<ConstantPointerNull>(orig_val)) {
1197 raw_string_ostream ss(
str);
1198 ss <<
"Mismatched activity for: " << I
1199 <<
" const val: " << *orig_val;
1203 wrap(orig_val), wrap(&BuilderZ)));
1205 needs_writebarrier =
true;
1214 if (!diff && vd.isKnown() && !vd.anyPointer(orig_val, DL) &&
1215 !vd.anyFloat(orig_val, DL)) {
1216 auto rule = [&val]() {
return val; };
1225 gutils->
setPtrDiffe(&I, orig_ptr, diff, BuilderZ, prevalign, 0, storeSize,
1226 isVolatile, ordering, syncScope, mask, prevNoAlias,
1227 prevScopes, needs_writebarrier);
1234 IRBuilder<> Builder2(&I);
1235 BasicBlock *merge =
nullptr;
1241 unsigned nextStart = storeSize;
1244 for (
size_t i = start; i < storeSize; ++i) {
1245 auto nex = vd[{(int)i}];
1252 dt.checkedOrIn(nex,
true, Legal);
1258 unsigned size = nextStart - start;
1259 if (!dt.isKnown()) {
1262 raw_string_ostream ss(
str);
1263 ss <<
"Cannot deduce type of store " << I << vd.str()
1264 <<
" start: " << start <<
" size: " << size
1265 <<
" storeSize: " << storeSize;
1272 if (start % prevalign->value() == 0)
1278 if (Type *FT = dt.isFloat()) {
1293 if (!isa<AllocaInst>(basePtr) &&
1300 shadow_ptr = gutils->
extractMeta(Builder2, shadow_ptr, 0);
1302 Value *shadow = Builder2.CreateICmpNE(primal_ptr, shadow_ptr);
1304 BasicBlock *current = Builder2.GetInsertBlock();
1306 current, current->getName() +
"_active");
1308 current->getName() +
"_amerge");
1309 Builder2.CreateCondBr(shadow, conditional, merge);
1310 Builder2.SetInsertPoint(conditional);
1318 Builder2, align, start, size, isVolatile, ordering, syncScope,
1319 mask, prevNoAlias, prevScopes);
1322 Value *maskL = mask;
1328 auto rule = [&](Value *dif1Ptr) {
1330 Builder2.CreateLoad(valType, dif1Ptr, isVolatile);
1332 dif1->setAlignment(*align);
1333 dif1->setOrdering(ordering);
1334 dif1->setSyncScopeID(syncScope);
1336 SmallVector<Metadata *, 1> scopeMD = {
1338 for (
auto M : prevScopes)
1339 scopeMD.push_back(M);
1341 SmallVector<Metadata *, 1> MDs;
1342 for (ssize_t j = -1; j < gutils->
getWidth(); j++) {
1343 if (j != (ssize_t)idx)
1346 for (
auto M : prevNoAlias)
1349 dif1->setMetadata(LLVMContext::MD_alias_scope,
1350 MDNode::get(I.getContext(), scopeMD));
1351 dif1->setMetadata(LLVMContext::MD_noalias,
1352 MDNode::get(I.getContext(), MDs));
1353 dif1->setMetadata(LLVMContext::MD_tbaa,
1354 I.getMetadata(LLVMContext::MD_tbaa));
1355 dif1->setMetadata(LLVMContext::MD_tbaa_struct,
1356 I.getMetadata(LLVMContext::MD_tbaa_struct));
1363 maskL =
lookup(mask, Builder2);
1364 Type *tys[] = {valType, orig_ptr->getType()};
1366 Intrinsic::masked_load, tys);
1368 ConstantInt::get(Type::getInt32Ty(mask->getContext()),
1369 align ? align->value() : 0);
1373 auto rule = [&](Value *ip) {
1374 Value *args[] = {ip, alignv, maskL,
1375 Constant::getNullValue(valType)};
1376 diff = Builder2.CreateCall(F, args);
1386 Builder2, align, start, size, isVolatile, ordering, syncScope,
1387 mask, prevNoAlias, prevScopes);
1389 ->addToDiffe(orig_val, diff, Builder2, FT, start, size, {},
1400 Value *diff = constantval
1401 ? Constant::getNullValue(diffeTy)
1403 gutils->
setPtrDiffe(&I, orig_ptr, diff, BuilderZ, align, start, size,
1404 isVolatile, ordering, syncScope, mask,
1405 prevNoAlias, prevScopes);
1417 if (Constant *C = dyn_cast<Constant>(orig_val)) {
1418 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
1419 C = CE->getOperand(0);
1421 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
1422 if (GV->getName() ==
"ompi_request_null") {
1428 bool forwardsShadow, backwardsShadow;
1435 (forwardsShadow || backwardsShadow)) ||
1439 Value *valueop =
nullptr;
1441 bool needs_writebarrier =
false;
1445 if (!isa<UndefValue>(orig_val) &&
1446 !isa<ConstantPointerNull>(orig_val)) {
1448 raw_string_ostream ss(
str);
1449 ss <<
"Mismatched activity for: " << I
1450 <<
" const val: " << *orig_val;
1454 gutils, wrap(orig_val), wrap(&BuilderZ)));
1456 needs_writebarrier =
true;
1468 for (
unsigned i = 0; i < gutils->
getWidth(); ++i) {
1469 array = BuilderZ.CreateInsertValue(array, val, {i});
1477 gutils->
setPtrDiffe(&I, orig_ptr, valueop, BuilderZ, align, start,
1478 size, isVolatile, ordering, syncScope, mask,
1479 prevNoAlias, prevScopes, needs_writebarrier);
1483 if (nextStart == storeSize)
1489 Builder2.CreateBr(merge);
1490 Builder2.SetInsertPoint(merge);
1527 using namespace llvm;
1540 if (I.getType()->isPointerTy() ||
1541 I.getOpcode() == CastInst::CastOps::PtrToInt)
1544 Value *orig_op0 = I.getOperand(0);
1547 IRBuilder<> Builder2(&I);
1552 if (orig_op0->getType()->isSized())
1554 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
1555 orig_op0->getType()) +
1560 if (
auto ET = I.getSrcTy()->getScalarType())
1561 if (ET->isFPOrFPVectorTy()) {
1564 "failed to deduce adding type of cast ", I,
1565 " assumed ", FT,
" from src");
1569 if (
auto ET = I.getDestTy()->getScalarType())
1570 if (ET->isFPOrFPVectorTy()) {
1573 "failed to deduce adding type of cast ", I,
1574 " assumed ", FT,
" from dst");
1582 if (
auto ET = I.getSrcTy()->getScalarType())
1583 if (ET->isIntOrIntVectorTy()) {
1585 "failed to deduce adding type of cast ", I,
1586 " assumed integral from src");
1591 raw_string_ostream ss(
str);
1592 ss <<
"Cannot deduce adding type (cast) of " << I;
1598 auto rule = [&](Value *dif) {
1599 if (I.getOpcode() == CastInst::CastOps::FPTrunc ||
1600 I.getOpcode() == CastInst::CastOps::FPExt) {
1601 return Builder2.CreateFPCast(dif, op0->getType());
1602 }
else if (I.getOpcode() == CastInst::CastOps::BitCast) {
1603 return Builder2.CreateBitCast(dif, op0->getType());
1604 }
else if (I.getOpcode() == CastInst::CastOps::Trunc) {
1606 return Builder2.CreateZExt(dif, op0->getType());
1609 llvm::raw_string_ostream ss(s);
1610 ss << *I.getParent()->getParent() <<
"\n";
1611 ss <<
"cannot handle above cast " << I <<
"\n";
1613 return (llvm::Value *)UndefValue::get(op0->getType());
1617 Value *dif =
diffe(&I, Builder2);
1618 Value *diff =
applyChainRule(op0->getType(), Builder2, rule, dif);
1625 setDiffe(&I, Constant::getNullValue(diffTy), Builder2);
1648 if (SI.getType()->isPointerTy())
1663 using namespace llvm;
1666 Value *orig_op1 = SI.getOperand(1);
1668 Value *orig_op2 = SI.getOperand(2);
1672 IRBuilder<> Builder2(&SI);
1675 Value *dif1 =
nullptr;
1676 Value *dif2 =
nullptr;
1679 if (orig_op1->getType()->isSized())
1680 size = (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
1681 orig_op1->getType()) +
1690 for (
int i = 0; i < 2; i++)
1691 if (
auto P0 = dyn_cast<PHINode>(SI.getOperand(i + 1))) {
1693 SmallVector<Instruction *, 4> activeUses;
1694 for (
auto u : P0->users()) {
1696 activeUses.push_back(cast<Instruction>(u));
1698 activeUses.push_back(cast<Instruction>(u));
1700 if (activeUses.size() == 1 && activeUses[0] == &SI &&
1704 SmallVector<BasicBlock *, 1> Latches;
1705 gutils->
OrigLI->getLoopFor(P0->getParent())->getLoopLatches(Latches);
1706 bool allIncoming =
true;
1707 for (
auto Latch : Latches) {
1708 if (&SI != P0->getIncomingValueForBlock(Latch)) {
1709 allIncoming =
false;
1713 if (allIncoming && lc.
exitBlocks.size() == 1) {
1715 auto addingType = TR.
addingType(size, SI.getOperand(2 - i));
1722 if (VectorType *VTy =
1723 dyn_cast<VectorType>(SI.getOperand(0)->getType())) {
1724 inc = Builder2.CreateVectorSplat(VTy->getElementCount(), inc);
1726 Value *difSI =
diffe(&SI, Builder2);
1728 Builder2.CreateICmpEQ(gutils->
lookupM(index, EB), inc);
1731 Constant::getNullValue(
1733 addToDiffe(SI.getOperand(2 - i), dif, Builder2, addingType);
1743 Builder2,
lookup(op0, Builder2),
diffe(&SI, Builder2),
1744 Constant::getNullValue(gutils->
getShadowType(op1->getType())),
1745 "diffe" + op1->getName());
1748 Builder2,
lookup(op0, Builder2),
1749 Constant::getNullValue(gutils->
getShadowType(op2->getType())),
1750 diffe(&SI, Builder2),
"diffe" + op2->getName());
1755 Type *addingType = TR.
addingType(size, orig_op1);
1757 addToDiffe(orig_op1, dif1, Builder2, addingType);
1759 llvm::errs() <<
" warning: assuming integral for " << SI <<
"\n";
1762 Type *addingType = TR.
addingType(size, orig_op2);
1764 addToDiffe(orig_op2, dif2, Builder2, addingType);
1766 llvm::errs() <<
" warning: assuming integral for " << SI <<
"\n";
1771 using namespace llvm;
1785 IRBuilder<> Builder2(&EEI);
1788 Value *orig_vec = EEI.getVectorOperand();
1793 if (EEI.getType()->isSized())
1795 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
1799 auto diff =
diffe(&EEI, Builder2);
1803 ->addToDiffe(orig_vec, diff, Builder2, TR.
addingType(size, &EEI),
1806 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
1807 Value *sv[] = {
nullptr,
1809 sv[0] = ConstantInt::get(sv[1]->getType(), i);
1811 ->addToDiffe(orig_vec, gutils->
extractMeta(Builder2, diff, i),
1817 Constant::getNullValue(gutils->
getShadowType(EEI.getType())),
1828 using namespace llvm;
1843 IRBuilder<> Builder2(&IEI);
1846 Value *dif1 =
diffe(&IEI, Builder2);
1848 Value *orig_op0 = IEI.getOperand(0);
1849 Value *orig_op1 = IEI.getOperand(1);
1854 if (orig_op0->getType()->isSized())
1856 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
1857 orig_op0->getType()) +
1861 if (orig_op1->getType()->isSized())
1863 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
1864 orig_op1->getType()) +
1872 Builder2.CreateInsertElement(
1874 Constant::getNullValue(gutils->
getShadowType(op1->getType())),
1878 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
1879 Value *sv[] = {ConstantInt::get(op2->getType(), i)};
1881 ->addToDiffe(orig_op0,
1882 Builder2.CreateInsertElement(
1884 Constant::getNullValue(op1->getType()),
1886 Builder2, TR.
addingType(size0, orig_op0), sv);
1894 Builder2.CreateExtractElement(dif1,
lookup(op2, Builder2)),
1897 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
1898 Value *sv[] = {ConstantInt::get(op2->getType(), i)};
1900 ->addToDiffe(orig_op1,
1901 Builder2.CreateExtractElement(
1904 Builder2, TR.
addingType(size1, orig_op1), sv);
1910 Constant::getNullValue(gutils->
getShadowType(IEI.getType())),
1921 using namespace llvm;
1936 IRBuilder<> Builder2(&SVI);
1939 auto loaded =
diffe(&SVI, Builder2);
1941 cast<VectorType>(SVI.getOperand(0)->getType())->getElementCount();
1942 assert(!count.isScalable());
1943 size_t l1 = count.getKnownMinValue();
1944 uint64_t instidx = 0;
1946 for (
size_t idx : SVI.getShuffleMask()) {
1947 auto opnum = (idx < l1) ? 0 : 1;
1948 auto opidx = (idx < l1) ? idx : (idx - l1);
1952 if (SVI.getOperand(opnum)->getType()->isSized())
1953 size = (gutils->
newFunc->getParent()
1955 .getTypeSizeInBits(SVI.getOperand(opnum)->getType()) +
1960 ConstantInt::get(Type::getInt32Ty(SVI.getContext()), opidx)};
1961 Value *toadd = Builder2.CreateExtractElement(loaded, instidx);
1963 ->addToDiffe(SVI.getOperand(opnum), toadd, Builder2,
1964 TR.
addingType(size, SVI.getOperand(opnum)), sv);
1966 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
1968 ConstantInt::get(Type::getInt32Ty(SVI.getContext()), i),
1969 ConstantInt::get(Type::getInt32Ty(SVI.getContext()), opidx)};
1970 Value *toadd = Builder2.CreateExtractElement(
1973 ->addToDiffe(SVI.getOperand(opnum), toadd, Builder2,
1974 TR.
addingType(size, SVI.getOperand(opnum)), sv);
1981 Constant::getNullValue(gutils->
getShadowType(SVI.getType())),
1992 using namespace llvm;
1997 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
1998 llvm::errs() << EVI <<
"\n";
1999 llvm_unreachable(
"Illegal activity for extractvalue");
2013 if (EVI.getType()->isPointerTy())
2015 IRBuilder<> Builder2(&EVI);
2018 Value *orig_op0 = EVI.getOperand(0);
2020 auto prediff =
diffe(&EVI, Builder2);
2024 SmallVector<Value *, 4> sv;
2025 for (
auto i : EVI.getIndices())
2026 sv.push_back(ConstantInt::get(Type::getInt32Ty(EVI.getContext()), i));
2027 size_t storeSize = 1;
2028 if (EVI.getType()->isSized())
2030 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
2036 const auto &vd = TR.
query(&EVI);
2039 unsigned nextStart = storeSize;
2042 for (
size_t i = start; i < storeSize; ++i) {
2043 auto nex = vd[{(int)i}];
2050 dt.checkedOrIn(nex,
true, Legal);
2056 unsigned size = nextStart - start;
2057 if (!dt.isKnown()) {
2060 if (EVI.getType()->isFPOrFPVectorTy()) {
2063 }
else if (EVI.getType()->isIntOrIntVectorTy() ||
2064 EVI.getType()->isPointerTy()) {
2071 raw_string_ostream ss(
str);
2072 ss <<
"Cannot deduce type of extract " << EVI << vd.str()
2073 <<
" start: " << start <<
" size: " << size
2074 <<
" extractSize: " << storeSize;
2078 if (
auto FT = dt.isFloat())
2080 ->addToDiffe(orig_op0, prediff, Builder2, FT, start, size, sv,
2081 nullptr, sv.size());
2083 if (nextStart == storeSize)
2090 Constant::getNullValue(gutils->
getShadowType(EVI.getType())),
2101 using namespace llvm;
2117 bool hasNonPointer =
false;
2118 if (
auto st = dyn_cast<StructType>(IVI.getType())) {
2119 for (
unsigned i = 0; i < st->getNumElements(); ++i) {
2120 if (!st->getElementType(i)->isPointerTy()) {
2121 hasNonPointer =
true;
2124 }
else if (
auto at = dyn_cast<ArrayType>(IVI.getType())) {
2125 if (!at->getElementType()->isPointerTy()) {
2126 hasNonPointer =
true;
2132 bool floatingInsertion =
false;
2133 if (
auto MD =
hasMetadata(&IVI,
"enzyme_truetype")) {
2135 for (
auto &tuple : toIterate) {
2136 Type *ty = std::get<0>(tuple);
2137 if (ty && ty->isFloatingPointTy()) {
2138 floatingInsertion =
true;
2143 for (InsertValueInst *iv = &IVI;;) {
2145 if (iv->getInsertedValueOperand()->getType()->isSized() &&
2146 (iv->getInsertedValueOperand()->getType()->isIntOrIntVectorTy() ||
2147 iv->getInsertedValueOperand()->getType()->isFPOrFPVectorTy()))
2149 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
2150 iv->getInsertedValueOperand()->getType()) +
2153 auto it = TR.
intType(size0, iv->getInsertedValueOperand(), iv, gutils,
2155 if (it.isFloat() || !it.isKnown()) {
2156 floatingInsertion =
true;
2159 Value *val = iv->getAggregateOperand();
2162 if (
auto dc = dyn_cast<InsertValueInst>(val)) {
2166 floatingInsertion =
true;
2172 if (!floatingInsertion)
2182 assert(0 &&
"should be handled above");
2186 IRBuilder<> Builder2(&IVI);
2189 Value *orig_inserted = IVI.getInsertedValueOperand();
2190 Value *orig_agg = IVI.getAggregateOperand();
2193 if (orig_inserted->getType()->isSized())
2195 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
2196 orig_inserted->getType()) +
2201 const auto &TT = TR.
query(orig_inserted);
2204 Value *dindex =
nullptr;
2206 auto &dl = gutils->
newFunc->getParent()->getDataLayout();
2208 unsigned nextStart = size0;
2211 for (
size_t i = start; i < size0;) {
2212 auto nex = TT[{(int)i}];
2219 dt.checkedOrIn(nex,
true, Legal);
2224 if (
auto fltType = dt.isFloat()) {
2225 i += dl.getTypeSizeInBits(fltType) / 8;
2227 i += dl.getPointerSizeInBits() / 8;
2232 Type *flt = dt.isFloat();
2233 if (!dt.isKnown()) {
2236 if (orig_inserted->getType()->isFPOrFPVectorTy()) {
2237 flt = orig_inserted->getType()->getScalarType();
2239 }
else if (orig_inserted->getType()->isIntOrIntVectorTy() ||
2240 orig_inserted->getType()->isPointerTy()) {
2247 raw_string_ostream ss(
str);
2248 ss <<
"Cannot deduce type of insertvalue ins " << IVI
2249 <<
" size: " << size0 <<
" TT: " << TT.str();
2256 auto rule = [&](Value *prediff) {
2257 return Builder2.CreateExtractValue(prediff, IVI.getIndices());
2259 auto prediff =
diffe(&IVI, Builder2);
2260 dindex =
applyChainRule(orig_inserted->getType(), Builder2, rule,
2265 ->addToDiffe(orig_inserted, dindex, Builder2, flt, start,
2268 if (nextStart == size0)
2275 if (orig_agg->getType()->isSized())
2277 (gutils->
newFunc->getParent()->getDataLayout().getTypeSizeInBits(
2278 orig_agg->getType()) +
2284 const auto &TT = TR.
query(orig_agg);
2285 const MDNode *MD =
hasMetadata(&IVI,
"enzyme_truetype");
2289 Value *dindex =
nullptr;
2291 auto &dl = gutils->
newFunc->getParent()->getDataLayout();
2293 unsigned nextStart = size1;
2296 for (
size_t i = start; i < size1;) {
2297 auto nex = TT[{(int)i}];
2299 for (
size_t j = 0; j < MD->getNumOperands(); j += 2) {
2301 llvm::cast<llvm::MDString>(MD->getOperand(j))->getString(),
2303 auto offset = llvm::cast<llvm::ConstantInt>(
2304 llvm::cast<llvm::ConstantAsMetadata>(
2305 MD->getOperand(j + 1))
2308 if (offset == (int64_t)i) {
2320 dt.checkedOrIn(nex,
true, Legal);
2325 if (
auto fltType = dt.isFloat()) {
2326 i += dl.getTypeSizeInBits(fltType) / 8;
2328 i += dl.getPointerSizeInBits() / 8;
2333 Type *flt = dt.isFloat();
2334 if (!dt.isKnown()) {
2337 if (orig_agg->getType()->isFPOrFPVectorTy()) {
2338 flt = orig_agg->getType()->getScalarType();
2340 }
else if (orig_agg->getType()->isIntOrIntVectorTy() ||
2341 orig_agg->getType()->isPointerTy()) {
2348 raw_string_ostream ss(
str);
2349 ss <<
"Cannot deduce type of insertvalue agg " << IVI
2350 <<
" start: " << start <<
" size: " << size1
2351 <<
" TT: " << TT.str();
2358 auto rule = [&](Value *prediff) {
2359 return Builder2.CreateInsertValue(
2360 prediff, Constant::getNullValue(orig_inserted->getType()),
2363 auto prediff =
diffe(&IVI, Builder2);
2368 ->addToDiffe(orig_agg, dindex, Builder2, flt, start,
2371 if (nextStart == size1)
2378 Constant::getNullValue(gutils->
getShadowType(IVI.getType())),
2389 ((
GradientUtils *)gutils)->getReverseBuilder(Builder2, original);
2396 llvm::Value *
diffe(llvm::Value *val, llvm::IRBuilder<> &Builder) {
2402 llvm::IRBuilder<> &Builder) {
2409 template <
typename Func,
typename...
Args>
2411 Func rule,
Args... args) {
2413 ->applyChainRule(diffType, Builder, rule, args...);
2418 template <
typename Func,
typename...
Args>
2420 ((
GradientUtils *)gutils)->applyChainRule(Builder, rule, args...);
2425 template <
typename Func>
2427 llvm::IRBuilder<> &Builder, Func rule) {
2428 ((
GradientUtils *)gutils)->applyChainRule(diffs, Builder, rule);
2438 llvm::SmallVector<llvm::SelectInst *, 4>
2439 addToDiffe(llvm::Value *val, llvm::Value *dif, llvm::IRBuilder<> &Builder,
2440 llvm::Type *T, llvm::Value *mask =
nullptr) {
2442 ->addToDiffe(val, dif, Builder, T, {}, mask);
2445 llvm::Value *
lookup(llvm::Value *val, llvm::IRBuilder<> &Builder) {
2446 return gutils->
lookupM(val, Builder);
2452 if (BO.getOpcode() == llvm::Instruction::FDiv &&
2456 using namespace llvm;
2463 Value *orig_op0 = BO.getOperand(0);
2464 if (
auto P0 = dyn_cast<PHINode>(orig_op0)) {
2466 SmallVector<Instruction *, 4> activeUses;
2467 for (
auto u : P0->users()) {
2469 activeUses.push_back(cast<Instruction>(u));
2471 activeUses.push_back(cast<Instruction>(u));
2474 if (activeUses.size() == 1 && activeUses[0] == &BO &&
2478 SmallVector<BasicBlock *, 1> Latches;
2479 gutils->
OrigLI->getLoopFor(P0->getParent())->getLoopLatches(Latches);
2480 bool allIncoming =
true;
2481 for (
auto Latch : Latches) {
2482 if (&BO != P0->getIncomingValueForBlock(Latch)) {
2483 allIncoming =
false;
2487 if (allIncoming && lc.
exitBlocks.size() == 1) {
2489 IRBuilder<> Builder2(&BO);
2492 Value *orig_op1 = BO.getOperand(1);
2494 Value *dif1 =
nullptr;
2495 Value *idiff =
diffe(&BO, Builder2);
2497 Type *addingType = BO.getType();
2502 Value *Pstart = P0->getIncomingValueForBlock(
2508 auto rule = [&](Value *idiff) {
2509 auto res = Builder2.CreateFDiv(
2510 Builder2.CreateFNeg(Builder2.CreateFMul(idiff, lop0)),
2515 Builder2.CreateFCmpOEQ(
2516 idiff, Constant::getNullValue(idiff->getType())),
2529 Value *lop0 =
lookup(product, EB);
2532 auto rule = [&](Value *idiff) {
2533 auto res = Builder2.CreateFDiv(
2534 Builder2.CreateFNeg(Builder2.CreateFMul(
2535 s, Builder2.CreateFDiv(idiff, lop0))),
2540 Builder2.CreateFCmpOEQ(
2541 idiff, Constant::getNullValue(idiff->getType())),
2549 addToDiffe(orig_op1, dif1, Builder2, addingType);
2558 using namespace llvm;
2559 switch (BO.getOpcode()) {
2560#include "BinopDerivatives.inc"
2587 using namespace llvm;
2589 IRBuilder<> Builder2(&BO);
2592 Value *orig_op0 = BO.getOperand(0);
2593 Value *orig_op1 = BO.getOperand(1);
2595 Value *dif0 =
nullptr;
2596 Value *dif1 =
nullptr;
2597 Value *idiff =
diffe(&BO, Builder2);
2599 Type *addingType = BO.getType();
2601 switch (BO.getOpcode()) {
2602 case Instruction::LShr: {
2604 if (
auto ci = dyn_cast<ConstantInt>(orig_op1)) {
2606 if (orig_op0->getType()->isSized())
2607 size = (gutils->
newFunc->getParent()
2609 .getTypeSizeInBits(orig_op0->getType()) +
2613 if (Type *flt = TR.
addingType(size, orig_op0)) {
2614 auto bits = gutils->
newFunc->getParent()
2616 .getTypeAllocSizeInBits(flt);
2617 if (ci->getSExtValue() >= (int64_t)bits &&
2618 ci->getSExtValue() % bits == 0) {
2619 auto rule = [&](Value *idiff) {
2620 return Builder2.CreateShl(idiff, ci);
2622 dif0 =
applyChainRule(orig_op0->getType(), Builder2, rule, idiff);
2630 llvm::errs() <<
"warning: binary operator is integer and constant: "
2637 case Instruction::AShr: {
2639 llvm::errs() <<
"warning: binary operator is integer and constant: "
2646 case Instruction::And: {
2648 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2653 for (
int i = 0; i < 2; ++i) {
2654 auto CI = dyn_cast<ConstantInt>(BO.getOperand(i));
2655 if (CI && dl.getTypeSizeInBits(eFT) ==
2656 dl.getTypeSizeInBits(CI->getType())) {
2657 if (eFT->isDoubleTy() && CI->getValue() == -134217728) {
2660 Constant::getNullValue(gutils->
getShadowType(BO.getType())),
2669 llvm::errs() <<
"warning: binary operator is integer and constant: "
2676 case Instruction::Xor: {
2677 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2683 for (
int i = 0; i < 2; ++i) {
2687 Constant::getNullValue(gutils->
getShadowType(BO.getType())),
2689 auto isZero = Builder2.CreateICmpEQ(
2691 Constant::getNullValue(BO.getType()));
2692 auto rule = [&](Value *idiff) {
2693 auto ext = Builder2.CreateBitCast(idiff, FT);
2694 auto neg = Builder2.CreateFNeg(ext);
2696 neg = Builder2.CreateBitCast(neg, BO.getType());
2699 auto bc =
applyChainRule(BO.getOperand(1 - i)->getType(), Builder2,
2701 addToDiffe(BO.getOperand(1 - i), bc, Builder2, FT);
2706 llvm::errs() <<
"warning: binary operator is integer and constant: "
2713 case Instruction::Or: {
2714 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2720 for (
int i = 0; i < 2; ++i) {
2721 auto CI = dyn_cast<ConstantInt>(BO.getOperand(i));
2722 if (
auto CV = dyn_cast<ConstantVector>(BO.getOperand(i))) {
2723 CI = dyn_cast_or_null<ConstantInt>(CV->getSplatValue());
2724 FT = VectorType::get(FT, CV->getType()->getElementCount());
2726 if (
auto CV = dyn_cast<ConstantDataVector>(BO.getOperand(i))) {
2727 CI = dyn_cast_or_null<ConstantInt>(CV->getSplatValue());
2728 FT = VectorType::get(FT, CV->getType()->getElementCount());
2730 if (CI && dl.getTypeSizeInBits(eFT) ==
2731 dl.getTypeSizeInBits(CI->getType())) {
2732 auto AP = CI->getValue();
2733 bool validXor =
false;
2734#if LLVM_VERSION_MAJOR > 16
2737 if (AP.isNullValue())
2744#
if LLVM_VERSION_MAJOR > 16
2745 && (AP & ~0b01111111100000000000000000000000ULL).isZero()
2747 && (AP & ~0b01111111100000000000000000000000ULL).isNullValue()
2751#
if LLVM_VERSION_MAJOR > 16
2754 ~0b0111111111110000000000000000000000000000000000000000000000000000ULL)
2759 ~0b0111111111110000000000000000000000000000000000000000000000000000ULL)
2768 Constant::getNullValue(gutils->
getShadowType(BO.getType())),
2774 auto rule = [&](Value *idiff) {
2775 auto prev = Builder2.CreateOr(arg, BO.getOperand(i));
2776 prev = Builder2.CreateSub(prev, arg,
"",
true,
2779 if (eFT->isFloatTy()) {
2782 assert(eFT->isDoubleTy());
2783 num = 1023ULL << 52;
2785 prev = Builder2.CreateAdd(
2786 prev, ConstantInt::get(prev->getType(), num,
false),
"",
2788 auto prevFP = Builder2.CreateBitCast(prev, FT);
2789 auto idiffFP = Builder2.CreateBitCast(idiff, FT);
2790 prev = Builder2.CreateBitCast(
2797 Builder2, rule, idiff);
2798 addToDiffe(BO.getOperand(1 - i), prev, Builder2, FT);
2804 llvm::errs() <<
"warning: binary operator is integer and constant: "
2811 case Instruction::UDiv:
2812 case Instruction::URem:
2813 case Instruction::SRem:
2814 case Instruction::SDiv:
2815 case Instruction::Shl:
2816 case Instruction::Mul:
2817 case Instruction::Sub:
2818 case Instruction::Add: {
2821 <<
"warning: binary operator is integer and assumed constant: "
2831 llvm::raw_string_ostream ss(s);
2832 ss << *gutils->
oldFunc <<
"\n";
2833 for (
auto &arg : gutils->
oldFunc->args()) {
2834 ss <<
" constantarg[" << arg <<
"] = " << gutils->
isConstantValue(&arg)
2835 <<
" type: " << TR.
query(&arg).
str() <<
" - vals: {";
2840 for (
auto &BB : *gutils->
oldFunc)
2841 for (
auto &I : BB) {
2842 ss <<
" constantinst[" << I
2845 <<
" type: " << TR.
query(&I).
str() <<
"\n";
2847 ss <<
"cannot handle unknown binary operator: " << BO <<
"\n";
2856 addToDiffe(orig_op0, dif0, Builder2, addingType);
2858 addToDiffe(orig_op1, dif1, Builder2, addingType);
2862 using namespace llvm;
2869 IRBuilder<> Builder2(&BO);
2872 Value *orig_op0 = BO.getOperand(0);
2873 Value *orig_op1 = BO.getOperand(1);
2878 switch (BO.getOpcode()) {
2879 case Instruction::And: {
2881 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2887 for (
int i = 0; i < 2; ++i) {
2888 auto CI = dyn_cast<ConstantInt>(BO.getOperand(i));
2889 if (CI && dl.getTypeSizeInBits(eFT) ==
2890 dl.getTypeSizeInBits(CI->getType())) {
2891 if (eFT->isDoubleTy() && CI->getValue() == -134217728) {
2892 setDiffe(&BO, Constant::getNullValue(diffTy), Builder2);
2901 llvm::errs() <<
"warning: binary operator is integer and constant: "
2908 case Instruction::Xor: {
2909 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2914 Value *dif[2] = {constantval0 ? nullptr :
diffe(orig_op0, Builder2),
2915 constantval1 ? nullptr :
diffe(orig_op1, Builder2)};
2917 for (
int i = 0; i < 2; ++i) {
2919 dif[1 - i] && !dif[i]) {
2920 auto isZero = Builder2.CreateICmpEQ(
2922 Constant::getNullValue(BO.getType()));
2923 auto rule = [&](Value *idiff) {
2924 auto ext = Builder2.CreateBitCast(idiff, FT);
2925 auto neg = Builder2.CreateFNeg(ext);
2927 neg = Builder2.CreateBitCast(neg, BO.getType());
2930 auto bc =
applyChainRule(BO.getOperand(1 - i)->getType(), Builder2,
2938 llvm::errs() <<
"warning: binary operator is integer and constant: "
2945 case Instruction::Or: {
2946 auto &dl = gutils->
oldFunc->getParent()->getDataLayout();
2948 Value *dif[2] = {constantval0 ? nullptr :
diffe(orig_op0, Builder2),
2949 constantval1 ? nullptr :
diffe(orig_op1, Builder2)};
2955 for (
int i = 0; i < 2; ++i) {
2956 auto CI = dyn_cast<ConstantInt>(BO.getOperand(i));
2957 if (
auto CV = dyn_cast<ConstantVector>(BO.getOperand(i))) {
2958 CI = dyn_cast_or_null<ConstantInt>(CV->getSplatValue());
2959 FT = VectorType::get(FT, CV->getType()->getElementCount());
2961 if (
auto CV = dyn_cast<ConstantDataVector>(BO.getOperand(i))) {
2962 CI = dyn_cast_or_null<ConstantInt>(CV->getSplatValue());
2963 FT = VectorType::get(FT, CV->getType()->getElementCount());
2965 if (CI && dl.getTypeSizeInBits(eFT) ==
2966 dl.getTypeSizeInBits(CI->getType())) {
2967 auto AP = CI->getValue();
2968 bool validXor =
false;
2969#if LLVM_VERSION_MAJOR > 16
2972 if (AP.isNullValue())
2979#
if LLVM_VERSION_MAJOR > 16
2980 && (AP & ~0b01111111100000000000000000000000ULL).isZero()
2982 && (AP & ~0b01111111100000000000000000000000ULL).isNullValue()
2986#
if LLVM_VERSION_MAJOR > 16
2989 ~0b0111111111110000000000000000000000000000000000000000000000000000ULL)
2994 ~0b0111111111110000000000000000000000000000000000000000000000000000ULL)
3001 auto rule = [&](Value *difi) {
3003 auto prev = Builder2.CreateOr(arg, BO.getOperand(i));
3004 prev = Builder2.CreateSub(prev, arg,
"",
true,
3007 if (eFT->isFloatTy()) {
3010 assert(eFT->isDoubleTy());
3011 num = 1023ULL << 52;
3013 prev = Builder2.CreateAdd(
3014 prev, ConstantInt::get(prev->getType(), num,
false),
"",
3016 auto prevFP = Builder2.CreateBitCast(prev, FT);
3017 auto difiFP = Builder2.CreateBitCast(difi, FT);
3018 prev = Builder2.CreateBitCast(
3034 llvm::errs() <<
"warning: binary operator is integer and constant: "
3041 case Instruction::LShr: {
3043 if (
auto ci = dyn_cast<ConstantInt>(orig_op1)) {
3045 if (orig_op0->getType()->isSized())
3046 size = (gutils->
newFunc->getParent()
3048 .getTypeSizeInBits(orig_op0->getType()) +
3052 if (Type *flt = TR.
addingType(size, orig_op0)) {
3053 auto bits = gutils->
newFunc->getParent()
3055 .getTypeAllocSizeInBits(flt);
3056 if (ci->getSExtValue() >= (int64_t)bits &&
3057 ci->getSExtValue() % bits == 0) {
3058 auto rule = [&](Value *idiff) {
3059 return Builder2.CreateLShr(idiff, ci);
3062 diffe(orig_op0, Builder2));
3071 llvm::errs() <<
"warning: binary operator is integer and constant: "
3078 case Instruction::AShr:
3079 case Instruction::SDiv:
3080 case Instruction::UDiv:
3081 case Instruction::SRem:
3082 case Instruction::URem:
3083 case Instruction::Shl:
3084 case Instruction::Mul:
3085 case Instruction::Sub:
3086 case Instruction::Add: {
3089 llvm::errs() <<
"warning: binary operator is integer and constant: "
3099 llvm::raw_string_ostream ss(s);
3100 ss << *gutils->
oldFunc <<
"\n";
3101 for (
auto &arg : gutils->
oldFunc->args()) {
3102 ss <<
" constantarg[" << arg <<
"] = " << gutils->
isConstantValue(&arg)
3103 <<
" type: " << TR.
query(&arg).
str() <<
" - vals: {";
3108 for (
auto &BB : *gutils->
oldFunc)
3109 for (
auto &I : BB) {
3110 ss <<
" constantinst[" << I
3113 <<
" type: " << TR.
query(&I).
str() <<
"\n";
3115 ss <<
"cannot handle unknown binary operator: " << BO <<
"\n";
3118 rval = Constant::getNullValue(gutils->
getShadowType(BO.getType()));
3122 auto placeholder = cast<PHINode>(&*ifound->second);
3125 gutils->
erase(placeholder);
3139 using namespace llvm;
3141 IRBuilder<> BuilderZ(&MS);
3144 IRBuilder<> Builder2(&MS);
3149 bool forceErase =
false;
3153 pair.second.stores.count(&MS) && pair.second.LI) {
3163 Value *orig_op0 = MS.getArgOperand(0);
3164 Value *orig_op1 = MS.getArgOperand(1);
3173 if (
auto CI = dyn_cast<ConstantInt>(orig_op1))
3175 activeValToSet =
false;
3176 if (activeValToSet) {
3178 llvm::raw_string_ostream ss(s);
3179 ss <<
"couldn't handle non constant inst in memset to "
3180 "propagate differential to\n"
3190 Value *op3 =
nullptr;
3191 if (3 < MS.arg_size()) {
3205 SmallVector<Value *, 4> args = {op0, op1, op2};
3207 args.push_back(op3);
3210 if (
startsWith(funcName,
"memset_pattern") ||
3211 startsWith(funcName,
"llvm.experimental.memset"))
3212 cal = Builder2.CreateMemSet(
3213 op0, ConstantInt::get(Builder2.getInt8Ty(), 0), op2, {});
3215 cal = BuilderZ.CreateCall(MS.getCalledFunction(), args, Defs);
3217 llvm::SmallVector<unsigned int, 9> ToCopy2(
MD_ToCopy);
3218 ToCopy2.push_back(LLVMContext::MD_noalias);
3219 cal->copyMetadata(MS, ToCopy2);
3220 if (
auto m =
hasMetadata(&MS,
"enzyme_zerostack"))
3221 cal->setMetadata(
"enzyme_zerostack", m);
3223 cal->setMetadata(
"enzyme_truetype", m);
3225 if (
startsWith(funcName,
"memset_pattern") ||
3226 startsWith(funcName,
"llvm.experimental.memset")) {
3227 AttributeList NewAttrs;
3229 {AttributeList::ReturnIndex, AttributeList::FunctionIndex,
3230 AttributeList::FirstArgIndex})
3231 for (
auto attr : MS.getAttributes().getAttributes(idx))
3233 NewAttrs.addAttributeAtIndex(MS.getContext(), idx, attr);
3234 cal->setAttributes(NewAttrs);
3236 cal->setAttributes(MS.getAttributes());
3237 cal->setCallingConv(MS.getCallingConv());
3238 cal->setTailCallKind(MS.getTailCallKind());
3245 bool forwardsShadow, backwardsShadow;
3249 if (
auto ci = dyn_cast<ConstantInt>(MS.getOperand(2))) {
3250 size = ci->getLimitedValue();
3257 llvm::errs() << MS <<
"\n";
3262 std::vector<std::tuple<Type *, size_t, size_t>> toIterate;
3266 if (
auto MD =
hasMetadata(&MS,
"enzyme_truetype")) {
3269 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
3272 if (!vd.isKnownPastPointer()) {
3275 if (
auto CI = dyn_cast<ConstantInt>(MS.getOperand(1)))
3278 bool writtenTo =
false;
3281 if (
auto arg = dyn_cast<Argument>(root))
3282 if (arg->hasStructRetAttr())
3285 Instruction *cur = MS.getPrevNode();
3289 if (
auto MCI = dyn_cast<ConstantInt>(MS.getOperand(2))) {
3290 if (
auto II = dyn_cast<IntrinsicInst>(cur)) {
3291 if (II->getCalledFunction()->getName() ==
3292 "llvm.enzyme.lifetime_start") {
3295 dyn_cast<ConstantInt>(II->getOperand(0))) {
3296 if (MCI->getValue().ule(CI2->getValue()))
3300 cur = cur->getPrevNode();
3305 if (II->getIntrinsicID() == Intrinsic::lifetime_start) {
3308 dyn_cast<ConstantInt>(II->getOperand(0))) {
3309 if (MCI->getValue().ule(CI2->getValue()))
3313 cur = cur->getPrevNode();
3318 if (cur->mayWriteToMemory()) {
3322 cur = cur->getPrevNode();
3325 if (cur ==
nullptr) {
3338 if (!vd.isKnownPastPointer()) {
3340 if (isa<PHINode>(MS.getOperand(0)) ||
3341 isa<SelectInst>(MS.getOperand(0))) {
3342 SmallVector<Value *, 2> todo = {MS.getOperand(0)};
3344 SmallSet<Value *, 2> seen;
3346 while (todo.size()) {
3347 Value *cur = todo.back();
3349 if (seen.count(cur))
3352 if (
auto PN = dyn_cast<PHINode>(cur)) {
3353 for (
size_t i = 0, end = PN->getNumIncomingValues(); i < end;
3355 todo.push_back(PN->getIncomingValue(i));
3359 if (
auto S = dyn_cast<SelectInst>(cur)) {
3360 todo.push_back(S->getTrueValue());
3361 todo.push_back(S->getFalseValue());
3364 if (
auto CE = dyn_cast<ConstantExpr>(cur)) {
3366 todo.push_back(CE->getOperand(0));
3370 if (
auto CI = dyn_cast<CastInst>(cur)) {
3371 todo.push_back(CI->getOperand(0));
3374 if (isa<ConstantPointerNull>(cur))
3376 if (
auto CI = dyn_cast<ConstantInt>(cur))
3389 if (!vd.isKnownPastPointer()) {
3391#if LLVM_VERSION_MAJOR < 17
3392 if (
auto CI = dyn_cast<CastInst>(MS.getOperand(0))) {
3393 if (
auto PT = dyn_cast<PointerType>(CI->getSrcTy())) {
3394 auto ET = PT->getPointerElementType();
3396 if (
auto ST = dyn_cast<StructType>(ET)) {
3397 if (ST->getNumElements()) {
3398 ET = ST->getElementType(0);
3402 if (
auto AT = dyn_cast<ArrayType>(ET)) {
3403 ET = AT->getElementType();
3408 if (ET->isFPOrFPVectorTy()) {
3412 if (ET->isPointerTy()) {
3416 if (ET->isIntOrIntVectorTy()) {
3423 if (
auto gep = dyn_cast<GetElementPtrInst>(MS.getOperand(0))) {
3424 if (
auto AT = dyn_cast<ArrayType>(gep->getSourceElementType())) {
3425 if (AT->getElementType()->isIntegerTy()) {
3432 "failed to deduce type of memset ", MS);
3437 raw_string_ostream ss(
str);
3438 ss <<
"Cannot deduce type of memset " << MS;
3446 unsigned nextStart = size;
3449 for (
size_t i = start; i < size; ++i) {
3451 dt.checkedOrIn(vd[{(int)i}],
true, Legal);
3457 if (!dt.isKnown()) {
3459 llvm::errs() <<
" vd:" << vd.str() <<
" start:" << start
3460 <<
" size: " << size <<
" dt:" << dt.str() <<
"\n";
3462 assert(dt.isKnown());
3463 toIterate.emplace_back(dt.isFloat(), start, nextStart - start);
3465 if (nextStart == size)
3473 unsigned dstalign = dstAlign.valueOrOne().value();
3474 unsigned srcalign = srcAlign.valueOrOne().value();
3479 Value *op3 =
nullptr;
3480 if (3 < MS.arg_size()) {
3484 for (
auto &&[secretty_ref, seg_start_ref, seg_size_ref] : toIterate) {
3485 auto secretty = secretty_ref;
3486 auto seg_start = seg_start_ref;
3487 auto seg_size = seg_size_ref;
3489 Value *length = new_size;
3490 if (seg_start != std::get<1>(toIterate.back())) {
3491 length = ConstantInt::get(new_size->getType(), seg_start + seg_size);
3494 length = BuilderZ.CreateSub(
3495 length, ConstantInt::get(new_size->getType(), seg_start));
3498 unsigned subdstalign = dstalign;
3500 if (dstalign != 0) {
3501 if (start % dstalign != 0) {
3505 unsigned subsrcalign = srcalign;
3507 if (srcalign != 0) {
3508 if (start % srcalign != 0) {
3514 Value *shadow_dst = gutils->
invertPointerM(MS.getOperand(0), BuilderZ);
3527 auto rule = [&](Value *op0) {
3528 if (seg_start != 0) {
3529 Value *idxs[] = {ConstantInt::get(
3530 Type::getInt32Ty(op0->getContext()), seg_start)};
3531 op0 = BuilderZ.CreateInBoundsGEP(Type::getInt8Ty(op0->getContext()),
3534 SmallVector<Value *, 4> args = {op0, op1, length};
3536 args.push_back(op3);
3537 auto cal = BuilderZ.CreateCall(MS.getCalledFunction(), args, Defs);
3538 llvm::SmallVector<unsigned int, 9> ToCopy2(
MD_ToCopy);
3539 ToCopy2.push_back(LLVMContext::MD_noalias);
3540 if (
auto m =
hasMetadata(&MS,
"enzyme_zerostack"))
3541 cal->setMetadata(
"enzyme_zerostack", m);
3544 cal->setMetadata(
"enzyme_truetype", sliced);
3545 cal->copyMetadata(MS, ToCopy2);
3546 cal->setAttributes(MS.getAttributes());
3547 cal->setCallingConv(MS.getCallingConv());
3548 cal->setTailCallKind(MS.getTailCallKind());
3562 Value *op1l = gutils->
lookupM(op1, Builder2);
3565 op3l = gutils->
lookupM(op3l, BuilderZ);
3566 length = gutils->
lookupM(length, Builder2);
3567 auto rule = [&](Value *op0) {
3568 if (seg_start != 0) {
3569 Value *idxs[] = {ConstantInt::get(
3570 Type::getInt32Ty(op0->getContext()), seg_start)};
3571 op0 = Builder2.CreateInBoundsGEP(Type::getInt8Ty(op0->getContext()),
3574 SmallVector<Value *, 4> args = {op0, op1l, length};
3576 args.push_back(op3l);
3579 if (
startsWith(funcName,
"memset_pattern") ||
3580 startsWith(funcName,
"llvm.experimental.memset"))
3581 cal = Builder2.CreateMemSet(
3582 op0, ConstantInt::get(Builder2.getInt8Ty(), 0), length, {});
3584 cal = Builder2.CreateCall(MS.getCalledFunction(), args, Defs);
3585 llvm::SmallVector<unsigned int, 9> ToCopy2(
MD_ToCopy);
3586 ToCopy2.push_back(LLVMContext::MD_noalias);
3587 cal->copyMetadata(MS, ToCopy2);
3588 if (
auto m =
hasMetadata(&MS,
"enzyme_zerostack"))
3589 cal->setMetadata(
"enzyme_zerostack", m);
3592 cal->setMetadata(
"enzyme_truetype", sliced);
3594 if (
startsWith(funcName,
"memset_pattern") ||
3595 startsWith(funcName,
"llvm.experimental.memset")) {
3596 AttributeList NewAttrs;
3598 {AttributeList::ReturnIndex, AttributeList::FunctionIndex,
3599 AttributeList::FirstArgIndex})
3600 for (
auto attr : MS.getAttributes().getAttributes(idx))
3602 NewAttrs.addAttributeAtIndex(MS.getContext(), idx, attr);
3603 cal->setAttributes(NewAttrs);
3605 cal->setAttributes(MS.getAttributes());
3606 cal->setCallingConv(MS.getCallingConv());
3616 using namespace llvm;
3618 auto srcAlign = MTI.getSourceAlign();
3619 auto dstAlign = MTI.getDestAlign();
3621 MTI.getOperand(0), MTI.getOperand(1),
3627 llvm::MaybeAlign dstAlign, llvm::CallInst &MTI,
3628 llvm::Value *orig_dst, llvm::Value *orig_src,
3629 llvm::Value *new_size, llvm::Value *isVolatile) {
3630 using namespace llvm;
3637 if (unnecessaryStores.count(&MTI)) {
3643 if (
auto ci = dyn_cast<ConstantInt>(new_size)) {
3644 if (ci->getValue() == 1) {
3652 if (isa<ConstantPointerNull>(orig_dst) ||
3659 if (
auto ci = dyn_cast<ConstantInt>(new_size)) {
3660 size = ci->getLimitedValue();
3678 std::vector<std::tuple<Type *, size_t, size_t>> toIterate;
3683 if (
auto MD =
hasMetadata(&MTI,
"enzyme_truetype")) {
3688 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
3692 for (
size_t i = 0; i < MTI.getNumOperands(); i++)
3693 if (MTI.getOperand(i) == orig_dst)
3694 if (MTI.getAttributes().hasParamAttr(i,
"enzyme_type")) {
3695 auto attr = MTI.getAttributes().getParamAttr(i,
"enzyme_type");
3698 vd |= TT.Data0().ShiftIndices(DL, 0, size, 0);
3702 bool errorIfNoType =
true;
3706 errorIfNoType =
false;
3709 if (!vd.isKnownPastPointer()) {
3711 for (
auto val : {orig_dst, orig_src}) {
3712#if LLVM_VERSION_MAJOR < 17
3713 if (
auto CI = dyn_cast<CastInst>(val)) {
3714 if (
auto PT = dyn_cast<PointerType>(CI->getSrcTy())) {
3715 auto ET = PT->getPointerElementType();
3717 if (
auto ST = dyn_cast<StructType>(ET)) {
3718 if (ST->getNumElements()) {
3719 ET = ST->getElementType(0);
3723 if (
auto AT = dyn_cast<ArrayType>(ET)) {
3724 ET = AT->getElementType();
3729 if (ET->isFPOrFPVectorTy()) {
3734 if (ET->isPointerTy()) {
3738 if (ET->isIntOrIntVectorTy()) {
3745 if (
auto gep = dyn_cast<GetElementPtrInst>(val)) {
3746 if (
auto AT = dyn_cast<ArrayType>(gep->getSourceElementType())) {
3747 if (AT->getElementType()->isIntegerTy()) {
3757 if (size == 1 && !isa<ConstantInt>(new_size)) {
3758 for (
auto ptr : {orig_dst, orig_src}) {
3760 if (vd.isKnownPastPointer()) {
3762 size_t minInt = 0xFFFFFFFF;
3763 for (
const auto &pair : vd.getMapping()) {
3764 if (pair.first.size() != 1)
3766 if (minInt < (
size_t)pair.first[0])
3768 minInt = pair.first[0];
3779 "failed to deduce type of copy ", MTI);
3783 if (errorIfNoType) {
3785 raw_string_ostream ss(
str);
3786 ss <<
"Cannot deduce type of copy " << MTI;
3799 unsigned nextStart = size;
3802 for (
size_t i = start; i < size; ++i) {
3805 auto next = vd[{(int)i}];
3806 tmp.checkedOrIn(next,
true, Legal);
3823 if ((dt.isFloat() ==
nullptr) ==
3824 (vd[{(int)i}].isFloat() ==
nullptr)) {
3839 if (!dt.isKnown()) {
3841 llvm::errs() <<
" vd:" << vd.str() <<
" start:" << start
3842 <<
" size: " << size <<
" dt:" << dt.str() <<
"\n";
3844 assert(dt.isKnown());
3845 toIterate.emplace_back(dt.isFloat(), start, nextStart - start);
3847 if (nextStart == size)
3857 unsigned dstalign = dstAlign.valueOrOne().value();
3858 unsigned srcalign = srcAlign.valueOrOne().value();
3860 bool forwardsShadow, backwardsShadow;
3863 for (
auto &&[floatTy_ref, seg_start_ref, seg_size_ref] : toIterate) {
3864 auto floatTy = floatTy_ref;
3865 auto seg_start = seg_start_ref;
3866 auto seg_size = seg_size_ref;
3868 Value *length = new_size;
3869 if (seg_start != std::get<1>(toIterate.back())) {
3870 length = ConstantInt::get(new_size->getType(), seg_start + seg_size);
3873 length = BuilderZ.CreateSub(
3874 length, ConstantInt::get(new_size->getType(), seg_start));
3876 unsigned subdstalign = dstalign;
3878 if (dstalign != 0) {
3879 if (seg_start % dstalign != 0) {
3883 unsigned subsrcalign = srcalign;
3885 if (srcalign != 0) {
3886 if (seg_start % srcalign != 0) {
3898 auto rev_rule = [&](Value *shadow_dst, Value *shadow_src) {
3899 if (shadow_dst ==
nullptr)
3901 if (shadow_src ==
nullptr)
3904 gutils, Mode, floatTy, ID, subdstalign, subsrcalign,
3909 length, isVolatile, &MTI,
3910 forwardsShadow,
false,
3914 auto fwd_rule = [&](Value *ddst, Value *dsrc) {
3915 if (ddst ==
nullptr)
3917 if (dsrc ==
nullptr)
3921 dalign = MaybeAlign(subdstalign);
3924 salign = MaybeAlign(subsrcalign);
3925 if (ddst->getType()->isIntegerTy())
3927 BuilderZ.CreateIntToPtr(ddst,
getInt8PtrTy(ddst->getContext()));
3928 if (seg_start != 0) {
3929 ddst = BuilderZ.CreateConstInBoundsGEP1_64(
3930 Type::getInt8Ty(ddst->getContext()), ddst, seg_start);
3934 call = BuilderZ.CreateMemSet(
3935 ddst, ConstantInt::get(Type::getInt8Ty(ddst->getContext()), 0),
3936 length, dalign, cast<ConstantInt>(isVolatile)->isOne());
3938 auto toPtr = [&](Value *ptr) {
3939 if (ptr->getType()->isIntegerTy())
3941 BuilderZ.CreateIntToPtr(ptr,
getInt8PtrTy(ptr->getContext()));
3942 if (seg_start != 0) {
3943 ptr = BuilderZ.CreateConstInBoundsGEP1_64(
3944 Type::getInt8Ty(ptr->getContext()), ptr, seg_start);
3953 Value *copyLength = length;
3954 Value *zeroLength =
nullptr;
3957 if (src->getType() != dsrc->getType())
3958 src = BuilderZ.CreatePointerBitCastOrAddrSpaceCast(
3959 src, dsrc->getType());
3960 Value *inactive = BuilderZ.CreateICmpEQ(dsrc, src);
3961 Value *zero = ConstantInt::get(length->getType(), 0);
3962 copyLength = BuilderZ.CreateSelect(inactive, zero, length);
3963 zeroLength = BuilderZ.CreateSelect(inactive, length, zero);
3966 if (ID == Intrinsic::memmove) {
3968 BuilderZ.CreateMemMove(ddst, dalign, dsrc, salign, copyLength);
3971 BuilderZ.CreateMemCpy(ddst, dalign, dsrc, salign, copyLength);
3973 call->setAttributes(MTI.getAttributes());
3976 BuilderZ.CreateMemSet(
3977 ddst, ConstantInt::get(Type::getInt8Ty(ddst->getContext()), 0),
3978 zeroLength, dalign);
3981 call->setMetadata(LLVMContext::MD_alias_scope,
3982 MTI.getMetadata(LLVMContext::MD_alias_scope));
3983 call->setMetadata(LLVMContext::MD_noalias,
3984 MTI.getMetadata(LLVMContext::MD_noalias));
3985 call->setMetadata(LLVMContext::MD_tbaa,
3986 MTI.getMetadata(LLVMContext::MD_tbaa));
3987 call->setMetadata(LLVMContext::MD_tbaa_struct,
3988 MTI.getMetadata(LLVMContext::MD_tbaa_struct));
3989 call->setMetadata(LLVMContext::MD_invariant_group,
3990 MTI.getMetadata(LLVMContext::MD_invariant_group));
3991 if (
auto m =
hasMetadata(&MTI,
"enzyme_truetype"))
3993 call->setMetadata(
"enzyme_truetype", sliced);
3994 call->setTailCallKind(MTI.getTailCallKind());
4008 using namespace llvm;
4015 bool emitReverse =
true;
4017 if (
auto prev = dyn_cast_or_null<CallBase>(FI.getPrevNode())) {
4018 if (
auto F = prev->getCalledFunction())
4019 if (F->getName() ==
"julia.safepoint")
4020 emitReverse =
false;
4022 if (
auto prev = dyn_cast_or_null<CallBase>(FI.getNextNode())) {
4023 if (
auto F = prev->getCalledFunction())
4024 if (F->getName() ==
"julia.safepoint")
4025 emitReverse =
false;
4029 IRBuilder<> Builder2(&FI);
4031 auto order = FI.getOrdering();
4033 case AtomicOrdering::Acquire:
4034 order = AtomicOrdering::Release;
4036 case AtomicOrdering::Release:
4037 order = AtomicOrdering::Acquire;
4042 Builder2.CreateFence(order, FI.getSyncScopeID());
4050 using namespace llvm;
4052 if (II.getIntrinsicID() == Intrinsic::stacksave) {
4056 if (II.getIntrinsicID() == Intrinsic::stackrestore ||
4057 II.getIntrinsicID() == Intrinsic::lifetime_end ||
4058 II.getCalledFunction()->getName() ==
"llvm.enzyme.lifetime_end") {
4062#if LLVM_VERSION_MAJOR >= 20
4063 if (II.getIntrinsicID() == Intrinsic::experimental_memset_pattern) {
4081 SmallVector<Value *, 2> orig_ops(II.getNumOperands());
4083 for (
unsigned i = 0; i < II.getNumOperands(); ++i) {
4084 orig_ops[i] = II.getOperand(i);
4092 CallInst *
const newCall =
4094 IRBuilder<> BuilderZ(newCall);
4095 BuilderZ.setFastMathFlags(
getFast());
4106 llvm::SmallVectorImpl<llvm::Value *> &orig_ops) {
4107 using namespace llvm;
4109 Module *M = I.getParent()->getParent()->getParent();
4112#if LLVM_VERSION_MAJOR < 20
4113 case Intrinsic::nvvm_ldg_global_i:
4114 case Intrinsic::nvvm_ldg_global_p:
4115 case Intrinsic::nvvm_ldg_global_f:
4117 case Intrinsic::nvvm_ldu_global_i:
4118 case Intrinsic::nvvm_ldu_global_p:
4119 case Intrinsic::nvvm_ldu_global_f: {
4120 auto CI = cast<ConstantInt>(I.getOperand(1));
4129 if (ID == Intrinsic::masked_store) {
4130 auto align0 = cast<ConstantInt>(I.getOperand(2))->getZExtValue();
4131 auto align = MaybeAlign(align0);
4133 I.getOperand(0), align,
4134 false, llvm::AtomicOrdering::NotAtomic,
4135 SyncScope::SingleThread,
4139 if (ID == Intrinsic::masked_load) {
4140 auto align0 = cast<ConstantInt>(I.getOperand(1))->getZExtValue();
4141 auto align = MaybeAlign(align0);
4142 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
4143 bool constantval =
parseTBAA(I, DL,
nullptr)[{-1}].isIntegral();
4150 auto mod = I.getParent()->getParent()->getParent();
4151 auto called = cast<CallInst>(&I)->getCalledFunction();
4154#include "IntrinsicDerivatives.inc"
4162#if LLVM_VERSION_MAJOR <= 20
4163 case Intrinsic::nvvm_barrier0:
4165 case Intrinsic::nvvm_barrier_cta_sync_aligned_all:
4166 case Intrinsic::nvvm_barrier_cta_sync_aligned_count:
4168#if LLVM_VERSION_MAJOR < 22
4169 case Intrinsic::nvvm_barrier0_popc:
4170 case Intrinsic::nvvm_barrier0_and:
4171 case Intrinsic::nvvm_barrier0_or:
4173 case Intrinsic::nvvm_barrier_cta_red_and_aligned_all:
4174 case Intrinsic::nvvm_barrier_cta_red_and_aligned_count:
4175 case Intrinsic::nvvm_barrier_cta_red_or_aligned_all:
4176 case Intrinsic::nvvm_barrier_cta_red_or_aligned_count:
4177 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_all:
4178 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_count:
4180 case Intrinsic::nvvm_membar_cta:
4181 case Intrinsic::nvvm_membar_gl:
4182 case Intrinsic::nvvm_membar_sys:
4183 case Intrinsic::amdgcn_s_barrier:
4188 if (ID == Intrinsic::umax || ID == Intrinsic::smax ||
4189 ID == Intrinsic::abs || ID == Intrinsic::sadd_with_overflow ||
4190 ID == Intrinsic::uadd_with_overflow ||
4191 ID == Intrinsic::smul_with_overflow ||
4192 ID == Intrinsic::umul_with_overflow ||
4193 ID == Intrinsic::ssub_with_overflow ||
4194 ID == Intrinsic::usub_with_overflow)
4197 "failed to deduce type of intrinsic ", I);
4201 llvm::raw_string_ostream ss(s);
4202 ss << *gutils->
oldFunc <<
"\n";
4203 ss << *gutils->
newFunc <<
"\n";
4204 ss <<
"cannot handle (augmented) unknown intrinsic\n" << I;
4205 IRBuilder<> BuilderZ(&I);
4216 IRBuilder<> Builder2(&I);
4219 Value *vdiff =
nullptr;
4221 vdiff =
diffe(&I, Builder2);
4228#if LLVM_VERSION_MAJOR < 22
4229 case Intrinsic::nvvm_barrier0_popc:
4230 case Intrinsic::nvvm_barrier0_and:
4231 case Intrinsic::nvvm_barrier0_or:
4233 case Intrinsic::nvvm_barrier_cta_red_and_aligned_all:
4234 case Intrinsic::nvvm_barrier_cta_red_and_aligned_count:
4235 case Intrinsic::nvvm_barrier_cta_red_or_aligned_all:
4236 case Intrinsic::nvvm_barrier_cta_red_or_aligned_count:
4237 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_all:
4238 case Intrinsic::nvvm_barrier_cta_red_popc_aligned_count:
4241 SmallVector<Value *, 1> args = {};
4242#if LLVM_VERSION_MAJOR > 20
4243 args.push_back(ConstantInt::get(Type::getInt32Ty(M->getContext()), 0));
4244 auto cal = cast<CallInst>(Builder2.CreateCall(
4246 M, Intrinsic::nvvm_barrier_cta_sync_aligned_all),
4249 M, Intrinsic::nvvm_barrier_cta_sync_aligned_all)
4250 ->getCallingConv());
4252 auto cal = cast<CallInst>(Builder2.CreateCall(
4255 ->getCallingConv());
4261#if LLVM_VERSION_MAJOR <= 20
4262 case Intrinsic::nvvm_barrier0:
4264 case Intrinsic::nvvm_barrier_cta_sync_aligned_all:
4265 case Intrinsic::nvvm_barrier_cta_sync_aligned_count:
4267 case Intrinsic::amdgcn_s_barrier:
4268 case Intrinsic::nvvm_membar_cta:
4269 case Intrinsic::nvvm_membar_gl:
4270 case Intrinsic::nvvm_membar_sys: {
4271 SmallVector<Value *, 2> args = {};
4272#if LLVM_VERSION_MAJOR > 20
4273 if (ID == Intrinsic::nvvm_barrier_cta_sync_aligned_all ||
4274 ID == Intrinsic::nvvm_barrier_cta_sync_aligned_count) {
4275 auto *CB = cast<CallBase>(&I);
4276 for (Use &arg : CB->args())
4281 auto cal = cast<CallInst>(
4288 case Intrinsic::lifetime_start: {
4291 SmallVector<Value *, 2> args = {
4294 Type *tys[] = {args[1]->getType()};
4295 auto cal = Builder2.CreateCall(
4297 cal->setCallingConv(
4299 ->getCallingConv());
4303 case Intrinsic::vector_reduce_fmax: {
4306 auto VT = cast<VectorType>(orig_ops[0]->getType());
4308 assert(!VT->getElementCount().isScalable());
4309 size_t numElems = VT->getElementCount().getKnownMinValue();
4310 SmallVector<Value *> elems;
4311 SmallVector<Value *> cmps;
4313 for (
size_t i = 0; i < numElems; ++i)
4314 elems.push_back(Builder2.CreateExtractElement(prev, (uint64_t)i));
4316 Value *curmax = elems[0];
4317 for (
size_t i = 0; i < numElems - 1; ++i) {
4318 cmps.push_back(Builder2.CreateFCmpOLT(curmax, elems[i + 1]));
4319 if (i + 2 != numElems)
4320 curmax =
CreateSelect(Builder2, cmps[i], elems[i + 1], curmax);
4323 auto rule = [&](Value *vdiff) {
4324 auto nv = Constant::getNullValue(orig_ops[0]->getType());
4325 Value *res = Builder2.CreateInsertElement(nv, vdiff, (uint64_t)0);
4327 for (
size_t i = 0; i < numElems - 1; ++i) {
4328 auto rhs_v = Builder2.CreateInsertElement(nv, vdiff, i + 1);
4335 addToDiffe(orig_ops[0], dif0, Builder2, I.getType());
4342 if (ID == Intrinsic::umax || ID == Intrinsic::smax ||
4343 ID == Intrinsic::abs || ID == Intrinsic::sadd_with_overflow ||
4344 ID == Intrinsic::uadd_with_overflow ||
4345 ID == Intrinsic::smul_with_overflow ||
4346 ID == Intrinsic::umul_with_overflow ||
4347 ID == Intrinsic::ssub_with_overflow ||
4348 ID == Intrinsic::usub_with_overflow)
4351 "failed to deduce type of intrinsic ", I);
4355 llvm::raw_string_ostream ss(s);
4356 ss << *gutils->
oldFunc <<
"\n";
4357 ss << *gutils->
newFunc <<
"\n";
4358 if (Intrinsic::isOverloaded(ID))
4359 ss <<
"cannot handle (reverse) unknown intrinsic\n"
4360 << Intrinsic::getName(ID, ArrayRef<Type *>(),
4361 gutils->
oldFunc->getParent(),
nullptr)
4365 ss <<
"cannot handle (reverse) unknown intrinsic\n"
4366 << Intrinsic::getName(ID) <<
"\n"
4377 IRBuilder<> Builder2(&I);
4382 case Intrinsic::vector_reduce_fmax: {
4386 auto VT = cast<VectorType>(orig_ops[0]->getType());
4388 assert(!VT->getElementCount().isScalable());
4389 size_t numElems = VT->getElementCount().getKnownMinValue();
4390 SmallVector<Value *> elems;
4391 SmallVector<Value *> cmps;
4393 for (
size_t i = 0; i < numElems; ++i)
4394 elems.push_back(Builder2.CreateExtractElement(prev, (uint64_t)i));
4396 Value *curmax = elems[0];
4397 for (
size_t i = 0; i < numElems - 1; ++i) {
4398 cmps.push_back(Builder2.CreateFCmpOLT(curmax, elems[i + 1]));
4399 if (i + 2 != numElems)
4400 curmax =
CreateSelect(Builder2, cmps[i], elems[i + 1], curmax);
4403 auto rule = [&](Value *vdiff) {
4404 Value *res = Builder2.CreateExtractElement(vdiff, (uint64_t)0);
4406 for (
size_t i = 0; i < numElems - 1; ++i) {
4407 auto rhs_v = Builder2.CreateExtractElement(vdiff, i + 1);
4412 auto vdiff =
diffe(orig_ops[0], Builder2);
4426 if (ID == Intrinsic::umax || ID == Intrinsic::smax ||
4427 ID == Intrinsic::abs || ID == Intrinsic::sadd_with_overflow ||
4428 ID == Intrinsic::uadd_with_overflow ||
4429 ID == Intrinsic::smul_with_overflow ||
4430 ID == Intrinsic::umul_with_overflow ||
4431 ID == Intrinsic::ssub_with_overflow ||
4432 ID == Intrinsic::usub_with_overflow)
4435 "failed to deduce type of intrinsic ", I);
4439 llvm::raw_string_ostream ss(s);
4440 if (Intrinsic::isOverloaded(ID))
4441 ss <<
"cannot handle (forward) unknown intrinsic\n"
4442 << Intrinsic::getName(ID, ArrayRef<Type *>(),
4443 gutils->
oldFunc->getParent(),
nullptr)
4447 ss <<
"cannot handle (forward) unknown intrinsic\n"
4448 << Intrinsic::getName(ID) <<
"\n"
4461#include "BlasAttributor.inc"
4462#include "BlasDerivatives.inc"
4465 using namespace llvm;
4467 Function *kmpc = call.getCalledFunction();
4469 if (overwritten_args_map.find(&call) == overwritten_args_map.end()) {
4470 llvm::errs() <<
" call: " << call <<
"\n";
4471 for (
auto &pair : overwritten_args_map) {
4472 llvm::errs() <<
" + " << *pair.first <<
"\n";
4476 auto found_ow = overwritten_args_map.find(&call);
4477 assert(found_ow != overwritten_args_map.end());
4478 const bool subsequent_calls_may_write = found_ow->second.first;
4479 const std::vector<bool> &overwritten_args = found_ow->second.second;
4482 BuilderZ.setFastMathFlags(
getFast());
4484 Function *task = dyn_cast<Function>(call.getArgOperand(2));
4485 if (task ==
nullptr && isa<ConstantExpr>(call.getArgOperand(2))) {
4486 task = dyn_cast<Function>(
4487 cast<ConstantExpr>(call.getArgOperand(2))->getOperand(0));
4489 if (task ==
nullptr) {
4490 llvm::errs() <<
"could not derive underlying task from omp call: " << call
4492 llvm_unreachable(
"could not derive underlying task from omp call");
4494 if (task->empty()) {
4496 <<
"could not derive underlying task contents from omp call: " << call
4499 "could not derive underlying task contents from omp call");
4505 bool foreignFunction = called ==
nullptr;
4507 SmallVector<Value *, 8> args = {0, 0, 0};
4508 SmallVector<Value *, 8> pre_args = {0, 0, 0};
4511 SmallVector<Instruction *, 4> postCreate;
4512 SmallVector<Instruction *, 4> userReplace;
4514 SmallVector<Value *, 4> OutTypes;
4515 SmallVector<Type *, 4> OutFPTypes;
4517 std::vector<bool> nowrite_shadows = {
false,
false};
4519 for (
unsigned i = 3; i < call.arg_size(); ++i) {
4523 pre_args.push_back(argi);
4524 nowrite_shadows.push_back(
false);
4527 IRBuilder<> Builder2(&call);
4529 args.push_back(
lookup(argi, Builder2));
4532 auto argTy = gutils->
getDiffeType(call.getArgOperand(i), foreignFunction);
4533 argsInverted.push_back(argTy);
4539 auto argType = argi->getType();
4543 IRBuilder<> Builder2(&call);
4551 if (
auto arg = dyn_cast<Argument>(baseOp)) {
4554 nowrite_shadows.back() =
true;
4563 bool forwardsShadow =
true;
4567 forwardsShadow = found->second.primalInitialize;
4570 if (!forwardsShadow) {
4571 nowrite_shadows.back() =
true;
4584 assert(TR.
query(call.getArgOperand(i))[{-1}].isFloat());
4585 OutTypes.push_back(call.getArgOperand(i));
4586 OutFPTypes.push_back(argType);
4594 Value *tape =
nullptr;
4595 CallInst *augmentcall =
nullptr;
4602 std::map<Value *, std::set<int64_t>> intseen;
4609 for (
auto &arg : called->args()) {
4612 std::pair<Argument *, TypeTree>(&arg, IntPtr));
4614 std::pair<Argument *, std::set<int64_t>>(&arg, {0}));
4616 nextTypeInfo.
Arguments.insert(std::pair<Argument *, TypeTree>(
4617 &arg, TR.
query(call.getArgOperand(argnum - 2 + 3))));
4619 std::pair<Argument *, std::set<int64_t>>(
4637 assert(augmentedReturn);
4638 if (augmentedReturn) {
4641 subdata = fd->second;
4653 false, nextTypeInfo,
4654 subsequent_calls_may_write, overwritten_args, nowrite_shadows,
4659 assert(augmentedReturn);
4660 auto subaugmentations =
4661 (std::map<const llvm::CallInst *, AugmentedReturn *>
4668 auto newcalled = subdata->
fn;
4672 ValueToValueMapTy VMap;
4673 newcalled = CloneFunction(newcalled, VMap);
4674 auto tapeArg = newcalled->arg_end();
4676 Type *tapeElemType = subdata->
tapeType;
4677 SmallVector<std::pair<ssize_t, Value *>, 4> geps;
4678 SmallPtrSet<Instruction *, 4> gepsToErase;
4679 for (
auto a : tapeArg->users()) {
4680 if (
auto gep = dyn_cast<GetElementPtrInst>(a)) {
4681 auto idx = gep->idx_begin();
4683 auto cidx = cast<ConstantInt>(idx->get());
4684 assert(gep->getNumIndices() == 2);
4685 SmallPtrSet<StoreInst *, 1> storesToErase;
4686 for (
auto st : gep->users()) {
4687 auto SI = cast<StoreInst>(st);
4688 Value *op = SI->getValueOperand();
4689 storesToErase.insert(SI);
4690 geps.emplace_back(cidx->getLimitedValue(), op);
4692 for (
auto SI : storesToErase)
4693 SI->eraseFromParent();
4694 gepsToErase.insert(gep);
4695 }
else if (
auto SI = dyn_cast<StoreInst>(a)) {
4696 Value *op = SI->getValueOperand();
4697 gepsToErase.insert(SI);
4698 geps.emplace_back(-1, op);
4700 llvm::errs() <<
"unknown tape user: " << a <<
"\n";
4701 assert(0 &&
"unknown tape user");
4702 llvm_unreachable(
"unknown tape user");
4705 for (
auto gep : gepsToErase)
4706 gep->eraseFromParent();
4707 IRBuilder<> ph(&*newcalled->getEntryBlock().begin());
4708 tape = UndefValue::get(tapeElemType);
4709 ValueToValueMapTy available;
4710 auto subarg = newcalled->arg_begin();
4713 for (
size_t i = 3; i < pre_args.size(); ++i) {
4714 available[&*subarg] = pre_args[i];
4717 for (
auto pair : geps) {
4718 Value *op = pair.second;
4720 Value *replacement = gutils->
unwrapM(op, BuilderZ, available,
4725 : BuilderZ.CreateInsertValue(tape, replacement, pair.first);
4726 if (
auto ci = dyn_cast<CastInst>(alloc)) {
4727 alloc = ci->getOperand(0);
4729 if (
auto uload = dyn_cast<Instruction>(replacement)) {
4731 if (
auto ci = dyn_cast<CastInst>(replacement)) {
4732 if (
auto ucast = dyn_cast<Instruction>(ci->getOperand(0)))
4736 if (
auto ci = dyn_cast<CallInst>(alloc)) {
4737 if (
auto F = ci->getCalledFunction()) {
4739 if (F->getName() ==
"malloc") {
4741 ->tapeIndiciesToFree.emplace(pair.first);
4742 Value *toload = tapeArg;
4743 if (pair.first != -1) {
4746 Type::getInt64Ty(tapeArg->getContext()), 0),
4748 Type::getInt32Ty(tapeArg->getContext()),
4750 toload = ph.CreateInBoundsGEP(tapeElemType, toload, Idxs);
4752 op->replaceAllUsesWith(ph.CreateLoad(op->getType(), toload));
4753 cast<Instruction>(op)->eraseFromParent();
4755 ci->eraseFromParent();
4761 ConstantInt::get(Type::getInt64Ty(tapeArg->getContext()), 0),
4762 ConstantInt::get(Type::getInt32Ty(tapeArg->getContext()),
4764 op->replaceAllUsesWith(ph.CreateLoad(
4768 : ph.CreateInBoundsGEP(tapeElemType, tapeArg, Idxs)));
4769 cast<Instruction>(op)->eraseFromParent();
4774 BuilderZ.CreateStore(tape, alloc);
4775 pre_args.push_back(alloc);
4781 auto numargs = ConstantInt::get(Type::getInt32Ty(call.getContext()),
4782 pre_args.size() - 3);
4784 pre_args[1] = numargs;
4785 pre_args[2] = BuilderZ.CreatePointerCast(
4786 newcalled, kmpc->getFunctionType()->getParamType(2));
4788 BuilderZ.CreateCall(kmpc->getFunctionType(), kmpc, pre_args);
4789 augmentcall->setCallingConv(call.getCallingConv());
4790 augmentcall->setDebugLoc(
4792 BuilderZ.SetInsertPoint(
4796 assert(0 &&
"unhandled unknown outline");
4801 Intrinsic::ID ID = Intrinsic::not_intrinsic;
4803 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
4804 llvm::errs() << *gutils->
oldFunc <<
"\n";
4805 llvm::errs() << *gutils->
newFunc <<
"\n";
4806 llvm::errs() << *called <<
"\n";
4807 llvm_unreachable(
"no subdata");
4813 assert(found == subdata->
returns.end());
4817 assert(found == subdata->
returns.end());
4822 IRBuilder<> Builder2(&call);
4826 BuilderZ.SetInsertPoint(
4831 Function *newcalled =
nullptr;
4836 if (tape ==
nullptr) {
4837#if LLVM_VERSION_MAJOR >= 18
4838 auto It = BuilderZ.GetInsertPoint();
4839 It.setHeadBit(
true);
4840 BuilderZ.SetInsertPoint(It);
4842 tape = BuilderZ.CreatePHI(subdata->
tapeType, 0,
"tapeArg");
4847 tape =
lookup(tape, Builder2);
4849 .CreateAlloca(tape->getType());
4850 Builder2.CreateStore(tape, alloc);
4851 args.push_back(alloc);
4855 for (
size_t i = 0; i < argsInverted.size(); i++) {
4867 .todiff = cast<Function>(called),
4868 .retType = subretType,
4869 .constant_args = argsInverted,
4870 .subsequent_calls_may_write = subsequent_calls_may_write,
4871 .overwritten_args = overwritten_args,
4872 .returnUsed =
false,
4873 .shadowReturnUsed =
false,
4878 .additionalType = tape ?
getUnqual(tape->getType()) :
nullptr,
4879 .forceAnonymousTape =
false,
4880 .typeInfo = nextTypeInfo,
4888 auto tapeArg = newcalled->arg_end();
4890 LoadInst *tape =
nullptr;
4891 for (
auto u : tapeArg->users()) {
4893 if (!isa<LoadInst>(u)) {
4894 llvm::errs() <<
" newcalled: " << *newcalled <<
"\n";
4895 llvm::errs() <<
" u: " << *u <<
"\n";
4897 tape = cast<LoadInst>(u);
4900 SmallVector<Value *, 4> extracts;
4902 assert(subdata->
tapeIndices.begin()->second == -1);
4903 extracts.push_back(tape);
4905 for (
auto a : tape->users()) {
4906 extracts.push_back(a);
4909 SmallVector<LoadInst *, 4> geps;
4910 for (
auto E : extracts) {
4911 AllocaInst *AI =
nullptr;
4912 for (
auto U : E->users()) {
4913 if (
auto SI = dyn_cast<StoreInst>(U)) {
4914 assert(SI->getValueOperand() == E);
4915 AI = cast<AllocaInst>(SI->getPointerOperand());
4919 for (
auto U : AI->users()) {
4920 if (
auto LI = dyn_cast<LoadInst>(U)) {
4926 for (
auto LI : geps) {
4927 CallInst *freeCall =
nullptr;
4928 for (
auto LU : LI->users()) {
4929 if (
auto CI = dyn_cast<CallInst>(LU)) {
4930 if (
auto F = CI->getCalledFunction()) {
4931 if (F->getName() ==
"free") {
4936 }
else if (
auto BC = dyn_cast<CastInst>(LU)) {
4937 for (
auto CU : BC->users()) {
4938 if (
auto CI = dyn_cast<CallInst>(CU)) {
4939 if (
auto F = CI->getCalledFunction()) {
4940 if (F->getName() ==
"free") {
4952 freeCall->eraseFromParent();
4957 Value *OutAlloc =
nullptr;
4958 auto ST = StructType::get(newcalled->getContext(), OutFPTypes);
4959 if (OutTypes.size()) {
4961 args.push_back(OutAlloc);
4963 SmallVector<Type *, 3> MetaTypes;
4965 cast<Function>(newcalled)->getFunctionType()->params()) {
4966 MetaTypes.push_back(P);
4969 auto FT = FunctionType::get(Type::getVoidTy(newcalled->getContext()),
4972 Function::Create(FT, GlobalVariable::InternalLinkage,
4973 cast<Function>(newcalled)->getName() +
"#out",
4974 *task->getParent());
4976 BasicBlock::Create(newcalled->getContext(),
"entry", F);
4977 IRBuilder<> B(entry);
4978 SmallVector<Value *, 2> SubArgs;
4979 for (
auto &arg : F->args())
4980 SubArgs.push_back(&arg);
4981 Value *cacheArg = SubArgs.back();
4983 Value *outdiff = B.CreateCall(newcalled, SubArgs);
4984 for (
size_t ee = 0; ee < OutTypes.size(); ee++) {
4985 Value *dif = B.CreateExtractValue(outdiff, ee);
4987 ConstantInt::get(Type::getInt64Ty(ST->getContext()), 0),
4988 ConstantInt::get(Type::getInt32Ty(ST->getContext()), ee)};
4989 Value *ptr = B.CreateInBoundsGEP(ST, cacheArg, Idxs);
4991 if (dif->getType()->isIntOrIntVectorTy()) {
4993 ptr = B.CreateBitCast(
4997 cast<PointerType>(ptr->getType())->getAddressSpace()));
4998 dif = B.CreateBitCast(dif,
IntToFloatTy(dif->getType()));
5002 AtomicRMWInst::BinOp op = AtomicRMWInst::FAdd;
5003 if (
auto vt = dyn_cast<VectorType>(dif->getType())) {
5004 assert(!vt->getElementCount().isScalable());
5005 size_t numElems = vt->getElementCount().getKnownMinValue();
5006 for (
size_t i = 0; i < numElems; ++i) {
5007 auto vdif = B.CreateExtractElement(dif, i);
5009 ConstantInt::get(Type::getInt64Ty(vt->getContext()), 0),
5010 ConstantInt::get(Type::getInt32Ty(vt->getContext()), i)};
5011 auto vptr = B.CreateInBoundsGEP(vt, ptr, Idxs);
5012 B.CreateAtomicRMW(op, vptr, vdif, align,
5013 AtomicOrdering::Monotonic, SyncScope::System);
5016 B.CreateAtomicRMW(op, ptr, dif, align, AtomicOrdering::Monotonic,
5024 auto numargs = ConstantInt::get(Type::getInt32Ty(call.getContext()),
5029 args[2] = Builder2.CreatePointerCast(
5030 newcalled, kmpc->getFunctionType()->getParamType(2));
5033 Builder2.CreateCall(kmpc->getFunctionType(), kmpc, args);
5034 diffes->setCallingConv(call.getCallingConv());
5037 for (
size_t i = 0; i < OutTypes.size(); i++) {
5040 if (OutTypes[i]->getType()->isSized())
5041 size = (gutils->
newFunc->getParent()
5043 .getTypeSizeInBits(OutTypes[i]->getType()) +
5047 ConstantInt::get(Type::getInt64Ty(call.getContext()), 0),
5048 ConstantInt::get(Type::getInt32Ty(call.getContext()), i)};
5050 ->addToDiffe(OutTypes[i],
5051 Builder2.CreateLoad(
5053 Builder2.CreateInBoundsGEP(ST, OutAlloc, Idxs)),
5061 : Builder2.CreateExtractValue(tape, idx));
5065 assert(0 &&
"openmp indirect unhandled");
5071 llvm::CallInst &call, llvm::Value *origArg, llvm::Value *dsto,
5072 llvm::Value *srco, llvm::Value *len_arg, llvm::IRBuilder<> &Builder2,
5073 llvm::ArrayRef<llvm::OperandBundleDef> ReverseDefs) {
5074 using namespace llvm;
5077 if (
auto ci = dyn_cast<ConstantInt>(len_arg)) {
5078 size = ci->getLimitedValue();
5080 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
5082 if (!vd.isKnownPastPointer()) {
5083#if LLVM_VERSION_MAJOR < 17
5085 if (isa<CastInst>(origArg) &&
5086 cast<CastInst>(origArg)->getSrcTy()->isPointerTy() &&
5087 cast<CastInst>(origArg)
5089 ->getPointerElementType()
5090 ->isFPOrFPVectorTy()) {
5093 ->getPointerElementType()
5101 if (
auto LI = dyn_cast<LoadInst>(origArg)) {
5102 auto *PT = dyn_cast<PointerType>(LI->getType());
5103 if (PT && PT->getPointerElementType()->isFPOrFPVectorTy()) {
5105 ConcreteType(PT->getPointerElementType()->getScalarType()))
5113 if (origArg->getType()->isPointerTy() &&
5114 origArg->getType()->getPointerElementType()->isFPOrFPVectorTy()) {
5116 ->getPointerElementType()
5130 if (
auto AI = dyn_cast<AllocaInst>(origArg)) {
5131 if (AI->getAllocatedType()->isFPOrFPVectorTy()) {
5137 if (
auto GEP = dyn_cast<GetElementPtrInst>(origArg)) {
5138 if (GEP->getSourceElementType()->isFPOrFPVectorTy()) {
5140 ConcreteType(GEP->getSourceElementType()->getScalarType()))
5146 EmitFailure(
"CannotDeduceType", call.getDebugLoc(), &call,
5147 "failed to deduce type of copy ", call);
5152 unsigned nextStart = size;
5155 for (
size_t i = start; i < size; ++i) {
5157 dt.checkedOrIn(vd[{(int)i}],
true, Legal);
5163 if (!dt.isKnown()) {
5165 llvm::errs() <<
" vd:" << vd.str() <<
" start:" << start
5166 <<
" size: " << size <<
" dt:" << dt.str() <<
"\n";
5168 assert(dt.isKnown());
5170 Value *length = len_arg;
5171 if (nextStart != size) {
5172 length = ConstantInt::get(len_arg->getType(), nextStart);
5175 length = Builder2.CreateSub(
5176 length, ConstantInt::get(len_arg->getType(), start));
5178 if (
auto secretty = dt.isFloat()) {
5179 auto offset = start;
5180 if (dsto->getType()->isIntegerTy())
5182 Builder2.CreateIntToPtr(dsto,
getInt8PtrTy(dsto->getContext()));
5184 cast<PointerType>(dsto->getType())->getAddressSpace();
5187 dsto = Builder2.CreateConstInBoundsGEP1_64(
5188 Type::getInt8Ty(dsto->getContext()), dsto, offset);
5190 if (srco->getType()->isIntegerTy())
5192 Builder2.CreateIntToPtr(srco,
getInt8PtrTy(dsto->getContext()));
5194 cast<PointerType>(srco->getType())->getAddressSpace();
5198 srco = Builder2.CreateConstInBoundsGEP1_64(
5199 Type::getInt8Ty(srco->getContext()), srco, offset);
5202 Builder2.CreatePointerCast(dsto, secretpt),
5203 Builder2.CreatePointerCast(srco, secretpt),
5204 Builder2.CreateUDiv(
5207 ConstantInt::get(length->getType(),
5208 Builder2.GetInsertBlock()
5212 .getTypeAllocSizeInBits(secretty) /
5216 *Builder2.GetInsertBlock()->getParent()->getParent(), secretty,
5217 1, 1, dstaddr, srcaddr,
5218 cast<IntegerType>(length->getType())->getBitWidth(),
5221 Builder2.CreateCall(dmemcpy, args, ReverseDefs);
5224 if (nextStart == size)
5231 llvm::Function *called,
5232 bool subsequent_calls_may_write,
5233 const std::vector<bool> &overwritten_args,
5234 bool shadowReturnUsed,
5236 using namespace llvm;
5239 BuilderZ.setFastMathFlags(
getFast());
5242 Module &M = *call.getParent()->getParent()->getParent();
5244 bool foreignFunction = called ==
nullptr;
5255 assert(augmentedReturn);
5256 if (augmentedReturn) {
5259 subdata = fd->second;
5267 IRBuilder<> Builder2(&call);
5270 SmallVector<Value *, 8> args;
5271 std::vector<DIFFE_TYPE> argsInverted;
5272 std::map<int, Type *> gradByVal;
5273 std::map<int, std::vector<Attribute>> structAttrs;
5275 for (
unsigned i = 0; i < call.arg_size(); ++i) {
5277 if (call.paramHasAttr(i, Attribute::StructRet)) {
5278 structAttrs[args.size()].push_back(Attribute::get(
5279 call.getContext(),
"enzyme_sret",
5281 .getValueAsType())));
5283 for (
auto attr : {
"enzymejl_returnRoots",
"enzymejl_parmtype",
5284 "enzymejl_parmtype_ref",
"enzyme_type",
5285 "enzymejl_sret_union_bytes",
"enzymejl_rooted_typ"})
5286 if (call.getAttributes().hasParamAttr(i, attr)) {
5287 structAttrs[args.size()].push_back(call.getParamAttr(i, attr));
5290 if (call.getAttributes().hasParamAttr(i, ty)) {
5291 auto attr = call.getAttributes().getParamAttr(i, ty);
5292 structAttrs[args.size()].push_back(attr);
5297 if (call.isByValArgument(i)) {
5298 gradByVal[args.size()] = call.getParamByValType(i);
5301 bool writeOnlyNoCapture =
true;
5302 bool readOnly =
true;
5304 writeOnlyNoCapture =
false;
5307 writeOnlyNoCapture =
false;
5314 writeOnlyNoCapture =
false;
5317 gutils->
getDiffeType(call.getArgOperand(i), foreignFunction);
5321 (writeOnlyNoCapture ||
5324 (writeOnlyNoCapture && readOnly);
5329 argsInverted.push_back(argTy);
5330 args.push_back(argi);
5338 if (call.getAttributes().hasParamAttr(i, ty)) {
5339 auto attr = call.getAttributes().getParamAttr(i, ty);
5340 structAttrs[args.size()].push_back(attr);
5343 for (
auto attr : {
"enzymejl_returnRoots",
"enzymejl_parmtype",
5344 "enzymejl_parmtype_ref",
"enzyme_type",
5345 "enzymejl_sret_union_bytes",
"enzymejl_rooted_typ"})
5346 if (call.getAttributes().hasParamAttr(i, attr)) {
5348 structAttrs[args.size()].push_back(call.getParamAttr(i, attr));
5349 }
else if (attr == std::string(
"enzymejl_returnRoots")) {
5350 structAttrs[args.size()].push_back(
5351 Attribute::get(call.getContext(),
"enzymejl_returnRoots_v",
5352 call.getAttributes()
5353 .getParamAttr(i,
"enzymejl_returnRoots")
5354 .getValueAsString()));
5355 }
else if (attr == std::string(
"enzymejl_sret_union_bytes")) {
5356 structAttrs[args.size()].push_back(Attribute::get(
5357 call.getContext(),
"enzymejl_sret_union_bytes_v",
5358 call.getAttributes()
5359 .getParamAttr(i,
"enzymejl_sret_union_bytes")
5360 .getValueAsString()));
5361 }
else if (attr == std::string(
"enzymejl_rooted_typ")) {
5362 structAttrs[args.size()].push_back(
5363 Attribute::get(call.getContext(),
"enzymejl_rooted_typ_v",
5364 call.getAttributes()
5365 .getParamAttr(i,
"enzymejl_rooted_typ")
5366 .getValueAsString()));
5369 if (call.paramHasAttr(i, Attribute::StructRet)) {
5371 structAttrs[args.size()].push_back(
5372 Attribute::get(call.getContext(),
"enzyme_sret",
5374 call.getParamAttr(i, Attribute::StructRet)
5375 .getValueAsType())));
5377 structAttrs[args.size()].push_back(
5378 Attribute::get(call.getContext(),
"enzyme_sret_v",
5380 call.getParamAttr(i, Attribute::StructRet)
5381 .getValueAsType())));
5387 args.push_back(gutils->
invertPointerM(call.getArgOperand(i), Builder2));
5389#if LLVM_VERSION_MAJOR >= 16
5390 std::optional<int> tapeIdx;
5392 Optional<int> tapeIdx;
5396 if (found != subdata->
returns.end()) {
5397 tapeIdx = found->second;
5400 Value *tape =
nullptr;
5403 auto idx = *tapeIdx;
5404 FunctionType *FT = subdata->
fn->getFunctionType();
5405#if LLVM_VERSION_MAJOR >= 18
5406 auto It = BuilderZ.GetInsertPoint();
5407 It.setHeadBit(
true);
5408 BuilderZ.SetInsertPoint(It);
5410 tape = BuilderZ.CreatePHI(
5412 ? FT->getReturnType()
5413 : cast<StructType>(FT->getReturnType())->getElementType(idx),
5416 assert(!tape->getType()->isEmptyTy());
5420 args.push_back(tape);
5423 Value *newcalled =
nullptr;
5424 FunctionType *FT =
nullptr;
5433 tape ? tape->getType() :
nullptr, nextTypeInfo,
5434 subsequent_calls_may_write, overwritten_args,
5436 FT = cast<Function>(newcalled)->getFunctionType();
5438 auto callval = call.getCalledOperand();
5442 newcalled = BuilderZ.CreateExtractValue(newcalled, {0});
5447 "Attempting to call an indirect active function "
5448 "whose runtime value is inactive",
5451 auto ft = call.getFunctionType();
5455 ft, Mode, gutils->
getWidth(), tape ? tape->getType() :
nullptr,
5456 argsInverted,
false,
false,
5457 subretused, retActive);
5459 newcalled = BuilderZ.CreatePointerCast(newcalled,
getUnqual(fptype));
5460 newcalled = BuilderZ.CreateLoad(fptype, newcalled);
5466 SmallVector<ValueType, 2> BundleTypes;
5467 for (
auto A : argsInverted)
5476 CallInst *diffes = Builder2.CreateCall(FT, newcalled, args, Defs);
5477 diffes->setCallingConv(call.getCallingConv());
5480 for (
auto pair : gradByVal) {
5481 diffes->addParamAttr(
5483 Attribute::getWithByValType(diffes->getContext(), pair.second));
5486 for (
auto &pair : structAttrs) {
5487 for (
auto val : pair.second)
5488 diffes->addParamAttr(pair.first, val);
5493 Value *primal =
nullptr;
5494 Value *
diffe =
nullptr;
5497 primal = Builder2.CreateExtractValue(diffes, 0);
5498 diffe = Builder2.CreateExtractValue(diffes, 1);
5501 }
else if (!FT->getReturnType()->isVoidTy()) {
5506 auto placeholder = cast<PHINode>(&*ifound->second);
5509 gutils->
erase(newcall);
5518 gutils->
erase(placeholder);
5520 if (primal &&
diffe) {
5525 gutils->
erase(newcall);
5529 }
else if (primal) {
5531 gutils->
erase(newcall);
5542 SmallVector<Value *, 8> args;
5543 SmallVector<Value *, 8> pre_args;
5544 std::vector<DIFFE_TYPE> argsInverted;
5545 SmallVector<Instruction *, 4> postCreate;
5546 SmallVector<Instruction *, 4> userReplace;
5547 std::map<int, Type *> preByVal;
5548 std::map<int, Type *> gradByVal;
5549 std::map<int, std::vector<Attribute>> structAttrs;
5551 bool replaceFunction =
false;
5555 &call, *replacedReturns, postCreate, userReplace, gutils,
5556 unnecessaryInstructions, oldUnreachable, subretused);
5557 if (replaceFunction) {
5558 modifyPrimal =
false;
5562 SmallVector<ValueType, 2> PreBundleTypes;
5563 SmallVector<ValueType, 2> BundleTypes;
5564 std::vector<bool> nowrite_shadows;
5566 for (
unsigned i = 0; i < call.arg_size(); ++i) {
5570 if (call.isByValArgument(i)) {
5571 preByVal[pre_args.size()] = call.getParamByValType(i);
5573 for (
auto attr : {
"enzymejl_returnRoots",
"enzymejl_parmtype",
5574 "enzymejl_parmtype_ref",
"enzyme_type",
5575 "enzymejl_sret_union_bytes",
"enzymejl_rooted_typ"})
5576 if (call.getAttributes().hasParamAttr(i, attr)) {
5577 structAttrs[pre_args.size()].push_back(call.getParamAttr(i, attr));
5579 if (call.paramHasAttr(i, Attribute::StructRet)) {
5580 structAttrs[pre_args.size()].push_back(Attribute::get(
5581 call.getContext(),
"enzyme_sret",
5583 call.getParamAttr(i, Attribute::StructRet).getValueAsType())));
5586 if (call.getAttributes().hasParamAttr(i, ty)) {
5587 auto attr = call.getAttributes().getParamAttr(i, ty);
5588 structAttrs[pre_args.size()].push_back(attr);
5591 auto argTy = gutils->
getDiffeType(call.getArgOperand(i), foreignFunction);
5593 bool writeOnlyNoCapture =
true;
5594 bool readNoneNoCapture =
false;
5596 writeOnlyNoCapture =
false;
5597 readNoneNoCapture =
false;
5600 writeOnlyNoCapture =
false;
5603 readNoneNoCapture =
false;
5607 writeOnlyNoCapture =
false;
5608 readNoneNoCapture =
false;
5611 Value *prearg = argi;
5616 nowrite_shadows.push_back(
false);
5619 if (readNoneNoCapture ||
5621 (writeOnlyNoCapture ||
5626 pre_args.push_back(prearg);
5629 IRBuilder<> Builder2(&call);
5632 if (call.isByValArgument(i)) {
5633 gradByVal[args.size()] = call.getParamByValType(i);
5636 if ((writeOnlyNoCapture && !replaceFunction) ||
5637 (readNoneNoCapture ||
5639 (writeOnlyNoCapture ||
5644 args.push_back(
lookup(argi, Builder2));
5647 argsInverted.push_back(argTy);
5650 PreBundleTypes.push_back(preType);
5651 BundleTypes.push_back(revType);
5655 auto argType = argi->getType();
5660 if (call.getAttributes().hasParamAttr(i, ty)) {
5661 auto attr = call.getAttributes().getParamAttr(i, ty);
5662 structAttrs[pre_args.size()].push_back(attr);
5665 for (
auto attr : {
"enzymejl_returnRoots",
"enzymejl_parmtype",
5666 "enzymejl_parmtype_ref",
"enzyme_type",
5667 "enzymejl_sret_union_bytes",
"enzymejl_rooted_typ"})
5668 if (call.getAttributes().hasParamAttr(i, attr)) {
5670 structAttrs[pre_args.size()].push_back(
5671 call.getParamAttr(i, attr));
5672 }
else if (attr == std::string(
"enzymejl_returnRoots")) {
5673 structAttrs[pre_args.size()].push_back(
5674 Attribute::get(call.getContext(),
"enzymejl_returnRoots_v",
5675 call.getAttributes()
5676 .getParamAttr(i, attr)
5677 .getValueAsString()));
5678 }
else if (attr == std::string(
"enzymejl_sret_union_bytes")) {
5679 structAttrs[pre_args.size()].push_back(Attribute::get(
5680 call.getContext(),
"enzymejl_sret_union_bytes_v",
5681 call.getAttributes()
5682 .getParamAttr(i, attr)
5683 .getValueAsString()));
5684 }
else if (attr == std::string(
"enzymejl_rooted_typ")) {
5685 structAttrs[pre_args.size()].push_back(
5686 Attribute::get(call.getContext(),
"enzymejl_rooted_typ_v",
5687 call.getAttributes()
5688 .getParamAttr(i, attr)
5689 .getValueAsString()));
5692 if (call.paramHasAttr(i, Attribute::StructRet)) {
5694 structAttrs[pre_args.size()].push_back(
5695 Attribute::get(call.getContext(),
"enzyme_sret",
5697 call.getParamAttr(i, Attribute::StructRet)
5698 .getValueAsType())));
5700 structAttrs[pre_args.size()].push_back(
5701 Attribute::get(call.getContext(),
"enzyme_sret_v",
5703 call.getParamAttr(i, Attribute::StructRet)
5704 .getValueAsType())));
5708 IRBuilder<> Builder2(&call);
5711 Value *darg =
nullptr;
5713 if (((writeOnlyNoCapture && TR.
query(call.getArgOperand(
5724 args.push_back(
lookup(darg, Builder2));
5728 if (
auto arg = dyn_cast<Argument>(baseOp)) {
5731 nowrite_shadows.back() =
true;
5740 bool forwardsShadow =
true;
5744 forwardsShadow = found->second.primalInitialize;
5747 if (!forwardsShadow) {
5748 nowrite_shadows.back() =
true;
5754 nowrite_shadows.back() =
true;
5768 raw_string_ostream ss(
str);
5769 ss <<
"Mismatched estimated activity type for " << *argType
5770 <<
" expected DUP_ARG or CONSTANT found " << wt
5771 <<
", call = " << call <<
"\n";
5777 EmitFailure(
"MismatchArgType", call.getDebugLoc(), &call, ss.str());
5781 if (foreignFunction)
5782 assert(!argType->isIntOrIntVectorTy());
5786 PreBundleTypes.push_back(preType);
5787 BundleTypes.push_back(revType);
5790 if (call.arg_size() !=
5791 cast<Function>(called)->getFunctionType()->getNumParams()) {
5792 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
5793 llvm::errs() << *gutils->
oldFunc <<
"\n";
5794 llvm::errs() << call <<
"\n";
5795 llvm::errs() <<
" number of arg operands != function parameters\n";
5796 EmitFailure(
"MismatchArgs", call.getDebugLoc(), &call,
5797 "Number of arg operands != function parameters\n", call);
5801 Value *tape =
nullptr;
5802 CallInst *augmentcall =
nullptr;
5803 Value *cachereplace =
nullptr;
5807#if LLVM_VERSION_MAJOR >= 16
5808 std::optional<int> tapeIdx;
5809 std::optional<int> returnIdx;
5810 std::optional<int> differetIdx;
5812 Optional<int> tapeIdx;
5813 Optional<int> returnIdx;
5814 Optional<int> differetIdx;
5818 Value *newcalled =
nullptr;
5819 FunctionType *FT =
nullptr;
5823 auto callval = call.getCalledOperand();
5824 Value *uncast = callval;
5825 while (
auto CE = dyn_cast<ConstantExpr>(uncast)) {
5827 uncast = CE->getOperand(0);
5832 if (isa<ConstantInt>(uncast)) {
5834 raw_string_ostream ss(
str);
5835 ss <<
"cannot find shadow for " << *callval
5836 <<
" for use as function in " << call;
5844 "Attempting to call an indirect active function "
5845 "whose runtime value is inactive",
5848 FunctionType *ft = call.getFunctionType();
5850 std::set<llvm::Type *> seen;
5854 ft,
true, subretType);
5855 FT = FunctionType::get(
5856 StructType::get(newcalled->getContext(), res.second), res.first,
5859 newcalled = BuilderZ.CreatePointerCast(newcalled,
getUnqual(fptype));
5860 newcalled = BuilderZ.CreateLoad(fptype, newcalled);
5863 if (!call.getType()->isVoidTy()) {
5876 subretused, shadowReturnUsed, nextTypeInfo,
5877 subsequent_calls_may_write, overwritten_args, nowrite_shadows,
5881 assert(augmentedReturn);
5882 auto subaugmentations =
5883 (std::map<const llvm::CallInst *, AugmentedReturn *>
5890 Intrinsic::ID ID = Intrinsic::not_intrinsic;
5893 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
5894 llvm::errs() << *gutils->
oldFunc <<
"\n";
5895 llvm::errs() << *gutils->
newFunc <<
"\n";
5896 llvm::errs() << *called <<
"\n";
5902 fnandtapetype = subdata;
5903 newcalled = subdata->
fn;
5904 FT = cast<Function>(newcalled)->getFunctionType();
5908 if (found != subdata->
returns.end()) {
5909 differetIdx = found->second;
5911 assert(!shadowReturnUsed);
5915 if (found != subdata->
returns.end()) {
5916 returnIdx = found->second;
5918 assert(!subretused);
5922 if (found != subdata->
returns.end()) {
5923 tapeIdx = found->second;
5939 auto NC = dyn_cast<Function>(newcalled);
5940 llvm::errs() << *gutils->
oldFunc <<
"\n";
5941 llvm::errs() << *gutils->
newFunc <<
"\n";
5943 llvm::errs() <<
" trying to call " << NC->getName() <<
" " << *FT
5946 llvm::errs() <<
" trying to call " << *newcalled <<
" " << *FT
5949 for (
unsigned i = 0; i < pre_args.size(); ++i) {
5950 assert(pre_args[i]);
5951 assert(pre_args[i]->getType());
5952 llvm::errs() <<
"args[" << i <<
"] = " << *pre_args[i]
5953 <<
" FT:" << *FT->getParamType(i) <<
"\n";
5955 assert(0 &&
"calling with wrong number of arguments");
5959 if (pre_args.size() != FT->getNumParams())
5960 goto badaugmentedfn;
5962 for (
unsigned i = 0; i < pre_args.size(); ++i) {
5963 if (pre_args[i]->getType() == FT->getParamType(i))
5965 else if (!call.getCalledFunction())
5967 BuilderZ.CreateBitCast(pre_args[i], FT->getParamType(i));
5969 goto badaugmentedfn;
5972 augmentcall = BuilderZ.CreateCall(
5973 FT, newcalled, pre_args,
5976 augmentcall->setCallingConv(call.getCallingConv());
5977 augmentcall->setDebugLoc(
5980 for (
auto pair : preByVal) {
5981 augmentcall->addParamAttr(
5982 pair.first, Attribute::getWithByValType(augmentcall->getContext(),
5986 for (
auto &pair : structAttrs) {
5987 for (
auto val : pair.second)
5988 augmentcall->addParamAttr(pair.first, val);
5991 if (!augmentcall->getType()->isVoidTy())
5992 augmentcall->setName(call.getName() +
"_augmented");
5995 auto tval = *tapeIdx;
5996 tape = (tval == -1) ? augmentcall
5997 : BuilderZ.CreateExtractValue(
5998 augmentcall, {(unsigned)tval},
"subcache");
5999 if (tape->getType()->isEmptyTy()) {
6000 auto tt = tape->getType();
6003 if (tape != augmentcall)
6004 gutils->
erase(cast<Instruction>(tape));
6005 tape = UndefValue::get(tt);
6014 Value *dcall =
nullptr;
6016 assert(augmentcall);
6017 auto rval = *returnIdx;
6018 dcall = (rval < 0) ? augmentcall
6019 : BuilderZ.CreateExtractValue(augmentcall,
6025 assert(dcall->getType() == call.getType());
6029 if (!call.getType()->isFPOrFPVectorTy() && TR.
anyPointer(&call)) {
6036 assert(dcall->getType() == call.getType());
6039 if (isa<Instruction>(dcall) && !isa<PHINode>(dcall)) {
6040 cast<Instruction>(dcall)->takeName(newCall);
6046 std::map<UsageKey, bool> Seen =
6048 bool primalNeededInReverse =
false;
6052 if (!found->second) {
6053 primalNeededInReverse =
true;
6057 if (!primalNeededInReverse) {
6062 primalNeededInReverse =
6067 if (primalNeededInReverse)
6071 BuilderZ.SetInsertPoint(newCall->getNextNode());
6072 gutils->
erase(newCall);
6074 BuilderZ.SetInsertPoint(BuilderZ.GetInsertPoint()->getNextNode());
6089 auto tval = *tapeIdx;
6090#if LLVM_VERSION_MAJOR >= 18
6091 auto It = BuilderZ.GetInsertPoint();
6092 It.setHeadBit(
true);
6093 BuilderZ.SetInsertPoint(It);
6095 tape = BuilderZ.CreatePHI(
6096 (tapeIdx == -1) ? FT->getReturnType()
6097 : cast<StructType>(FT->getReturnType())
6098 ->getElementType(tval),
6107 Intrinsic::ID ID = Intrinsic::not_intrinsic;
6114#if LLVM_VERSION_MAJOR >= 18
6115 auto It = BuilderZ.GetInsertPoint();
6116 It.setHeadBit(
true);
6117 BuilderZ.SetInsertPoint(It);
6122 raw_string_ostream ss(
str);
6123 ss <<
"Failed to compute consistent cache index for operation: "
6130 EmitFailure(
"GetIndexError", call.getDebugLoc(), &call,
6135 cachereplace = newCall;
6137 cachereplace = BuilderZ.CreatePHI(
6138 call.getType(), 1, call.getName() +
"_tmpcacheB");
6144#if LLVM_VERSION_MAJOR >= 18
6145 auto It = BuilderZ.GetInsertPoint();
6146 It.setHeadBit(
true);
6147 BuilderZ.SetInsertPoint(It);
6149 auto pn = BuilderZ.CreatePHI(
6150 call.getType(), 1, (call.getName() +
"_replacementE").str());
6157 BuilderZ.SetInsertPoint(BuilderZ.GetInsertPoint()->getNextNode());
6164 auto placeholder = cast<PHINode>(&*ifound->second);
6171 bool hasNonReturnUse =
false;
6172 for (
auto use : call.users()) {
6174 !isa<ReturnInst>(use)) {
6175 hasNonReturnUse =
true;
6179 if (subcheck && hasNonReturnUse) {
6181 Value *newip =
nullptr;
6187 raw_string_ostream ss(
str);
6188 ss <<
"Did not have return index set when differentiating "
6190 ss <<
" call" << call <<
"\n";
6191 ss <<
" augmentcall" << *augmentcall <<
"\n";
6197 EmitFailure(
"GetIndexError", call.getDebugLoc(), &call,
6200 placeholder->replaceAllUsesWith(
6201 UndefValue::get(placeholder->getType()));
6202 if (placeholder == &*BuilderZ.GetInsertPoint()) {
6203 BuilderZ.SetInsertPoint(placeholder->getNextNode());
6205 gutils->
erase(placeholder);
6207 auto drval = *differetIdx;
6210 : BuilderZ.CreateExtractValue(augmentcall,
6212 call.getName() +
"'ac");
6213 assert(newip->getType() == placeholder->getType());
6214 placeholder->replaceAllUsesWith(newip);
6215 if (placeholder == &*BuilderZ.GetInsertPoint()) {
6216 BuilderZ.SetInsertPoint(placeholder->getNextNode());
6218 gutils->
erase(placeholder);
6221 newip = placeholder;
6231 if (placeholder == &*BuilderZ.GetInsertPoint()) {
6232 BuilderZ.SetInsertPoint(placeholder->getNextNode());
6234 gutils->
erase(placeholder);
6238 if (fnandtapetype && fnandtapetype->
tapeType &&
6244 auto tapep = BuilderZ.CreatePointerCast(
6247 cast<PointerType>(tape->getType())->getAddressSpace()));
6249 BuilderZ.CreateLoad(fnandtapetype->
tapeType, tapep,
"tapeld");
6250 truetape->setMetadata(
"enzyme_mustcache",
6251 MDNode::get(truetape->getContext(), {}));
6259 auto placeholder = cast<PHINode>(&*ifound->second);
6261 gutils->
erase(placeholder);
6264 subretused && !call.doesNotAccessMemory()) {
6268 assert(!replaceFunction);
6269#if LLVM_VERSION_MAJOR >= 18
6270 auto It = BuilderZ.GetInsertPoint();
6271 It.setHeadBit(
true);
6272 BuilderZ.SetInsertPoint(It);
6274 cachereplace = BuilderZ.CreatePHI(call.getType(), 1,
6275 call.getName() +
"_cachereplace2");
6277 BuilderZ, cachereplace,
6280#if LLVM_VERSION_MAJOR >= 18
6281 auto It = BuilderZ.GetInsertPoint();
6282 It.setHeadBit(
true);
6283 BuilderZ.SetInsertPoint(It);
6285 auto pn = BuilderZ.CreatePHI(call.getType(), 1,
6286 call.getName() +
"_replacementC");
6292 if (!subretused && !replaceFunction)
6301 IRBuilder<> Builder2(&call);
6304 Value *newcalled =
nullptr;
6305 FunctionType *FT =
nullptr;
6312 for (
size_t i = 0; i < argsInverted.size(); i++) {
6324 .todiff = cast<Function>(called),
6325 .retType = subretType,
6326 .constant_args = argsInverted,
6327 .subsequent_calls_may_write = subsequent_calls_may_write,
6328 .overwritten_args = overwritten_args,
6329 .returnUsed = replaceFunction && subretused,
6330 .shadowReturnUsed = shadowReturnUsed && replaceFunction,
6335 .additionalType = tape ? tape->getType() :
nullptr,
6336 .forceAnonymousTape =
false,
6337 .typeInfo = nextTypeInfo,
6343 FT = cast<Function>(newcalled)->getFunctionType();
6348 auto callval = call.getCalledOperand();
6352 llvm::raw_string_ostream ss(s);
6353 ss << *gutils->
oldFunc <<
"\n";
6354 ss <<
"in Mode: " <<
to_string(Mode) <<
"\n";
6355 ss <<
" orig: " << call <<
" callval: " << *callval <<
"\n";
6356 ss <<
" constant function being called, but active call instruction\n";
6367 auto ft = call.getFunctionType();
6372 res.first.push_back(
getInt8PtrTy(newcalled->getContext()));
6373 FT = FunctionType::get(
6374 StructType::get(newcalled->getContext(), res.second), res.first,
6377 newcalled = Builder2.CreatePointerCast(newcalled,
getUnqual(fptype));
6378 newcalled = Builder2.CreateLoad(
6379 fptype, Builder2.CreateConstGEP1_64(fptype, newcalled, 1));
6383 args.push_back(
diffe(&call, Builder2));
6387 auto ntape = gutils->
lookupM(tape, Builder2);
6389 assert(ntape->getType());
6390 args.push_back(ntape);
6398 auto NC = dyn_cast<Function>(newcalled);
6399 llvm::errs() << *gutils->
oldFunc <<
"\n";
6400 llvm::errs() << *gutils->
newFunc <<
"\n";
6402 llvm::errs() <<
" trying to call " << NC->getName() <<
" " << *FT
6405 llvm::errs() <<
" trying to call " << *newcalled <<
" " << *FT <<
"\n";
6407 for (
unsigned i = 0; i < args.size(); ++i) {
6409 assert(args[i]->getType());
6410 llvm::errs() <<
"args[" << i <<
"] = " << *args[i]
6411 <<
" FT:" << *FT->getParamType(i) <<
"\n";
6413 assert(0 &&
"calling with wrong number of arguments");
6417 if (args.size() != FT->getNumParams())
6420 for (
unsigned i = 0; i < args.size(); ++i) {
6421 if (args[i]->getType() == FT->getParamType(i))
6423 else if (!call.getCalledFunction())
6424 args[i] = Builder2.CreateBitCast(args[i], FT->getParamType(i));
6430 Builder2.CreateCall(FT, newcalled, args,
6432 &call, BundleTypes, Builder2,
true));
6433 diffes->setCallingConv(call.getCallingConv());
6436 for (
auto pair : gradByVal) {
6437 diffes->addParamAttr(pair.first, Attribute::getWithByValType(
6438 diffes->getContext(), pair.second));
6441 for (
auto &pair : structAttrs) {
6442 for (
auto val : pair.second)
6443 diffes->addParamAttr(pair.first, val);
6446 unsigned structidx = 0;
6447 if (replaceFunction) {
6450 if (shadowReturnUsed)
6454 for (
unsigned i = 0; i < call.arg_size(); ++i) {
6456 Value *diffeadd = Builder2.CreateExtractValue(diffes, {structidx});
6461 if (call.getArgOperand(i)->getType()->isSized())
6462 size = (gutils->
newFunc->getParent()
6464 .getTypeSizeInBits(call.getArgOperand(i)->getType()) +
6468 addToDiffe(call.getArgOperand(i), diffeadd, Builder2,
6474 if (diffes->getType()->isVoidTy()) {
6475 if (structidx != 0) {
6476 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
6477 llvm::errs() <<
"diffes: " << *diffes <<
" structidx=" << structidx
6478 <<
" subretused=" << subretused
6479 <<
" shadowReturnUsed=" << shadowReturnUsed <<
"\n";
6481 assert(structidx == 0);
6483 assert(cast<StructType>(diffes->getType())->getNumElements() ==
6489 Constant::getNullValue(gutils->
getShadowType(call.getType())),
6492 if (replaceFunction) {
6498 auto placeholder = cast<PHINode>(&*ifound->second);
6500 if (shadowReturnUsed) {
6502 auto dretval = cast<Instruction>(
6503 Builder2.CreateExtractValue(diffes, {subretused ? 1U : 0U}));
6505 assert(!subretused);
6509 gutils->
erase(placeholder);
6512 Instruction *retval =
nullptr;
6515 retval = cast<Instruction>(Builder2.CreateExtractValue(diffes, {0}));
6524 SmallPtrSet<Value *, 2> postCreateSet(postCreate.begin(),
6526 for (
auto a : postCreate) {
6527 a->moveBefore(*Builder2.GetInsertBlock(), Builder2.GetInsertPoint());
6528 for (
size_t i = 0; i < a->getNumOperands(); i++) {
6529 auto op = dyn_cast<Instruction>(a->getOperand(i));
6530 if (!op || postCreateSet.count(op))
6533 IRBuilder<> BuilderA(a);
6534 a->setOperand(i, gutils->
lookupM(op, BuilderA));
6543 gutils->
erase(newCall);
6550 Value *dcall =
nullptr;
6551 assert(cachereplace->getType() == call.getType());
6552 assert(dcall ==
nullptr);
6553 dcall = cachereplace;
6560 if (!call.getType()->isFPOrFPVectorTy() && TR.
anyPointer(&call)) {
6567 assert(dcall->getType() == call.getType());
6568 newCall->replaceAllUsesWith(dcall);
6569 if (isa<Instruction>(dcall) && !isa<PHINode>(dcall)) {
6570 cast<Instruction>(dcall)->takeName(&call);
6572 gutils->
erase(newCall);
6585 void handleMPI(llvm::CallInst &call, llvm::Function *called,
6586 llvm::StringRef funcName);
6589 llvm::StringRef funcName,
6590 bool subsequent_calls_may_write,
6591 const std::vector<bool> &overwritten_args,
6592 llvm::CallInst *
const newCall);
6596 using namespace llvm;
6605 if (
startsWith(funcName, (
"llvm.intel.subscript"))) {
6606 assert(isa<IntrinsicInst>(call));
6611 if (funcName ==
"llvm.enzyme.lifetime_start") {
6615 if (funcName ==
"llvm.enzyme.lifetime_end") {
6616 SmallVector<Value *, 2> orig_ops(call.getNumOperands());
6617 for (
unsigned i = 0; i < call.getNumOperands(); ++i) {
6618 orig_ops[i] = call.getOperand(i);
6626 IRBuilder<> BuilderZ(newCall);
6627 BuilderZ.setFastMathFlags(
getFast());
6629 if (overwritten_args_map.find(&call) == overwritten_args_map.end() &&
6632 llvm::errs() <<
" call: " << call <<
"\n";
6633 for (
auto &pair : overwritten_args_map) {
6634 llvm::errs() <<
" + " << *pair.first <<
"\n";
6638 assert(overwritten_args_map.find(&call) != overwritten_args_map.end() ||
6641 const bool subsequent_calls_may_write =
6645 : overwritten_args_map.find(&call)->second.first;
6646 const std::vector<bool> &overwritten_args =
6649 ? std::vector<bool>()
6650 : overwritten_args_map.find(&call)->second.second;
6654 bool subretused =
false;
6655 bool shadowReturnUsed =
false;
6660 &call, &subretused, &shadowReturnUsed, smode);
6666 Value *invertedReturn =
nullptr;
6669 invertedReturn = cast<PHINode>(&*ifound->second);
6672 Value *normalReturn = subretused ? newCall :
nullptr;
6674 bool noMod = found->second(BuilderZ, &call, *gutils, normalReturn,
6678 assert(normalReturn == newCall);
6684 auto placeholder = cast<PHINode>(&*ifound->second);
6685 if (invertedReturn && invertedReturn != placeholder) {
6686 if (invertedReturn->getType() !=
6688 llvm::errs() <<
" o: " << call <<
"\n";
6689 llvm::errs() <<
" ot: " << *call.getType() <<
"\n";
6690 llvm::errs() <<
" ir: " << *invertedReturn <<
"\n";
6691 llvm::errs() <<
" irt: " << *invertedReturn->getType() <<
"\n";
6692 llvm::errs() <<
" p: " << *placeholder <<
"\n";
6693 llvm::errs() <<
" PT: " << *placeholder->getType() <<
"\n";
6694 llvm::errs() <<
" newCall: " << *newCall <<
"\n";
6695 llvm::errs() <<
" newCallT: " << *newCall->getType() <<
"\n";
6697 assert(invertedReturn->getType() ==
6699 placeholder->replaceAllUsesWith(invertedReturn);
6700 gutils->
erase(placeholder);
6702 std::make_pair((
const Value *)&call,
6706 gutils->
erase(placeholder);
6710 if (normalReturn && normalReturn != newCall) {
6711 assert(normalReturn->getType() == newCall->getType());
6713 gutils->
erase(newCall);
6724 IRBuilder<> Builder2(&call);
6729 Value *invertedReturn =
nullptr;
6731 PHINode *placeholder =
nullptr;
6733 placeholder = cast<PHINode>(&*ifound->second);
6734 if (shadowReturnUsed)
6735 invertedReturn = placeholder;
6738 Value *normalReturn = subretused ? newCall :
nullptr;
6740 Value *tape =
nullptr;
6742 Type *tapeType =
nullptr;
6746 bool noMod = found->second.first(BuilderZ, &call, *gutils,
6747 normalReturn, invertedReturn, tape);
6750 assert(normalReturn == newCall);
6754 tapeType = tape->getType();
6759 assert(augmentedReturn);
6760 auto subaugmentations =
6761 (std::map<const llvm::CallInst *, AugmentedReturn *>
6774 assert(augmentedReturn);
6775 auto subaugmentations =
6776 (std::map<const llvm::CallInst *, AugmentedReturn *>
6778 auto fd = subaugmentations->find(&call);
6779 assert(fd != subaugmentations->end());
6785 tapeType = (llvm::Type *)fd->second;
6787#if LLVM_VERSION_MAJOR >= 18
6788 auto It = BuilderZ.GetInsertPoint();
6789 It.setHeadBit(
true);
6790 BuilderZ.SetInsertPoint(It);
6792 tape = BuilderZ.CreatePHI(tapeType, 0);
6798 tape = gutils->
lookupM(tape, Builder2);
6804 if (!shadowReturnUsed) {
6806 gutils->
erase(placeholder);
6808 if (invertedReturn && invertedReturn != placeholder) {
6809 if (invertedReturn->getType() !=
6811 llvm::errs() <<
" o: " << call <<
"\n";
6812 llvm::errs() <<
" ot: " << *call.getType() <<
"\n";
6813 llvm::errs() <<
" ir: " << *invertedReturn <<
"\n";
6814 llvm::errs() <<
" irt: " << *invertedReturn->getType() <<
"\n";
6815 llvm::errs() <<
" p: " << *placeholder <<
"\n";
6816 llvm::errs() <<
" PT: " << *placeholder->getType() <<
"\n";
6817 llvm::errs() <<
" newCall: " << *newCall <<
"\n";
6818 llvm::errs() <<
" newCallT: " << *newCall->getType() <<
"\n";
6820 assert(invertedReturn->getType() ==
6822 placeholder->replaceAllUsesWith(invertedReturn);
6823 gutils->
erase(placeholder);
6825 BuilderZ, invertedReturn,
6832 if (placeholder->getType() != invertedReturn->getType())
6833 llvm::errs() <<
" place: " << *placeholder
6834 <<
" invRet: " << *invertedReturn;
6835 placeholder->replaceAllUsesWith(invertedReturn);
6836 gutils->
erase(placeholder);
6841 std::make_pair((
const Value *)&call,
6846 bool primalNeededInReverse;
6851 std::map<UsageKey, bool> Seen =
6853 primalNeededInReverse =
6857 if (subretused && primalNeededInReverse) {
6858 if (normalReturn != newCall) {
6859 assert(normalReturn->getType() == newCall->getType());
6861 BuilderZ.SetInsertPoint(newCall->getNextNode());
6862 gutils->
erase(newCall);
6865 BuilderZ, normalReturn,
6868 if (normalReturn && normalReturn != newCall) {
6869 assert(normalReturn->getType() == newCall->getType());
6872 BuilderZ.SetInsertPoint(newCall->getNextNode());
6873 gutils->
erase(newCall);
6882 if (funcName ==
"__kmpc_fork_call") {
6889 subsequent_calls_may_write, overwritten_args,
6893 bool useConstantFallback =
6896 if (!useConstantFallback) {
6900 "Call was deduced inactive but still doing differential "
6901 "rewrite as it may escape an allocation",
6905 if (useConstantFallback) {
6909 PHINode *placeholder = cast<PHINode>(&*found->second);
6911 gutils->
erase(placeholder);
6916 noFree |= call.hasFnAttr(Attribute::NoFree);
6917 if (!noFree && called) {
6918 noFree |= called->hasFnAttribute(Attribute::NoFree);
6921 std::map<UsageKey, bool> CacheResults =
6925 bool mayActiveFree =
false;
6926 for (
unsigned i = 0; i < call.arg_size(); ++i) {
6927 Value *a = call.getOperand(i);
6932 if (!TR.
query(a)[{-1}].isPossiblePointer())
6936 mayActiveFree =
true;
6944 bool isAllocation =
false;
6945 for (
auto objv = obj;;) {
6947 isAllocation =
true;
6950 if (
auto objC = dyn_cast<CallBase>(objv))
6953 SmallPtrSet<Value *, 1> set;
6954 for (
auto &B : *F) {
6955 if (
auto RI = dyn_cast<ReturnInst>(B.getTerminator())) {
6957 if (isa<ConstantPointerNull>(v))
6962 if (set.size() == 1) {
6963 objv = *set.begin();
6969 if (!isAllocation) {
6970 mayActiveFree =
true;
6976 if (!found->second) {
6977 auto CacheResults2(CacheResults);
6982 CacheResults2, oldUnreachable)) {
6983 mayActiveFree =
true;
6990 auto CacheResults2(CacheResults);
6994 CacheResults2, oldUnreachable)) {
6995 mayActiveFree =
true;
7003 auto callval = call.getCalledOperand();
7004 if (!isa<Constant>(callval))
7025 (call.mayWriteToMemory() ||
7029 std::map<UsageKey, bool> Seen =
7031 bool primalNeededInReverse =
false;
7035 if (!found->second) {
7036 primalNeededInReverse =
true;
7040 if (!primalNeededInReverse) {
7045 primalNeededInReverse =
7050 if (primalNeededInReverse) {
7070 if (call.mayWriteToMemory() &&
7078 if (!call.mayWriteToMemory() && !subretused) {
7087 call, called, subsequent_calls_may_write, overwritten_args,
7088 shadowReturnUsed, subretType, subretused);
static bool isZero(llvm::Constant *cst)
DIFFE_TYPE
Potential differentiable argument classifications.
FunctionType * getFunctionTypeForClone(llvm::FunctionType *FTy, DerivativeMode mode, unsigned width, llvm::Type *additionalArg, llvm::ArrayRef< DIFFE_TYPE > constant_args, bool diffeReturnArg, bool returnTape, bool returnPrimal, bool returnShadow)
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static bool isReadOnly(Operation *op)
static Operation * getFunctionFromCall(CallOpInterface iface)
llvm::cl::opt< bool > EnzymeGlobalActivity
constexpr const char * to_string(ActivityAnalyzer::UseActivity UA)
std::pair< SmallVector< Type *, 4 >, SmallVector< Type *, 4 > > getDefaultFunctionTypeForAugmentation(FunctionType *called, bool returnUsed, DIFFE_TYPE retType)
assuming not top level
bool legalCombinedForwardReverse(CallInst *origop, const std::map< ReturnInst *, StoreInst * > &replacedReturns, SmallVectorImpl< Instruction * > &postCreate, SmallVectorImpl< Instruction * > &userReplace, const GradientUtils *gutils, const SmallPtrSetImpl< const Instruction * > &unnecessaryInstructions, const SmallPtrSetImpl< BasicBlock * > &oldUnreachable, const bool subretused)
bool shouldAugmentCall(CallInst *op, const GradientUtils *gutils)
std::pair< SmallVector< Type *, 4 >, SmallVector< Type *, 4 > > getDefaultFunctionTypeForGradient(FunctionType *called, DIFFE_TYPE retType, ArrayRef< DIFFE_TYPE > tys)
assuming not top level
static std::string str(AugmentedStruct c)
llvm::cl::opt< bool > looseTypeAnalysis
llvm::cl::opt< bool > nonmarkedglobals_inactiveloads
StringMap< std::pair< std::function< bool(IRBuilder<> &, CallInst *, GradientUtils &, Value *&, Value *&, Value *&)>, std::function< void(IRBuilder<> &, CallInst *, DiffeGradientUtils &, Value *)> > > customCallHandlers
void SubTransferHelper(GradientUtils *gutils, DerivativeMode mode, Type *secretty, Intrinsic::ID intrinsic, unsigned dstalign, unsigned srcalign, unsigned offset, bool dstConstant, Value *shadow_dst, Value *primal_dst, bool srcConstant, Value *shadow_src, Value *primal_src, Value *length, Value *isVolatile, llvm::CallInst *MTI, bool allowForward, bool shadowsLookedUp, bool backwardsShadow)
SmallVector< unsigned int, 9 > MD_ToCopy
StringMap< std::function< bool(IRBuilder<> &, CallInst *, GradientUtils &, Value *&, Value *&)> > customFwdCallHandlers
constexpr int IndexMappingError
std::pair< const llvm::Value *, QueryType > UsageKey
static TypeTree parseTBAA(TBAAStructTypeNode AccessType, llvm::Instruction &I, const llvm::DataLayout &DL, std::shared_ptr< llvm::ModuleSlotTracker > MST)
Given a TBAA access node return the corresponding TypeTree This includes recursively parsing the acce...
TypeTree defaultTypeTreeForLLVM(llvm::Type *ET, llvm::Instruction *I, bool intIsPointer)
static bool isMemFreeLibMFunction(llvm::StringRef str, llvm::Intrinsic::ID *ID=nullptr)
CallInst * CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree)
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::Value * EmitNoDerivativeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2, llvm::Value *condition)
llvm::MDNode * sliceTrueType(const llvm::MDNode *md, size_t start, size_t length)
Restrict an enzyme_truetype annotation describing a memory region to the byte range [start,...
void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal, llvm::Value *shadow, const char *Message, llvm::DebugLoc &&loc, llvm::Instruction *orig)
Function * getOrInsertDifferentialFloatMemcpy(Module &M, Type *elementType, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
Create function for type that is equivalent to memcpy but adds to destination rather than a direct co...
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
llvm::Constant * getUndefinedValueForType(llvm::Module &M, llvm::Type *T, bool forceZero)
std::vector< std::tuple< llvm::Type *, size_t, size_t > > parseTrueType(const llvm::MDNode *md, DerivativeMode Mode, bool const_src)
static llvm::Value * checkedMul(bool strongZero, llvm::IRBuilder<> &Builder2, llvm::Value *idiff, llvm::Value *pres, const llvm::Twine &Name="")
static bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II)
static std::string getRenamedPerCallingConv(llvm::StringRef caller, llvm::StringRef callee)
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
static void dumpMap(const llvm::ValueMap< T, N > &o, llvm::function_ref< bool(const llvm::Value *)> shouldPrint=[](T) { return true;})
Print a map, optionally with a shouldPrint function to decide to print a given value.
@ Args
Return is a struct of all args.
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
static llvm::PointerType * getUnqual(llvm::Type *T)
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
static std::map< K, V >::iterator insert_or_assign2(std::map< K, V > &map, K key, V val)
Insert into a map.
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
static bool isFortranMPICall(llvm::StringRef Name)
True if Name uses a Fortran ABI mangling of an MPI routine (trailing underscore(s),...
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
static bool containsOnlyAtMostTopBit(const llvm::Value *V, llvm::Type *FT, const llvm::DataLayout &dl, llvm::Type **vFT=nullptr)
static bool shouldDisableNoWrite(const llvm::CallInst *CI)
void EmitWarning(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::BasicBlock *BB, const Args &...args)
static bool isSpecialPtr(llvm::Type *Ty)
llvm::cl::opt< bool > EnzymeJuliaAddrLoad
static llvm::Type * IntToFloatTy(llvm::Type *T)
Convert a integer type to a floating point type of the same size.
void EmitWarningAlways(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Function &F, const Args &...args)
Emit a diagnostic which is always shown, unlike EmitWarning which requires the user to opt in with -R...
static bool EmitWarningEnabled(const llvm::LLVMContext &Ctx)
static llvm::Value * CreateSelect(llvm::IRBuilder<> &Builder2, llvm::Value *cmp, llvm::Value *tval, llvm::Value *fval, const llvm::Twine &Name="")
static std::string convertSRetTypeToString(llvm::Type *T)
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
static constexpr const char * MixedActivityHint
Remedy appended to mismatched-activity warnings, which are emitted when a value Enzyme proved inactiv...
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
ValueType
Classification of value as an original program variable, a derivative variable, neither,...
static llvm::Attribute::AttrKind PrimalParamAttrsToPreserve[]
static llvm::Attribute::AttrKind ShadowParamAttrsToPreserve[]
static DIFFE_TYPE whatType(llvm::Type *arg, DerivativeMode mode, bool integersAreConstant, std::set< llvm::Type * > &seen)
Attempt to automatically detect the differentiable classification based off of a given type.
llvm::Value * lookup(llvm::Value *val, llvm::IRBuilder<> &Builder)
void visitExtractElementInst(llvm::ExtractElementInst &EEI)
void forwardModeInvertedPointerFallback(llvm::Instruction &I)
llvm::Value * diffe(llvm::Value *val, llvm::IRBuilder<> &Builder)
void visitShuffleVectorInst(llvm::ShuffleVectorInst &SVI)
void createSelectInstAdjoint(llvm::SelectInst &SI)
void DifferentiableMemCopyFloats(llvm::CallInst &call, llvm::Value *origArg, llvm::Value *dsto, llvm::Value *srco, llvm::Value *len_arg, llvm::IRBuilder<> &Builder2, llvm::ArrayRef< llvm::OperandBundleDef > ReverseDefs)
void applyChainRule(llvm::ArrayRef< llvm::Value * > diffs, llvm::IRBuilder<> &Builder, Func rule)
Unwraps an collection of constant vector derivatives from their internal representations and applies ...
bool handleKnownCallDerivatives(llvm::CallInst &call, llvm::Function *called, llvm::StringRef funcName, bool subsequent_calls_may_write, const std::vector< bool > &overwritten_args, llvm::CallInst *const newCall)
void setDiffe(llvm::Value *val, llvm::Value *dif, llvm::IRBuilder<> &Builder)
void visitBinaryOperator(llvm::BinaryOperator &BO)
void visitInsertValueInst(llvm::InsertValueInst &IVI)
void visitCallInst(llvm::CallInst &call)
void visitGetElementPtrInst(llvm::GetElementPtrInst &gep)
llvm::Value * MPI_TYPE_SIZE(llvm::Value *DT, llvm::IRBuilder<> &B, llvm::Type *intType, llvm::Function *caller)
void visitFCmpInst(llvm::FCmpInst &I)
void visitExtractValueInst(llvm::ExtractValueInst &EVI)
void visitLoadLike(llvm::Instruction &I, llvm::MaybeAlign alignment, bool constantval, llvm::Value *mask=nullptr, llvm::Value *orig_maskInit=nullptr)
static llvm::AtomicOrdering reverseLoadOrdering(llvm::AtomicOrdering order)
The ordering with which the reverse pass may read a location that the primal modified atomically with...
void visitCastInst(llvm::CastInst &I)
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...
void visitInsertElementInst(llvm::InsertElementInst &IEI)
void visitCommonStore(llvm::Instruction &I, llvm::Value *orig_ptr, llvm::Value *orig_val, llvm::MaybeAlign prevalign, bool isVolatile, llvm::AtomicOrdering ordering, llvm::SyncScope::ID syncScope, llvm::Value *mask)
void visitICmpInst(llvm::ICmpInst &I)
void visitFenceInst(llvm::FenceInst &FI)
void shadowStoreSchedule(llvm::Instruction &I, llvm::Value *orig_ptr, bool &forwardsShadow, bool &backwardsShadow)
Which pass(es) emit the shadow counterpart of the store-like instruction I into the duplicated memory...
void resolveShadowPlaceholder(llvm::Instruction &I, llvm::Value *shadow=nullptr)
Resolve the shadow placeholder of the active instruction I in a pass which does not otherwise compute...
void visitIntrinsicInst(llvm::IntrinsicInst &II)
void createBinaryOperatorAdjoint(llvm::BinaryOperator &BO)
void visitPHINode(llvm::PHINode &phi)
llvm::SmallVector< llvm::SelectInst *, 4 > addToDiffe(llvm::Value *val, llvm::Value *dif, llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *mask=nullptr)
void eraseIfUnused(llvm::Instruction &I, bool erase=true, bool check=true)
AdjointGenerator(DerivativeMode Mode, GradientUtils *gutils, llvm::ArrayRef< DIFFE_TYPE > constant_args, DIFFE_TYPE retType, std::function< unsigned(llvm::Instruction *, CacheType, llvm::IRBuilder<> &)> getIndex, const std::map< llvm::CallInst *, std::pair< bool, const std::vector< bool > > > overwritten_args_map, const AugmentedReturn *augmentedReturn, const std::map< llvm::ReturnInst *, llvm::StoreInst * > *replacedReturns, const llvm::SmallPtrSetImpl< const llvm::Value * > &unnecessaryValues, const llvm::SmallPtrSetImpl< const llvm::Instruction * > &unnecessaryInstructions, const llvm::SmallPtrSetImpl< const llvm::Instruction * > &unnecessaryStores, const llvm::SmallPtrSetImpl< llvm::BasicBlock * > &oldUnreachable)
void visitInstruction(llvm::Instruction &inst)
void visitAtomicRMWInst(llvm::AtomicRMWInst &I)
void visitLoadInst(llvm::LoadInst &LI)
void visitMemSetInst(llvm::MemSetInst &MS)
void visitMemTransferInst(llvm::MemTransferInst &MTI)
void visitOMPCall(llvm::CallInst &call)
void visitSelectInst(llvm::SelectInst &SI)
void createBinaryOperatorDual(llvm::BinaryOperator &BO)
void getForwardBuilder(llvm::IRBuilder<> &Builder2)
void getReverseBuilder(llvm::IRBuilder<> &Builder2, bool original=true)
void applyChainRule(llvm::IRBuilder<> &Builder, Func rule, Args... args)
Unwraps a vector derivative from its internal representation and applies a function f to each element...
void visitAllocaInst(llvm::AllocaInst &I)
void recursivelyHandleSubfunction(llvm::CallInst &call, llvm::Function *called, bool subsequent_calls_may_write, const std::vector< bool > &overwritten_args, bool shadowReturnUsed, DIFFE_TYPE subretType, bool subretused)
llvm::Value * MPI_COMM_SIZE(llvm::Value *comm, llvm::IRBuilder<> &B, llvm::Type *rankTy, llvm::Function *caller)
void visitMemTransferCommon(llvm::Intrinsic::ID ID, llvm::MaybeAlign srcAlign, llvm::MaybeAlign dstAlign, llvm::CallInst &MTI, llvm::Value *orig_dst, llvm::Value *orig_src, llvm::Value *new_size, llvm::Value *isVolatile)
llvm::Value * MPI_COMM_RANK(llvm::Value *comm, llvm::IRBuilder<> &B, llvm::Type *rankTy, llvm::Function *caller)
bool handleAdjointForIntrinsic(llvm::Intrinsic::ID ID, llvm::Instruction &I, llvm::SmallVectorImpl< llvm::Value * > &orig_ops)
void handleMPI(llvm::CallInst &call, llvm::Function *called, llvm::StringRef funcName)
void addAtomicRMWValueAdjoint(llvm::Instruction &I, llvm::Value *ptr, llvm::Value *val, llvm::Type *loadTy, llvm::Type *FT, bool negate, llvm::Align align, llvm::AtomicOrdering order, llvm::SyncScope::ID ssid, bool isVolatile)
Reverse-mode adjoint of the value operand val of an atomic modification new = old + val (negate == fa...
void visitStoreInst(llvm::StoreInst &SI)
void visitMemSetCommon(llvm::CallInst &MS)
return structtype if recursive function
const std::vector< DIFFE_TYPE > constant_args
std::set< ssize_t > tapeIndiciesToFree
std::map< const llvm::CallInst *, const AugmentedReturn * > subaugmentations
Map from original call to sub augmentation data.
std::map< std::pair< llvm::Instruction *, CacheType >, int > tapeIndices
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.
llvm::Function *const newFunc
The function whose instructions we are caching.
llvm::TargetLibraryInfo & TLI
Various analysis results of newFunc.
llvm::BasicBlock * inversionAllocs
Concrete SubType of a given value.
bool isKnown() const
Whether this ConcreteType has information (is not unknown)
llvm::Type * isFloat() const
Return the floating point type, if this is a float.
llvm::Function * CreateNoFree(RequestContext context, llvm::Function *todiff)
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 * 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::ValueMap< const llvm::Instruction *, AssertingReplacingVH > unwrappedLoads
llvm::SmallPtrSet< llvm::Instruction *, 4 > unnecessaryIntermediates
llvm::ValueMap< const llvm::Value *, InvertedPointerVH > invertedPointers
bool getContext(llvm::BasicBlock *BB, LoopContext &lc)
const std::map< llvm::Instruction *, bool > * can_modref_map
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::SmallPtrSet< llvm::Instruction *, 4 > TapesToPreventRecomputation
A set of tape extractions to enforce a cache of rather than attempting to recompute.
void eraseWithPlaceholder(llvm::Instruction *I, llvm::Instruction *orig, const llvm::Twine &suffix="_replacementA", bool erase=true)
bool legalRecompute(const llvm::Value *val, const llvm::ValueToValueMapTy &available, llvm::IRBuilder<> *BuilderM, bool reverse=false, bool legalRecomputeCache=true) const
std::map< const llvm::Value *, bool > knownRecomputeHeuristic
llvm::Value * getOrInsertTotalMultiplicativeProduct(llvm::Value *val, LoopContext &lc)
llvm::BasicBlock * addReverseBlock(llvm::BasicBlock *currentBlock, llvm::Twine const &name, bool forkCache=true, bool push=true)
llvm::SmallPtrSet< llvm::Value *, 4 > allocationsToBeRematerialized
llvm::ValueMap< llvm::Value *, Rematerializer > rematerializableAllocations
llvm::ValueMap< llvm::PHINode *, llvm::WeakTrackingVH > fictiousPHIs
llvm::BasicBlock * getOriginalFromNew(const llvm::BasicBlock *newinst) const
std::map< UsageKey, bool > populateSeenFromKnownRecompute() const
llvm::SmallVector< llvm::OperandBundleDef, 2 > getInvertedBundles(llvm::CallInst *orig, llvm::ArrayRef< ValueType > types, llvm::IRBuilder<> &Builder2, bool lookup, const llvm::ValueToValueMapTy &available=llvm::ValueToValueMapTy())
void replaceAWithB(llvm::Value *A, llvm::Value *B, bool storeInCache=false) override
Replace this instruction both in LLVM modules and any local data-structures.
DIFFE_TYPE getDiffeType(llvm::Value *v, bool foreignFunction) const
void replaceAndRemoveUnwrapCacheFor(llvm::Value *A, llvm::Value *B)
llvm::Value * isOriginal(const llvm::Value *newinst) const
std::vector< bool > nowrite_shadows
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::ValueMap< const llvm::Value *, AssertingReplacingVH > originalToNewFn
llvm::Value * invertPointerM(llvm::Value *val, llvm::IRBuilder<> &BuilderM)
llvm::Value * getOrInsertConditionalIndex(llvm::Value *val, LoopContext &lc, bool pickTrue)
DIFFE_TYPE getReturnDiffeType(llvm::Value *orig, bool *primalReturnUsedP, bool *shadowReturnUsedP, DerivativeMode cmode) const
void setPtrDiffe(llvm::Instruction *orig, llvm::Value *ptr, llvm::Value *newval, llvm::IRBuilder<> &BuilderM, llvm::MaybeAlign align, unsigned start, unsigned size, bool isVolatile, llvm::AtomicOrdering ordering, llvm::SyncScope::ID syncScope, llvm::Value *mask, llvm::ArrayRef< llvm::Metadata * > noAlias, llvm::ArrayRef< llvm::Metadata * > scopes, bool needs_post_cache=false)
bool isConstantInstruction(const llvm::Instruction *inst) const
llvm::ValueMap< const llvm::Value *, AssertingReplacingVH > newToOriginalFn
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
llvm::Value * cacheForReverse(llvm::IRBuilder<> &BuilderQ, llvm::Value *malloc, int idx, bool replace=true)
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.
TypeAnalysis & interprocedural
Calling TypeAnalysis to be used in the case of calls to other functions.
std::map< llvm::Value *, TypeTree > analysis
Intermediate conservative, but correct Type analysis results.
A holder class representing the results of running TypeAnalysis on a given function.
llvm::Type * addingType(size_t num, llvm::Value *val, size_t start=0) const
ConcreteType firstPointer(size_t num, llvm::Value *val, llvm::Instruction *I, GradientUtils *gutils=nullptr, llvm::IRBuilder<> *BuilderIfShouldErr=nullptr, bool pointerIntSame=false) const
Returns whether in the first num bytes there is pointer, int, float, or none If pointerIntSame is set...
void dump(llvm::raw_ostream &ss=llvm::errs()) const
Prints all known information.
bool anyPointer(llvm::Value *val) const
Whether any part of the top level register can contain a pointer e.g.
std::set< int64_t > knownIntegralValues(llvm::Value *val) const
The set of values val will take on during this program.
const TypeTree & query(llvm::Value *val) const
The TypeTree of a particular Value.
ConcreteType intType(size_t num, llvm::Value *val, llvm::Instruction *I, GradientUtils *gutils=nullptr, llvm::IRBuilder<> *BuilderIfShouldErr=nullptr, bool pointerIntSame=false) const
llvm::Function * getFunction() const
FnTypeInfo getCallInfo(llvm::CallBase &CI, llvm::Function &fn) const
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
TypeTree Only(int Off, llvm::Instruction *orig) const
Prepend an offset to all mappings.
TypeTree Data0() const
Peel off the outermost index at offset 0.
static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx)
TypeTree ShiftIndices(const llvm::DataLayout &dl, const int offset, const int maxSize, size_t addOffset=0) const
Replace mappings in the range in [offset, offset+maxSize] with those in.
TypeTree Lookup(size_t len, const llvm::DataLayout &dl) const
Select all submappings whose first index is in range [0, len) and remove the first index.
std::string str() const
Returns a string representation of this TypeTree.
bool insert(const std::vector< int > Seq, ConcreteType CT, bool PointerIntSame=false)
Return if changed.
TypeTree PurgeAnything() const
Keep only mappings where the type is not an Anything
llvm::Type * allFloat(llvm::Value *val, const llvm::DataLayout &dl, bool anythingIsFloat=false) const
bool callShouldNotUseDerivative(const GradientUtils *gutils, llvm::CallBase &orig, QueryType qtype, const llvm::Value *val)
Return whether or not this is a constant and should use reverse pass.
bool is_value_needed_in_reverse(const GradientUtils *gutils, const llvm::Value *inst, DerivativeMode mode, std::map< UsageKey, bool > &seen, const llvm::SmallPtrSetImpl< llvm::BasicBlock * > &oldUnreachable)
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.
Container for all loop information to synthesize gradients.
llvm::BasicBlock * header
Header of this loop.
llvm::AssertingVH< llvm::Instruction > incvar
Increment of the induction.
llvm::SmallPtrSet< llvm::BasicBlock *, 8 > exitBlocks
All blocks this loop exits too.
llvm::BasicBlock * preheader
Preheader of this loop.
todiff is the function to differentiate retType is the activity info of the return.