96 llvm::StringRef funcName) {
102 BuilderZ.setFastMathFlags(
getFast());
109 raw_string_ostream ss(s);
110 ss << funcName <<
" is not supported in vector reverse mode: " << call;
116 if (funcName ==
"PMPI_Isend" || funcName ==
"MPI_Isend" ||
117 funcName ==
"PMPI_Irecv" || funcName ==
"MPI_Irecv") {
123 Value *d_req = gutils->
invertPointerM(call.getOperand(6), BuilderZ);
124 if (d_req->getType()->isIntegerTy()) {
125 d_req = BuilderZ.CreateIntToPtr(
129 auto i64 = Type::getInt64Ty(call.getContext());
136 d_req = BuilderZ.CreateBitCast(d_req,
getUnqual(impialloc->getType()));
137 Value *d_req_prev = BuilderZ.CreateLoad(impialloc->getType(), d_req);
138 BuilderZ.CreateStore(
139 BuilderZ.CreatePointerCast(d_req_prev,
142 BuilderZ.CreateStore(impialloc, d_req);
144 if (funcName ==
"MPI_Isend" || funcName ==
"PMPI_Isend") {
147 BuilderZ, call.getType(), called);
149 auto len_arg = BuilderZ.CreateZExtOrTrunc(
151 Type::getInt64Ty(call.getContext()));
152 len_arg = BuilderZ.CreateMul(
154 BuilderZ.CreateZExtOrTrunc(tysize,
155 Type::getInt64Ty(call.getContext())),
158 Value *firstallocation =
160 len_arg,
"mpirecv_malloccache");
162 BuilderZ, impialloc, impi));
165 Value *ibuf = gutils->
invertPointerM(call.getOperand(0), BuilderZ);
166 if (ibuf->getType()->isIntegerTy())
168 BuilderZ.CreateIntToPtr(ibuf,
getInt8PtrTy(call.getContext()));
169 BuilderZ.CreateStore(
173 BuilderZ.CreateStore(
174 BuilderZ.CreateZExtOrTrunc(
179 if (dataType->getType()->isIntegerTy())
180 dataType = BuilderZ.CreateIntToPtr(
182 BuilderZ.CreateStore(
183 BuilderZ.CreatePointerCast(dataType,
187 BuilderZ.CreateStore(
188 BuilderZ.CreateZExtOrTrunc(
192 BuilderZ.CreateStore(
193 BuilderZ.CreateZExtOrTrunc(
198 if (comm->getType()->isIntegerTy())
199 comm = BuilderZ.CreateIntToPtr(comm,
201 BuilderZ.CreateStore(
202 BuilderZ.CreatePointerCast(comm,
getInt8PtrTy(call.getContext())),
205 BuilderZ.CreateStore(
207 Type::getInt8Ty(impialloc->getContext()),
208 (funcName ==
"MPI_Isend" || funcName ==
"PMPI_Isend")
216 IRBuilder<> Builder2(&call);
219 Type *statusType =
nullptr;
220#if LLVM_VERSION_MAJOR < 17
221 if (Function *recvfn = called->getParent()->getFunction(
223 auto statusArg = recvfn->arg_end();
225 if (
auto PT = dyn_cast<PointerType>(statusArg->getType()))
226 statusType = PT->getPointerElementType();
229 if (statusType ==
nullptr) {
230 statusType = ArrayType::get(Type::getInt8Ty(call.getContext()), 24);
231 llvm::errs() <<
" warning could not automatically determine mpi "
232 "status type, assuming [24 x i8]\n";
238 if (d_req->getType()->isIntegerTy()) {
240 Builder2.CreateIntToPtr(d_req,
getInt8PtrTy(call.getContext()));
244 Value *helper = Builder2.CreatePointerCast(d_req,
getUnqual(helperTy));
245 helper = Builder2.CreateLoad(helperTy, helper);
247 auto i64 = Type::getInt64Ty(call.getContext());
249 Value *firstallocation;
250 firstallocation = Builder2.CreateLoad(
253 Value *len_arg =
nullptr;
254 if (
auto C = dyn_cast<Constant>(
256 len_arg = Builder2.CreateZExtOrTrunc(C, i64);
258 len_arg = Builder2.CreateLoad(
261 Value *tysize =
nullptr;
262 if (
auto C = dyn_cast<Constant>(
266 tysize = Builder2.CreateLoad(
272 prev = Builder2.CreateLoad(
276 Builder2.CreateStore(prev, Builder2.CreatePointerCast(
282 tysize =
MPI_TYPE_SIZE(tysize, Builder2, call.getType(), called);
284 Value *args[] = { req,
286 .CreateAlloca(statusType)};
287 FunctionCallee waitFunc =
nullptr;
291 if (Function *recvfn = called->getParent()->getFunction(
293 auto statusArg = recvfn->arg_end();
295 if (statusArg->getType()->isIntegerTy())
296 args[1] = Builder2.CreatePtrToInt(args[1], statusArg->getType());
298 args[1] = Builder2.CreateBitCast(args[1], statusArg->getType());
303 Type *types[
sizeof(args) /
sizeof(*args)];
304 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args); i++)
305 types[i] = args[i]->getType();
306 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
307 waitFunc = called->getParent()->getOrInsertFunction(
327 auto fcall = Builder2.CreateCall(waitFunc, args, ReqDefs);
329 if (
auto F = dyn_cast<Function>(waitFunc.getCallee()))
330 fcall->setCallingConv(F->getCallingConv());
331 len_arg = Builder2.CreateMul(
333 Builder2.CreateZExtOrTrunc(tysize,
334 Type::getInt64Ty(Builder2.getContext())),
336 if (funcName ==
"MPI_Irecv" || funcName ==
"PMPI_Irecv") {
338 ConstantInt::get(Type::getInt8Ty(Builder2.getContext()), 0);
339 auto volatile_arg = ConstantInt::getFalse(Builder2.getContext());
341 auto dbuf = firstallocation;
342 Value *nargs[] = {dbuf, val_arg, len_arg, volatile_arg};
343 Type *tys[] = {dbuf->getType(), len_arg->getType()};
345 auto memset = cast<CallInst>(Builder2.CreateCall(
349 memset->addParamAttr(0, Attribute::NonNull);
350 }
else if (funcName ==
"MPI_Isend" || funcName ==
"PMPI_Isend") {
357 shadow, len_arg, Builder2, BufferDefs);
363 assert(0 &&
"illegal mpi");
369 IRBuilder<> Builder2(&call);
385 if (funcName ==
"MPI_Wait" || funcName ==
"PMPI_Wait") {
386 Value *d_reqp =
nullptr;
391 Value *d_req = gutils->
invertPointerM(call.getOperand(0), BuilderZ);
393 if (req->getType()->isIntegerTy()) {
394 req = BuilderZ.CreateIntToPtr(
398 Value *isNull =
nullptr;
399 if (
auto GV = gutils->
newFunc->getParent()->getNamedValue(
400 "ompi_request_null")) {
401 Value *reql = BuilderZ.CreatePointerCast(req,
getUnqual(GV->getType()));
402 reql = BuilderZ.CreateLoad(GV->getType(), reql);
403 isNull = BuilderZ.CreateICmpEQ(reql, GV);
406 if (d_req->getType()->isIntegerTy()) {
407 d_req = BuilderZ.CreateIntToPtr(
411 d_reqp = BuilderZ.CreateLoad(
417 Constant::getNullValue(d_reqp->getType()), d_reqp);
418 if (
auto I = dyn_cast<Instruction>(d_reqp))
425 IRBuilder<> Builder2(&call);
433 d_reqp = BuilderZ.CreatePHI(
getUnqual(impi), 0);
438 d_reqp =
lookup(d_reqp, Builder2);
440 Value *isNull = Builder2.CreateICmpEQ(
441 d_reqp, Constant::getNullValue(d_reqp->getType()));
443 BasicBlock *currentBlock = Builder2.GetInsertBlock();
445 currentBlock, currentBlock->getName() +
"_nonnull");
447 nonnullBlock, currentBlock->getName() +
"_end",
450 Builder2.CreateCondBr(isNull, endBlock, nonnullBlock);
451 Builder2.SetInsertPoint(nonnullBlock);
453 Value *cache = Builder2.CreateLoad(impi, d_reqp);
464 Type *types[
sizeof(args) /
sizeof(*args) - 1];
465 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args) - 1; i++)
466 types[i] = args[i]->getType();
468 *called->getParent(), types, call.getOperand(0)->getType(),
475 Builder2.CreateCall(dwait, args,
477 &call, {ValueType::Shadow, ValueType::None},
479 cal->setCallingConv(dwait->getCallingConv());
481 cal->addFnAttr(Attribute::AlwaysInline);
482 Builder2.CreateBr(endBlock);
486 SmallVector<BasicBlock *, 4> &vec =
489 vec.push_back(endBlock);
491 Builder2.SetInsertPoint(endBlock);
494 IRBuilder<> Builder2(&call);
508 if (funcName ==
"MPI_Waitall" || funcName ==
"PMPI_Waitall") {
509 Value *d_reqp =
nullptr;
516 Value *d_req = gutils->
invertPointerM(call.getOperand(1), BuilderZ);
518 if (req->getType()->isIntegerTy()) {
519 req = BuilderZ.CreateIntToPtr(
523 if (d_req->getType()->isIntegerTy()) {
524 d_req = BuilderZ.CreateIntToPtr(
530 {count->getType(), req->getType(), d_req->getType()}, reqType);
532 d_reqp = BuilderZ.CreateCall(dsave, {count, req, d_req});
533 cast<CallInst>(d_reqp)->setCallingConv(dsave->getCallingConv());
534 cast<CallInst>(d_reqp)->setDebugLoc(
541 IRBuilder<> Builder2(&call);
551 d_reqp = BuilderZ.CreatePHI(
getUnqual(reqType), 0);
556 d_reqp =
lookup(d_reqp, Builder2);
558 BasicBlock *currentBlock = Builder2.GetInsertBlock();
560 currentBlock, currentBlock->getName() +
"_loop");
562 loopBlock, currentBlock->getName() +
"_nonnull");
564 nonnullBlock, currentBlock->getName() +
"_eloop");
565 BasicBlock *endBlock =
569 Builder2.CreateCondBr(
570 Builder2.CreateICmpNE(count,
571 ConstantInt::get(count->getType(), 0,
false)),
572 loopBlock, endBlock);
574 Builder2.SetInsertPoint(loopBlock);
575 auto idx = Builder2.CreatePHI(count->getType(), 2);
576 idx->addIncoming(ConstantInt::get(count->getType(), 0,
false),
578 Value *inc = Builder2.CreateAdd(
579 idx, ConstantInt::get(count->getType(), 1,
false),
"",
true,
true);
580 idx->addIncoming(inc, eloopBlock);
582 Value *idxs[] = {idx};
583 Value *req = Builder2.CreateInBoundsGEP(reqType, req_orig, idxs);
584 Value *d_req = Builder2.CreateInBoundsGEP(reqType, d_reqp, idxs);
586 d_req = Builder2.CreateLoad(
590 Value *isNull = Builder2.CreateICmpEQ(
591 d_req, Constant::getNullValue(d_req->getType()));
593 Builder2.CreateCondBr(isNull, eloopBlock, nonnullBlock);
594 Builder2.SetInsertPoint(nonnullBlock);
596 Value *cache = Builder2.CreateLoad(impi, d_req);
607 Type *types[
sizeof(args) /
sizeof(*args) - 1];
608 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args) - 1; i++)
609 types[i] = args[i]->getType();
611 *called->getParent(), types, req->getType(), called->getName());
616 auto cal = Builder2.CreateCall(
619 {ValueType::None, ValueType::None,
620 ValueType::None, ValueType::None,
621 ValueType::None, ValueType::None,
624 cal->setCallingConv(dwait->getCallingConv());
626 cal->addFnAttr(Attribute::AlwaysInline);
627 Builder2.CreateBr(eloopBlock);
629 Builder2.SetInsertPoint(eloopBlock);
630 Builder2.CreateCondBr(Builder2.CreateICmpEQ(inc, count), endBlock,
635 SmallVector<BasicBlock *, 4> &vec =
638 vec.push_back(endBlock);
640 Builder2.SetInsertPoint(endBlock);
646 IRBuilder<> Builder2(&call);
659 if (funcName ==
"MPI_Send" || funcName ==
"MPI_Ssend" ||
660 funcName ==
"PMPI_Send" || funcName ==
"PMPI_Ssend") {
668 IRBuilder<> Builder2 =
669 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
676 Value *shadow = gutils->
invertPointerM(call.getOperand(0), Builder2);
678 shadow =
lookup(shadow, Builder2);
679 if (shadow->getType()->isIntegerTy())
681 Builder2.CreateIntToPtr(shadow,
getInt8PtrTy(call.getContext()));
683 Type *statusType =
nullptr;
684#if LLVM_VERSION_MAJOR < 17
685 if (called->getContext().supportsTypedPointers()) {
686 if (Function *recvfn = called->getParent()->getFunction(
688 auto statusArg = recvfn->arg_end();
690 if (
auto PT = dyn_cast<PointerType>(statusArg->getType()))
691 statusType = PT->getPointerElementType();
695 if (statusType ==
nullptr) {
696 statusType = ArrayType::get(Type::getInt8Ty(call.getContext()), 24);
697 llvm::errs() <<
" warning could not automatically determine mpi "
698 "status type, assuming [24 x i8]\n";
703 count =
lookup(count, Builder2);
707 datatype =
lookup(datatype, Builder2);
711 src =
lookup(src, Builder2);
715 tag =
lookup(tag, Builder2);
719 comm =
lookup(comm, Builder2);
736 Value *tysize =
MPI_TYPE_SIZE(datatype, Builder2, call.getType(), called);
738 auto len_arg = Builder2.CreateZExtOrTrunc(
739 args[1], Type::getInt64Ty(call.getContext()));
741 Builder2.CreateMul(len_arg,
742 Builder2.CreateZExtOrTrunc(
743 tysize, Type::getInt64Ty(call.getContext())),
746 Value *firstallocation =
748 len_arg,
"mpirecv_malloccache");
749 args[0] = firstallocation;
751 Type *types[
sizeof(args) /
sizeof(*args)];
752 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args); i++)
753 types[i] = args[i]->getType();
754 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
756 Builder2.SetInsertPoint(Builder2.GetInsertBlock());
764 auto fcall = Builder2.CreateCall(
765 called->getParent()->getOrInsertFunction(
768 fcall->setCallingConv(call.getCallingConv());
771 shadow, len_arg, Builder2, BufferDefs);
782 if (funcName ==
"MPI_Recv" || funcName ==
"PMPI_Recv") {
790 IRBuilder<> Builder2 =
791 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
798 Value *shadow = gutils->
invertPointerM(call.getOperand(0), Builder2);
800 shadow =
lookup(shadow, Builder2);
804 count =
lookup(count, Builder2);
808 datatype =
lookup(datatype, Builder2);
812 source =
lookup(source, Builder2);
816 tag =
lookup(tag, Builder2);
820 comm =
lookup(comm, Builder2);
827 Value *args[] = {shadow, count, datatype, source, tag, comm};
834 Builder2, !forwardMode);
836 Type *types[
sizeof(args) /
sizeof(*args)];
837 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args); i++)
838 types[i] = args[i]->getType();
839 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
841 auto fcall = Builder2.CreateCall(
842 called->getParent()->getOrInsertFunction(
845 fcall->setCallingConv(call.getCallingConv());
848 Builder2.CreateBitCast(args[0],
getInt8PtrTy(call.getContext()));
849 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
850 auto len_arg = Builder2.CreateZExtOrTrunc(
851 args[1], Type::getInt64Ty(call.getContext()));
852 auto tysize =
MPI_TYPE_SIZE(datatype, Builder2, call.getType(), called);
854 Builder2.CreateMul(len_arg,
855 Builder2.CreateZExtOrTrunc(
856 tysize, Type::getInt64Ty(call.getContext())),
858 auto volatile_arg = ConstantInt::getFalse(call.getContext());
860 Value *nargs[] = {dst_arg, val_arg, len_arg, volatile_arg};
861 Type *tys[] = {dst_arg->getType(), len_arg->getType()};
868 auto memset = cast<CallInst>(Builder2.CreateCall(
870 Intrinsic::memset, tys),
872 memset->addParamAttr(0, Attribute::NonNull);
885 if (funcName ==
"MPI_Bcast" || funcName ==
"PMPI_Bcast") {
893 IRBuilder<> Builder2 =
894 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
901 Value *shadow = gutils->
invertPointerM(call.getOperand(0), Builder2);
903 shadow =
lookup(shadow, Builder2);
904 if (shadow->getType()->isIntegerTy())
906 Builder2.CreateIntToPtr(shadow,
getInt8PtrTy(call.getContext()));
909 TR.
firstPointer(1, call.getOperand(0), &call, gutils, &Builder2);
911 Type *MPI_OP_Ptr_type =
getUnqual(MPI_OP_type);
915 count =
lookup(count, Builder2);
918 datatype =
lookup(datatype, Builder2);
921 root =
lookup(root, Builder2);
925 comm =
lookup(comm, Builder2);
932 Value *rank =
MPI_COMM_RANK(comm, Builder2, root->getType(), called);
933 Value *tysize =
MPI_TYPE_SIZE(datatype, Builder2, call.getType(), called);
935 auto len_arg = Builder2.CreateZExtOrTrunc(
936 count, Type::getInt64Ty(call.getContext()));
938 Builder2.CreateMul(len_arg,
939 Builder2.CreateZExtOrTrunc(
940 tysize, Type::getInt64Ty(call.getContext())),
947 BasicBlock *currentBlock = Builder2.GetInsertBlock();
949 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
951 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
953 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, root), rootBlock,
956 Builder2.SetInsertPoint(rootBlock);
960 len_arg,
"mpireduce_malloccache");
961 Builder2.CreateBr(mergeBlock);
963 Builder2.SetInsertPoint(mergeBlock);
965 buf = Builder2.CreatePHI(rootbuf->getType(), 2);
966 buf->addIncoming(rootbuf, rootBlock);
967 buf->addIncoming(UndefValue::get(buf->getType()), currentBlock);
989 MPI_OP_Ptr_type, MPI_OP_type, CT,
990 root->getType(), Builder2),
994 Type *types[
sizeof(args) /
sizeof(*args)];
995 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args); i++)
996 types[i] = args[i]->getType();
998 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
1000 Builder2.CreateCall(
1001 called->getParent()->getOrInsertFunction(
1007 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1009 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1011 rootBlock, currentBlock->getName() +
"_nonroot", gutils->
newFunc);
1013 nonrootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1015 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, root), rootBlock,
1018 Builder2.SetInsertPoint(rootBlock);
1021 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1022 Value *nargs[] = {shadow, buf, len_arg, volatile_arg};
1024 Type *tys[] = {shadow->getType(), buf->getType(), len_arg->getType()};
1027 Intrinsic::memcpy, tys);
1030 cast<CallInst>(Builder2.CreateCall(memcpyF, nargs, BufferDefs));
1031 mem->setCallingConv(memcpyF->getCallingConv());
1039 Builder2.CreateBr(mergeBlock);
1041 Builder2.SetInsertPoint(nonrootBlock);
1044 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
1045 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1046 Value *args[] = {shadow, val_arg, len_arg, volatile_arg};
1047 Type *tys[] = {args[0]->getType(), args[2]->getType()};
1048 auto memset = cast<CallInst>(Builder2.CreateCall(
1050 Intrinsic::memset, tys),
1052 memset->addParamAttr(0, Attribute::NonNull);
1053 Builder2.CreateBr(mergeBlock);
1055 Builder2.SetInsertPoint(mergeBlock);
1076 if (canonMPIName ==
"MPI_Reduce") {
1086 IRBuilder<> Builder2 =
1087 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
1095 Value *orig_sendbuf = call.getOperand(0);
1096 Value *orig_recvbuf = call.getOperand(1);
1097 Value *orig_count = call.getOperand(2);
1098 Value *orig_datatype = call.getOperand(3);
1099 Value *orig_op = call.getOperand(4);
1100 Value *orig_root = call.getOperand(5);
1101 Value *orig_comm = call.getOperand(6);
1108 if (Constant *C = dyn_cast<Constant>(orig_op)) {
1109 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
1110 C = CE->getOperand(0);
1112 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
1113 if (GV->getName() ==
"ompi_mpi_op_sum") {
1115 }
else if (fortranABI && GV->isConstant() &&
1116 GV->hasDefinitiveInitializer()) {
1119 if (
auto *CI = dyn_cast<ConstantInt>(GV->getInitializer())) {
1122 if (CI->getValue() == 1476395011) {
1128 if (CI->getValue() == 33) {
1135 if (ConstantInt *CI = dyn_cast<ConstantInt>(C)) {
1136 if (CI->getValue() == 1476395011) {
1142 if (ConstantInt *CI = dyn_cast<ConstantInt>(C)) {
1143 if (CI->getValue() == 33) {
1159 llvm::errs() <<
"warning: cannot determine MPI op used in `" << call
1160 <<
"`, assuming MPI_SUM\n";
1163 llvm::raw_string_ostream ss(s);
1164 ss <<
" call: " << call <<
"\n";
1165 ss <<
" unhandled mpi_reduce op: " << *orig_op <<
"\n";
1171 Value *shadow_recvbuf = gutils->
invertPointerM(orig_recvbuf, Builder2);
1173 shadow_recvbuf =
lookup(shadow_recvbuf, Builder2);
1174 if (shadow_recvbuf->getType()->isIntegerTy())
1175 shadow_recvbuf = Builder2.CreateIntToPtr(
1178 Value *shadow_sendbuf = gutils->
invertPointerM(orig_sendbuf, Builder2);
1180 shadow_sendbuf =
lookup(shadow_sendbuf, Builder2);
1181 if (shadow_sendbuf->getType()->isIntegerTy())
1182 shadow_sendbuf = Builder2.CreateIntToPtr(
1188 std::vector<ValueType> BufferBundleTypes(call.arg_size(),
1198 count =
lookup(count, Builder2);
1202 datatype =
lookup(datatype, Builder2);
1206 op =
lookup(op, Builder2);
1210 root =
lookup(root, Builder2);
1214 comm =
lookup(comm, Builder2);
1218 Type *i32Ty = Type::getInt32Ty(call.getContext());
1219 Value *countVal = fortranABI ? Builder2.CreateLoad(i32Ty, count) : count;
1220 Value *rootVal = fortranABI ? Builder2.CreateLoad(i32Ty, root) : root;
1222 Value *rank =
MPI_COMM_RANK(comm, Builder2, rootVal->getType(), called);
1230 Value *tysize =
MPI_TYPE_SIZE(datatype, Builder2, call.getType(), called);
1233 auto len_arg = Builder2.CreateZExtOrTrunc(
1234 countVal, Type::getInt64Ty(call.getContext()));
1236 Builder2.CreateMul(len_arg,
1237 Builder2.CreateZExtOrTrunc(
1238 tysize, Type::getInt64Ty(call.getContext())),
1244 len_arg,
"mpireduce_malloccache");
1249 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1251 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1253 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1255 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, rootVal), rootBlock,
1258 Builder2.SetInsertPoint(rootBlock);
1261 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1262 Value *nargs[] = {buf, shadow_recvbuf, len_arg, volatile_arg};
1264 Type *tys[] = {nargs[0]->getType(), nargs[1]->getType(),
1265 len_arg->getType()};
1268 Intrinsic::memcpy, tys);
1271 cast<CallInst>(Builder2.CreateCall(memcpyF, nargs, BufferDefs));
1272 mem->setCallingConv(memcpyF->getCallingConv());
1275 Builder2.CreateBr(mergeBlock);
1276 Builder2.SetInsertPoint(mergeBlock);
1286 SmallVector<Value *, 8> args = {
1295 .CreateAlloca(i32Ty,
nullptr,
"enzyme_mpi_ierr"));
1297 SmallVector<Type *, 8> types;
1298 for (
auto *arg : args) {
1299 types.push_back(arg->getType());
1302 FunctionType *FT = FunctionType::get(
1303 fortranABI ? Type::getVoidTy(call.getContext()) : call.getType(),
1305 Builder2.CreateCall(
1306 called->getParent()->getOrInsertFunction(
1313 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1315 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1317 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1319 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, rootVal), rootBlock,
1322 Builder2.SetInsertPoint(rootBlock);
1324 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
1325 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1326 Value *args[] = {shadow_recvbuf, val_arg, len_arg, volatile_arg};
1327 Type *tys[] = {args[0]->getType(), args[2]->getType()};
1328 auto memset = cast<CallInst>(Builder2.CreateCall(
1330 Intrinsic::memset, tys),
1332 memset->addParamAttr(0, Attribute::NonNull);
1334 Builder2.CreateBr(mergeBlock);
1335 Builder2.SetInsertPoint(mergeBlock);
1340 len_arg, Builder2, BufferDefs);
1362 if (canonMPIName ==
"MPI_Allreduce") {
1372 IRBuilder<> Builder2 =
1373 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
1381 Value *orig_sendbuf = call.getOperand(0);
1382 Value *orig_recvbuf = call.getOperand(1);
1383 Value *orig_count = call.getOperand(2);
1384 Value *orig_datatype = call.getOperand(3);
1385 Value *orig_op = call.getOperand(4);
1386 Value *orig_comm = call.getOperand(5);
1393 if (Constant *C = dyn_cast<Constant>(orig_op)) {
1394 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
1395 C = CE->getOperand(0);
1397 if (
auto GV = dyn_cast<GlobalVariable>(C)) {
1398 if (GV->getName() ==
"ompi_mpi_op_sum") {
1400 }
else if (fortranABI && GV->isConstant() &&
1401 GV->hasDefinitiveInitializer()) {
1404 if (
auto *CI = dyn_cast<ConstantInt>(GV->getInitializer())) {
1407 if (CI->getValue() == 1476395011) {
1413 if (CI->getValue() == 33) {
1420 if (ConstantInt *CI = dyn_cast<ConstantInt>(C)) {
1421 if (CI->getValue() == 1476395011) {
1427 if (ConstantInt *CI = dyn_cast<ConstantInt>(C)) {
1428 if (CI->getValue() == 33) {
1444 llvm::errs() <<
"warning: cannot determine MPI op used in `" << call
1445 <<
"`, assuming MPI_SUM\n";
1448 llvm::raw_string_ostream ss(s);
1449 ss <<
" call: " << call <<
"\n";
1450 ss <<
" unhandled mpi_allreduce op: " << *orig_op <<
"\n";
1456 Value *shadow_recvbuf = gutils->
invertPointerM(orig_recvbuf, Builder2);
1458 shadow_recvbuf =
lookup(shadow_recvbuf, Builder2);
1459 if (shadow_recvbuf->getType()->isIntegerTy())
1460 shadow_recvbuf = Builder2.CreateIntToPtr(
1463 Value *shadow_sendbuf = gutils->
invertPointerM(orig_sendbuf, Builder2);
1465 shadow_sendbuf =
lookup(shadow_sendbuf, Builder2);
1466 if (shadow_sendbuf->getType()->isIntegerTy())
1467 shadow_sendbuf = Builder2.CreateIntToPtr(
1473 std::vector<ValueType> BufferBundleTypes(call.arg_size(),
1483 count =
lookup(count, Builder2);
1487 datatype =
lookup(datatype, Builder2);
1491 comm =
lookup(comm, Builder2);
1495 op =
lookup(op, Builder2);
1499 Type *i32Ty = Type::getInt32Ty(call.getContext());
1500 Value *countVal = fortranABI ? Builder2.CreateLoad(i32Ty, count) : count;
1508 Value *tysize =
MPI_TYPE_SIZE(datatype, Builder2, call.getType(), called);
1511 auto len_arg = Builder2.CreateZExtOrTrunc(
1512 countVal, Type::getInt64Ty(call.getContext()));
1514 Builder2.CreateMul(len_arg,
1515 Builder2.CreateZExtOrTrunc(
1516 tysize, Type::getInt64Ty(call.getContext())),
1522 len_arg,
"mpireduce_malloccache");
1530 SmallVector<Value *, 8> args = {
1540 .CreateAlloca(i32Ty,
nullptr,
"enzyme_mpi_ierr"));
1542 SmallVector<Type *, 8> types;
1543 for (
auto *arg : args)
1544 types.push_back(arg->getType());
1546 FunctionType *FT = FunctionType::get(
1547 fortranABI ? Type::getVoidTy(call.getContext()) : call.getType(),
1549 Builder2.CreateCall(
1550 called->getParent()->getOrInsertFunction(
1557 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
1558 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1559 Value *args[] = {shadow_recvbuf, val_arg, len_arg, volatile_arg};
1560 Type *tys[] = {args[0]->getType(), args[2]->getType()};
1561 auto memset = cast<CallInst>(Builder2.CreateCall(
1563 Intrinsic::memset, tys),
1565 memset->addParamAttr(0, Attribute::NonNull);
1569 len_arg, Builder2, BufferDefs);
1592 if (funcName ==
"MPI_Gather" || funcName ==
"PMPI_Gather") {
1600 IRBuilder<> Builder2 =
1601 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
1608 Value *orig_sendbuf = call.getOperand(0);
1609 Value *orig_sendcount = call.getOperand(1);
1610 Value *orig_sendtype = call.getOperand(2);
1611 Value *orig_recvbuf = call.getOperand(3);
1612 Value *orig_recvcount = call.getOperand(4);
1613 Value *orig_recvtype = call.getOperand(5);
1614 Value *orig_root = call.getOperand(6);
1615 Value *orig_comm = call.getOperand(7);
1617 Value *shadow_recvbuf = gutils->
invertPointerM(orig_recvbuf, Builder2);
1619 shadow_recvbuf =
lookup(shadow_recvbuf, Builder2);
1620 if (shadow_recvbuf->getType()->isIntegerTy())
1621 shadow_recvbuf = Builder2.CreateIntToPtr(
1624 Value *shadow_sendbuf = gutils->
invertPointerM(orig_sendbuf, Builder2);
1626 shadow_sendbuf =
lookup(shadow_sendbuf, Builder2);
1627 if (shadow_sendbuf->getType()->isIntegerTy())
1628 shadow_sendbuf = Builder2.CreateIntToPtr(
1633 recvcount =
lookup(recvcount, Builder2);
1637 recvtype =
lookup(recvtype, Builder2);
1641 sendcount =
lookup(sendcount, Builder2);
1645 sendtype =
lookup(sendtype, Builder2);
1651 root =
lookup(root, Builder2);
1655 comm =
lookup(comm, Builder2);
1657 Type *i32Ty = Type::getInt32Ty(call.getContext());
1658 Value *sendcountVal =
1659 fortranABI ? Builder2.CreateLoad(i32Ty, sendcount) : sendcount;
1660 Value *recvcountVal =
1661 fortranABI ? Builder2.CreateLoad(i32Ty, recvcount) : recvcount;
1662 Value *rootVal = fortranABI ? Builder2.CreateLoad(i32Ty, root) : root;
1664 Value *rank =
MPI_COMM_RANK(comm, Builder2, rootVal->getType(), called);
1665 Value *tysize =
MPI_TYPE_SIZE(sendtype, Builder2, call.getType(), called);
1674 auto sendlen_arg = Builder2.CreateZExtOrTrunc(
1675 sendcountVal, Type::getInt64Ty(call.getContext()));
1677 Builder2.CreateMul(sendlen_arg,
1678 Builder2.CreateZExtOrTrunc(
1679 tysize, Type::getInt64Ty(call.getContext())),
1685 std::vector<ValueType> BufferBundleTypes(call.arg_size(),
1695 sendlen_arg,
"mpireduce_malloccache");
1707 SmallVector<Value *, 10> args;
1709 args = {shadow_recvbuf, recvcount, recvtype, buf,
1710 sendcount, sendtype, root, comm};
1711 for (
size_t i = 8, e = call.arg_size(); i < e; i++)
1715 args = {shadow_recvbuf, recvcountVal, recvtype, buf,
1716 sendcountVal, sendtype, rootVal, comm};
1718 SmallVector<Type *, 10> types;
1719 for (
auto *arg : args)
1720 types.push_back(arg->getType());
1722 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
1723 Builder2.CreateCall(
1724 called->getParent()->getOrInsertFunction(
1732 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1734 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1736 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1738 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, rootVal), rootBlock,
1741 Builder2.SetInsertPoint(rootBlock);
1742 auto recvlen_arg = Builder2.CreateZExtOrTrunc(
1743 recvcountVal, Type::getInt64Ty(call.getContext()));
1745 Builder2.CreateMul(recvlen_arg,
1746 Builder2.CreateZExtOrTrunc(
1747 tysize, Type::getInt64Ty(call.getContext())),
1749 recvlen_arg = Builder2.CreateMul(
1751 Builder2.CreateZExtOrTrunc(
1753 Type::getInt64Ty(call.getContext())),
1756 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
1757 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1758 Value *args[] = {shadow_recvbuf, val_arg, recvlen_arg, volatile_arg};
1759 Type *tys[] = {args[0]->getType(), args[2]->getType()};
1760 auto memset = cast<CallInst>(Builder2.CreateCall(
1762 Intrinsic::memset, tys),
1764 memset->addParamAttr(0, Attribute::NonNull);
1766 Builder2.CreateBr(mergeBlock);
1767 Builder2.SetInsertPoint(mergeBlock);
1772 sendlen_arg, Builder2, BufferDefs);
1795 if (canonMPIName ==
"MPI_Scatter") {
1803 IRBuilder<> Builder2 =
1804 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
1811 Value *orig_sendbuf = call.getOperand(0);
1812 Value *orig_sendcount = call.getOperand(1);
1813 Value *orig_sendtype = call.getOperand(2);
1814 Value *orig_recvbuf = call.getOperand(3);
1815 Value *orig_recvcount = call.getOperand(4);
1816 Value *orig_recvtype = call.getOperand(5);
1817 Value *orig_root = call.getOperand(6);
1818 Value *orig_comm = call.getOperand(7);
1820 Value *shadow_recvbuf = gutils->
invertPointerM(orig_recvbuf, Builder2);
1822 shadow_recvbuf =
lookup(shadow_recvbuf, Builder2);
1823 if (shadow_recvbuf->getType()->isIntegerTy())
1824 shadow_recvbuf = Builder2.CreateIntToPtr(
1827 Value *shadow_sendbuf = gutils->
invertPointerM(orig_sendbuf, Builder2);
1829 shadow_sendbuf =
lookup(shadow_sendbuf, Builder2);
1830 if (shadow_sendbuf->getType()->isIntegerTy())
1831 shadow_sendbuf = Builder2.CreateIntToPtr(
1836 recvcount =
lookup(recvcount, Builder2);
1840 recvtype =
lookup(recvtype, Builder2);
1844 sendcount =
lookup(sendcount, Builder2);
1848 sendtype =
lookup(sendtype, Builder2);
1852 root =
lookup(root, Builder2);
1856 comm =
lookup(comm, Builder2);
1860 Type *i32Ty = Type::getInt32Ty(call.getContext());
1861 Value *sendcountVal =
1862 fortranABI ? Builder2.CreateLoad(i32Ty, sendcount) : sendcount;
1863 Value *recvcountVal =
1864 fortranABI ? Builder2.CreateLoad(i32Ty, recvcount) : recvcount;
1865 Value *rootVal = fortranABI ? Builder2.CreateLoad(i32Ty, root) : root;
1867 Value *rank =
MPI_COMM_RANK(comm, Builder2, rootVal->getType(), called);
1868 Value *tysize =
MPI_TYPE_SIZE(sendtype, Builder2, call.getType(), called);
1876 auto recvlen_arg = Builder2.CreateZExtOrTrunc(
1877 recvcountVal, Type::getInt64Ty(call.getContext()));
1879 Builder2.CreateMul(recvlen_arg,
1880 Builder2.CreateZExtOrTrunc(
1881 tysize, Type::getInt64Ty(call.getContext())),
1887 std::vector<ValueType> BufferBundleTypes(call.arg_size(),
1896 PHINode *sendlen_phi;
1899 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1901 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1903 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1905 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, rootVal), rootBlock,
1908 Builder2.SetInsertPoint(rootBlock);
1910 auto sendlen_arg = Builder2.CreateZExtOrTrunc(
1911 sendcountVal, Type::getInt64Ty(call.getContext()));
1913 Builder2.CreateMul(sendlen_arg,
1914 Builder2.CreateZExtOrTrunc(
1915 tysize, Type::getInt64Ty(call.getContext())),
1917 sendlen_arg = Builder2.CreateMul(
1919 Builder2.CreateZExtOrTrunc(
1921 Type::getInt64Ty(call.getContext())),
1926 sendlen_arg,
"mpireduce_malloccache");
1928 Builder2.CreateBr(mergeBlock);
1930 Builder2.SetInsertPoint(mergeBlock);
1932 buf = Builder2.CreatePHI(rootbuf->getType(), 2);
1933 buf->addIncoming(rootbuf, rootBlock);
1934 buf->addIncoming(UndefValue::get(buf->getType()), currentBlock);
1936 sendlen_phi = Builder2.CreatePHI(sendlen_arg->getType(), 2);
1937 sendlen_phi->addIncoming(sendlen_arg, rootBlock);
1938 sendlen_phi->addIncoming(UndefValue::get(sendlen_arg->getType()),
1952 SmallVector<Value *, 10> args;
1954 args = {shadow_recvbuf, recvcount, recvtype, buf,
1955 sendcount, sendtype, root, comm};
1956 for (
size_t i = 8, e = call.arg_size(); i < e; i++)
1960 args = {shadow_recvbuf, recvcountVal, recvtype, buf,
1961 sendcountVal, sendtype, rootVal, comm};
1963 SmallVector<Type *, 10> types;
1964 for (
auto *arg : args)
1965 types.push_back(arg->getType());
1967 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
1968 Builder2.CreateCall(
1969 called->getParent()->getOrInsertFunction(
1976 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
1977 auto volatile_arg = ConstantInt::getFalse(call.getContext());
1978 Value *args[] = {shadow_recvbuf, val_arg, recvlen_arg, volatile_arg};
1979 Type *tys[] = {args[0]->getType(), args[2]->getType()};
1980 auto memset = cast<CallInst>(Builder2.CreateCall(
1982 Intrinsic::memset, tys),
1984 memset->addParamAttr(0, Attribute::NonNull);
1991 BasicBlock *currentBlock = Builder2.GetInsertBlock();
1993 currentBlock, currentBlock->getName() +
"_root", gutils->
newFunc);
1995 rootBlock, currentBlock->getName() +
"_post", gutils->
newFunc);
1997 Builder2.CreateCondBr(Builder2.CreateICmpEQ(rank, rootVal), rootBlock,
2000 Builder2.SetInsertPoint(rootBlock);
2004 sendlen_phi, Builder2, BufferDefs);
2011 Builder2.CreateBr(mergeBlock);
2012 Builder2.SetInsertPoint(mergeBlock);
2033 if (funcName ==
"MPI_Allgather" || funcName ==
"PMPI_Allgather") {
2041 IRBuilder<> Builder2 =
2042 forwardMode ? IRBuilder<>(&call) : IRBuilder<>(call.getParent());
2049 Value *orig_sendbuf = call.getOperand(0);
2050 Value *orig_sendcount = call.getOperand(1);
2051 Value *orig_sendtype = call.getOperand(2);
2052 Value *orig_recvbuf = call.getOperand(3);
2053 Value *orig_recvcount = call.getOperand(4);
2054 Value *orig_recvtype = call.getOperand(5);
2055 Value *orig_comm = call.getOperand(6);
2057 Value *shadow_recvbuf = gutils->
invertPointerM(orig_recvbuf, Builder2);
2059 shadow_recvbuf =
lookup(shadow_recvbuf, Builder2);
2061 if (shadow_recvbuf->getType()->isIntegerTy())
2062 shadow_recvbuf = Builder2.CreateIntToPtr(
2065 Value *shadow_sendbuf = gutils->
invertPointerM(orig_sendbuf, Builder2);
2067 shadow_sendbuf =
lookup(shadow_sendbuf, Builder2);
2069 if (shadow_sendbuf->getType()->isIntegerTy())
2070 shadow_sendbuf = Builder2.CreateIntToPtr(
2075 recvcount =
lookup(recvcount, Builder2);
2079 recvtype =
lookup(recvtype, Builder2);
2083 sendcount =
lookup(sendcount, Builder2);
2087 sendtype =
lookup(sendtype, Builder2);
2091 comm =
lookup(comm, Builder2);
2093 Value *tysize =
MPI_TYPE_SIZE(sendtype, Builder2, call.getType(), called);
2101 auto sendlen_arg = Builder2.CreateZExtOrTrunc(
2102 sendcount, Type::getInt64Ty(call.getContext()));
2104 Builder2.CreateMul(sendlen_arg,
2105 Builder2.CreateZExtOrTrunc(
2106 tysize, Type::getInt64Ty(call.getContext())),
2120 sendlen_arg,
"mpireduce_malloccache");
2123 TR.
firstPointer(1, orig_sendbuf, &call, gutils, &Builder2);
2125 Type *MPI_OP_Ptr_type =
getUnqual(MPI_OP_type);
2143 MPI_OP_Ptr_type, MPI_OP_type, CT,
2144 call.getType(), Builder2),
2147 Type *types[
sizeof(args) /
sizeof(*args)];
2148 for (
size_t i = 0; i <
sizeof(args) /
sizeof(*args); i++)
2149 types[i] = args[i]->getType();
2151 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
2152 Builder2.CreateCall(
2153 called->getParent()->getOrInsertFunction(
2155 "MPI_Reduce_scatter_block"),
2162 auto recvlen_arg = Builder2.CreateZExtOrTrunc(
2163 recvcount, Type::getInt64Ty(call.getContext()));
2165 Builder2.CreateMul(recvlen_arg,
2166 Builder2.CreateZExtOrTrunc(
2167 tysize, Type::getInt64Ty(call.getContext())),
2169 recvlen_arg = Builder2.CreateMul(
2171 Builder2.CreateZExtOrTrunc(
2173 Type::getInt64Ty(call.getContext())),
2175 auto val_arg = ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
2176 auto volatile_arg = ConstantInt::getFalse(call.getContext());
2177 Value *args[] = {shadow_recvbuf, val_arg, recvlen_arg, volatile_arg};
2178 Type *tys[] = {args[0]->getType(), args[2]->getType()};
2179 auto memset = cast<CallInst>(Builder2.CreateCall(
2181 Intrinsic::memset, tys),
2183 memset->addParamAttr(0, Attribute::NonNull);
2188 sendlen_arg, Builder2, BufferDefs);
2202 if (funcName ==
"MPI_Barrier" || funcName ==
"PMPI_Barrier") {
2205 IRBuilder<> Builder2(&call);
2207 auto callval = call.getCalledOperand();
2210 SmallVector<Value *, 4> args;
2211 for (
unsigned i = 0; i < call.arg_size(); i++)
2214 Builder2.CreateCall(call.getFunctionType(), callval, args);
2223 if (funcName ==
"MPI_Comm_free" || funcName ==
"MPI_Comm_disconnect") {
2235 IRBuilder<> Builder2(&call);
2238 Value *args[] = {
lookup(call.getOperand(commFound->second), Builder2)};
2239 Type *types[] = {args[0]->getType()};
2241 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
2242 Builder2.CreateCall(
2243 called->getParent()->getOrInsertFunction(
2254 if (funcName ==
"MPI_Init" || funcName ==
"PMPI_Init" ||
2255 funcName ==
"MPI_Init_thread" || funcName ==
"PMPI_Init_thread" ||
2256 funcName ==
"MPI_Finalize" || funcName ==
"PMPI_Finalize" ||
2257 funcName ==
"MPI_Test" || funcName ==
"PMPI_Test" ||
2258 funcName ==
"MPI_Probe" || funcName ==
"PMPI_Probe") {
2262 IRBuilder<> Builder2(&call);
2264 SmallVector<Value *, 8> args;
2265 for (
unsigned i = 0; i < call.arg_size(); ++i) {
2269 Builder2.CreateCall(call.getFunctionType(), call.getCalledOperand(),
2275 IRBuilder<> Builder2(&call);
2277 SmallVector<Value *, 8> args;
2278 for (
unsigned i = 0; i < call.arg_size(); ++i) {
2281 Builder2.CreateCall(call.getFunctionType(), call.getCalledOperand(),
2289 llvm::errs() << *gutils->
oldFunc->getParent() <<
"\n";
2290 llvm::errs() << *gutils->
oldFunc <<
"\n";
2291 llvm::errs() << call <<
"\n";
2292 llvm::errs() << called <<
"\n";
2293 llvm_unreachable(
"Unhandled MPI FUNCTION");
2357 CallInst &call, Function *called, StringRef funcName,
2358 bool subsequent_calls_may_write,
const std::vector<bool> &overwritten_args,
2359 CallInst *
const newCall) {
2360 bool subretused =
false;
2361 bool shadowReturnUsed =
false;
2365 IRBuilder<> BuilderZ(newCall);
2366 BuilderZ.setFastMathFlags(
getFast());
2376 if (
startsWith(funcName,
"julia.atomicmodify.")) {
2382 auto finishPrimal = [&]() {
2383 bool primalNeededInReverse =
false;
2387 primalNeededInReverse = !found->second;
2389 if (!primalNeededInReverse &&
2394 std::map<UsageKey, bool> Seen =
2399 primalNeededInReverse =
2404 if (primalNeededInReverse) {
2418 auto emitCall = [&](Value *ptr,
unsigned valIdx, Value *val) -> Value * {
2419 SmallVector<Value *, 6> args;
2420 for (
size_t i = 0; i < call.arg_size(); i++) {
2422 args.push_back(ptr);
2423 else if (i == valIdx && val)
2424 args.push_back(val);
2428 auto CI = BuilderZ.CreateCall(call.getFunctionType(), called, args);
2429 CI->setCallingConv(call.getCallingConv());
2430 CI->setAttributes(call.getAttributes());
2431 CI->copyMetadata(*newCall);
2446 Type *elty = cast<StructType>(call.getType())->getElementType(0);
2447 unsigned opArgNo = 0;
2450 dyn_cast<Function>(call.getArgOperand(1)), opArgNo, FT);
2451 if (opKind != AtomicRMWInst::BAD_BINOP &&
2452 call.arg_size() - 3 != cast<Function>(call.getArgOperand(1))
2455 opKind = AtomicRMWInst::BAD_BINOP;
2457 unsigned valIdx = opArgNo + 3;
2458 Value *valOp = opKind != AtomicRMWInst::BAD_BINOP
2459 ? call.getArgOperand(valIdx)
2466 if (constval && constptr) {
2471 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
2472 auto storeSize = (DL.getTypeSizeInBits(elty) + 7) / 8;
2473 auto vd = TR.
firstPointer(storeSize, call.getArgOperand(0), &call, gutils,
2477 bool constargs =
true;
2478 for (
size_t i = 4; i < call.arg_size(); i++)
2499 bool forwardsShadow, backwardsShadow;
2507 (forwardsShadow || backwardsShadow)) ||
2510 Value *dptr = gutils->
invertPointerM(call.getArgOperand(0), BuilderZ);
2512 BuilderZ, [&](Value *dptr) { emitCall(dptr, 0,
nullptr); }, dptr);
2521 if (opKind != AtomicRMWInst::BAD_BINOP) {
2530 Value *dval =
nullptr;
2533 }
else if (opKind == AtomicRMWInst::Xchg && !constptr) {
2535 !isa<UndefValue>(valOp) && !isa<ConstantPointerNull>(valOp)) {
2537 raw_string_ostream ss(
str);
2538 ss <<
"Mismatched activity for: " << call
2539 <<
" const val: " << *valOp;
2543 gutils, wrap(valOp), wrap(&BuilderZ)));
2554 Constant::getNullValue(gutils->
getShadowType(valOp->getType()));
2556 auto rule = [&](Value *dptr, Value *dval) -> Value * {
2561 if (opKind == AtomicRMWInst::FSub) {
2562 dnew = BuilderZ.CreateBitCast(dnew, FT);
2563 dnew = BuilderZ.CreateFNeg(dnew);
2565 dnew = BuilderZ.CreateBitCast(dnew, elty);
2566 return BuilderZ.CreateInsertValue(
2567 Constant::getNullValue(call.getType()), dnew, {1});
2569 return emitCall(dptr, valIdx, dval);
2572 call.getType(), BuilderZ, rule,
2585 if (constval || opKind != AtomicRMWInst::BAD_BINOP) {
2594 (opKind == AtomicRMWInst::FAdd ||
2595 opKind == AtomicRMWInst::FSub)) {
2597 auto order =
static_cast<AtomicOrdering
>(
2598 cast<ConstantInt>(call.getArgOperand(2))->getZExtValue());
2599 auto ssid =
static_cast<SyncScope::ID
>(
2600 cast<ConstantInt>(call.getArgOperand(3))->getZExtValue());
2601 auto align = call.getParamAlign(0).value_or(DL.getABITypeAlign(elty));
2603 opKind == AtomicRMWInst::FSub,
2604 align, order, ssid,
false);
2613 llvm::raw_string_ostream ss(s);
2614 ss << *gutils->
oldFunc <<
"\n" << call <<
"\n";
2615 ss <<
" Active atomic modify not yet handled";
2622 rval = Constant::getNullValue(gutils->
getShadowType(call.getType()));
2628 if (!call.getType()->isVoidTy()) {
2631 U.set(UndefValue::get(call.getType()));
2639 if (funcName ==
"__kmpc_for_static_init_4" ||
2640 funcName ==
"__kmpc_for_static_init_4u" ||
2641 funcName ==
"__kmpc_for_static_init_8" ||
2642 funcName ==
"__kmpc_for_static_init_8u") {
2643 IRBuilder<> Builder2(&call);
2645 auto fini = called->getParent()->getFunction(
"__kmpc_for_static_fini");
2650 auto fcall = Builder2.CreateCall(fini->getFunctionType(), fini, args);
2651 fcall->setCallingConv(fini->getCallingConv());
2661 if (!canonMPIName.empty() &&
2663 canonMPIName ==
"MPI_Comm_free" ||
2664 canonMPIName ==
"MPI_Comm_disconnect" || canonMPIName ==
"MPI_Init" ||
2665 canonMPIName ==
"MPI_Init_thread" || canonMPIName ==
"MPI_Finalize" ||
2666 canonMPIName ==
"MPI_Test" || canonMPIName ==
"MPI_Probe" ||
2674 if (handleBLAS(call, called, *blas, overwritten_args))
2678 if (funcName ==
"printf" || funcName ==
"puts" ||
2679 startsWith(funcName,
"_ZN3std2io5stdio6_print") ||
2686 if (called && (called->getName().contains(
"__enzyme_float") ||
2687 called->getName().contains(
"__enzyme_double") ||
2688 called->getName().contains(
"__enzyme_integer") ||
2689 called->getName().contains(
"__enzyme_pointer"))) {
2696 if (funcName ==
"__kmpc_for_static_init_4" ||
2697 funcName ==
"__kmpc_for_static_init_4u" ||
2698 funcName ==
"__kmpc_for_static_init_8" ||
2699 funcName ==
"__kmpc_for_static_init_8u") {
2701 IRBuilder<> Builder2(&call);
2703 auto fini = called->getParent()->getFunction(
"__kmpc_for_static_fini");
2709 auto fcall = Builder2.CreateCall(fini->getFunctionType(), fini, args);
2710 fcall->setCallingConv(fini->getCallingConv());
2714 if (funcName ==
"__kmpc_for_static_fini") {
2723 if (funcName ==
"__kmpc_barrier") {
2726 IRBuilder<> Builder2(&call);
2728 auto callval = call.getCalledOperand();
2732 Builder2.CreateCall(call.getFunctionType(), callval, args);
2736 if (funcName ==
"__kmpc_critical") {
2738 IRBuilder<> Builder2(&call);
2740 auto crit2 = called->getParent()->getFunction(
"__kmpc_end_critical");
2747 auto fcall = Builder2.CreateCall(crit2->getFunctionType(), crit2, args);
2748 fcall->setCallingConv(crit2->getCallingConv());
2752 if (funcName ==
"__kmpc_end_critical") {
2754 IRBuilder<> Builder2(&call);
2756 auto crit2 = called->getParent()->getFunction(
"__kmpc_critical");
2763 auto fcall = Builder2.CreateCall(crit2->getFunctionType(), crit2, args);
2764 fcall->setCallingConv(crit2->getCallingConv());
2770 funcName !=
"__kmpc_global_thread_num") {
2772 llvm::raw_string_ostream ss(s);
2773 ss <<
" unhandled openmp function: " << call <<
"\n";
2778 auto mod = call.getParent()->getParent()->getParent();
2779#include "CallDerivatives.inc"
2781 if (funcName ==
"llvm.julia.gc_preserve_end") {
2785 auto begin_call = cast<CallInst>(call.getOperand(0));
2787 IRBuilder<> Builder2(&call);
2792 llvm::raw_string_ostream ss(s);
2793 ss <<
"cannot reverse gc preserve region with several ends: "
2794 << *begin_call <<
"\n";
2798 SmallVector<Value *, 1> args;
2799 for (
auto &arg : begin_call->args()) {
2800 bool primalUsed =
false;
2801 bool shadowUsed =
false;
2809 Value *ptrshadow = gutils->
lookupM(
2812 args.push_back(ptrshadow);
2814 for (
size_t i = 0; i < gutils->
getWidth(); ++i)
2815 args.push_back(gutils->
extractMeta(Builder2, ptrshadow, i));
2819 auto newp = Builder2.CreateCall(
2820 called->getParent()->getOrInsertFunction(
2821 "llvm.julia.gc_preserve_begin",
2822 FunctionType::get(Type::getTokenTy(call.getContext()),
2823 ArrayRef<Type *>(),
true)),
2827 auto placeholder = cast<CallInst>(&*ifound->second);
2833 gutils->
erase(placeholder);
2837 if (funcName ==
"llvm.julia.gc_preserve_begin") {
2838 SmallVector<Value *, 1> args;
2839 for (
auto &arg : call.args()) {
2840 bool primalUsed =
false;
2841 bool shadowUsed =
false;
2850 args.push_back(ptrshadow);
2852 for (
size_t i = 0; i < gutils->
getWidth(); ++i)
2853 args.push_back(gutils->
extractMeta(BuilderZ, ptrshadow, i));
2857 auto newp = BuilderZ.CreateCall(called, args);
2860 gutils->
erase(oldp);
2864 IRBuilder<> Builder2(&call);
2869 auto placeholder = cast<CallInst>(&*ifound->second);
2872 gutils->
erase(placeholder);
2875 Builder2.CreateCall(
2876 called->getParent()->getOrInsertFunction(
2877 "llvm.julia.gc_preserve_end",
2878 FunctionType::get(Builder2.getVoidTy(), call.getType(),
false)),
2888 if (funcName ==
"_FortranAAssign" && call.arg_size() == 4) {
2892 IRBuilder<> Builder2(&call);
2902 auto rule = [&](Value *sTo, Value *sFrom) {
2903 auto dcall = Builder2.CreateCall(called->getFunctionType(), called,
2904 {sTo, sFrom, file, line});
2920 if (funcName ==
"_FortranAEtime" && call.arg_size() == 4 &&
2927 auto &DL = gutils->
newFunc->getParent()->getDataLayout();
2929 Type *IdxTy = DL.getIntPtrType(call.getContext());
2930 unsigned P = DL.getPointerSize();
2933 unsigned DimOff = 2 * P + 8;
2937 auto shadowElements = [&](IRBuilder<> &B) {
2938 auto field = [&](Value *desc, Type *T,
unsigned off) {
2939 Value *p = B.CreatePointerCast(desc, I8PtrTy);
2940 p = B.CreateConstInBoundsGEP1_64(B.getInt8Ty(), p, off);
2941 return B.CreateLoad(T, B.CreatePointerCast(p,
getUnqual(T)));
2943 SmallVector<Value *, 6> elems;
2944 for (
unsigned i = 0; i < 2; ++i) {
2945 Value *orig = call.getArgOperand(i);
2950 Value *base = field(desc, I8PtrTy, 0);
2951 for (
unsigned w = 0; w < gutils->
getWidth(); ++w) {
2952 Value *sdesc = gutils->
getWidth() == 1
2955 Value *sbase = field(sdesc, I8PtrTy, 0);
2956 Value *notPrimal = B.CreateICmpNE(sbase, base);
2957 Value *notNull = B.CreateIsNotNull(sbase);
2958 Value *valid = B.CreateAnd(notPrimal, notNull);
2959 auto add = [&](Value *cond, Value *addr) {
2961 B.CreateSelect(cond, addr, Constant::getNullValue(I8PtrTy)));
2967 Value *extent = field(desc, IdxTy, DimOff + P);
2968 Value *sm = field(desc, IdxTy, DimOff + 2 * P);
2969 Value *has1 = B.CreateICmpSGE(extent, ConstantInt::get(IdxTy, 1));
2970 add(B.CreateAnd(valid, has1), sbase);
2971 Value *has2 = B.CreateICmpSGE(extent, ConstantInt::get(IdxTy, 2));
2972 Value *valid2 = B.CreateAnd(valid, has2);
2973 add(valid2, B.CreateInBoundsGEP(B.getInt8Ty(), sbase, sm));
2978 auto zero = [&](IRBuilder<> &B, Value *addr) {
2979 auto VT = FixedVectorType::get(B.getFloatTy(), 1);
2980 Value *ptr = B.CreatePointerCast(addr,
getUnqual(VT));
2981 Value *notNull = B.CreateIsNotNull(addr);
2982 Value *mask = B.CreateVectorSplat(1, notNull);
2983 B.CreateMaskedStore(Constant::getNullValue(VT), ptr, Align(4), mask);
2987 IRBuilder<> B(newCall->getNextNode());
2988 for (
auto addr : shadowElements(B))
2994 for (
unsigned i = 0; i < 2; ++i)
2996 N += (i == 0 ? 2 : 1) * gutils->
getWidth();
2997 Type *TapeTy = ArrayType::get(I8PtrTy, N);
3000 tape = BuilderZ.CreatePHI(TapeTy, 0);
3002 tape = UndefValue::get(TapeTy);
3003 auto elems = shadowElements(BuilderZ);
3004 for (
unsigned i = 0; i < N; ++i)
3005 tape = BuilderZ.CreateInsertValue(tape, elems[i], i);
3006 if (
auto I = dyn_cast<Instruction>(tape))
3014 IRBuilder<> Builder2(&call);
3016 tape =
lookup(tape, Builder2);
3017 for (
unsigned i = 0; i < N; ++i)
3018 zero(Builder2, Builder2.CreateExtractValue(tape, i));
3033 if (funcName ==
"gsl_sf_legendre_array_e") {
3044 IRBuilder<> Builder2(&call);
3053 call.getOperand(0)->getType(), call.getOperand(1)->getType(),
3054 call.getOperand(2)->getType(), call.getOperand(3)->getType(),
3055 call.getOperand(4)->getType(), call.getOperand(4)->getType(),
3057 FunctionType *FT = FunctionType::get(call.getType(), types,
false);
3059 "gsl_sf_legendre_deriv_array_e", FT);
3061 llvm::Value *args[6] = {
3073 Type *typesS[] = {args[1]->getType()};
3075 FunctionType::get(args[1]->getType(), typesS,
false);
3077 "gsl_sf_legendre_array_n", FTS);
3078 Value *alSize = Builder2.CreateCall(FS, args[1]);
3081 Builder2.CreateLifetimeStart(tmp);
3082 Builder2.CreateLifetimeStart(dtmp);
3084 args[4] = Builder2.CreateBitCast(tmp, types[4]);
3085 args[5] = Builder2.CreateBitCast(dtmp, types[5]);
3087 Builder2.CreateCall(F, args, Defs);
3088 Builder2.CreateLifetimeEnd(tmp);
3091 BasicBlock *currentBlock = Builder2.GetInsertBlock();
3094 currentBlock, currentBlock->getName() +
"_loop");
3095 BasicBlock *endBlock =
3099 Builder2.CreateCondBr(
3100 Builder2.CreateICmpEQ(args[1], Constant::getNullValue(types[1])),
3101 endBlock, loopBlock);
3102 Builder2.SetInsertPoint(loopBlock);
3104 auto idx = Builder2.CreatePHI(types[1], 2);
3105 idx->addIncoming(ConstantInt::get(types[1], 0,
false), currentBlock);
3107 auto acc_idx = Builder2.CreatePHI(types[2], 2);
3109 Value *inc = Builder2.CreateAdd(
3110 idx, ConstantInt::get(types[1], 1,
false),
"",
true,
true);
3111 idx->addIncoming(inc, loopBlock);
3112 acc_idx->addIncoming(Constant::getNullValue(types[2]), currentBlock);
3114 Value *idxs[] = {idx};
3115 Value *dtmp_idx = Builder2.CreateInBoundsGEP(types[2], dtmp, idxs);
3116 Value *d_req = Builder2.CreateInBoundsGEP(
3118 Builder2.CreatePointerCast(
3123 auto l0 = Builder2.CreateLoad(types[2], dtmp_idx);
3124 auto l1 = Builder2.CreateLoad(types[2], d_req);
3125 auto acc = Builder2.CreateFAdd(acc_idx, Builder2.CreateFMul(l0, l1));
3126 Builder2.CreateStore(Constant::getNullValue(types[2]), d_req);
3128 acc_idx->addIncoming(acc, loopBlock);
3130 Builder2.CreateCondBr(Builder2.CreateICmpEQ(inc, args[1]), endBlock,
3133 Builder2.SetInsertPoint(endBlock);
3137 SmallVector<BasicBlock *, 4> &vec =
3140 vec.push_back(endBlock);
3143 auto fin_idx = Builder2.CreatePHI(types[2], 2);
3144 fin_idx->addIncoming(Constant::getNullValue(types[2]), currentBlock);
3145 fin_idx->addIncoming(acc, loopBlock);
3147 Builder2.CreateLifetimeEnd(dtmp);
3151 ->addToDiffe(call.getOperand(2), fin_idx, Builder2, types[2]);
3159 if (funcName ==
"_ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_"
3160 "node_baseS0_RS_") {
3167 SmallVector<Value *, 2> args;
3168 for (
auto &arg : call.args()) {
3174 BuilderZ.CreateCall(called, args);
3180 if (funcName ==
"_ZNSt8ios_baseC2Ev" || funcName ==
"_ZNSt8ios_baseD2Ev" ||
3181 funcName ==
"_ZNSt6localeC1Ev" || funcName ==
"_ZNSt6localeD1Ev" ||
3182 funcName ==
"_ZNKSt5ctypeIcE13_M_widen_initEv") {
3190 Value *args[] = {gutils->
invertPointerM(call.getArgOperand(0), BuilderZ)};
3191 BuilderZ.CreateCall(called, args);
3195 if (funcName ==
"_ZNSt9basic_iosIcSt11char_traitsIcEE4initEPSt15basic_"
3196 "streambufIcS1_E") {
3204 Value *args[] = {gutils->
invertPointerM(call.getArgOperand(0), BuilderZ),
3206 BuilderZ.CreateCall(called, args);
3212 if (funcName ==
"__dynamic_cast" ||
3213 funcName ==
"_ZSt18_Rb_tree_decrementPKSt18_Rb_tree_node_base" ||
3214 funcName ==
"_ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base" ||
3215 funcName ==
"_ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base" ||
3216 funcName ==
"_ZSt18_Rb_tree_incrementPSt18_Rb_tree_node_base" ||
3217 funcName ==
"jl_ptr_to_array" || funcName ==
"jl_ptr_to_array_1d") {
3218 bool shouldCache =
false;
3225 ValueToValueMapTy empty;
3231 auto placeholder = cast<PHINode>(&*ifound->second);
3234 Value *shadow = placeholder;
3242 SmallVector<Value *, 2> args;
3244 for (
auto &arg : call.args()) {
3246 (funcName ==
"__dynamic_cast" && i > 0) ||
3247 (funcName ==
"jl_ptr_to_array_1d" && i != 1) ||
3248 (funcName ==
"jl_ptr_to_array" && i != 1))
3254 shadow = BuilderZ.CreateCall(called, args);
3258 bool needsReplacement =
true;
3264 needsReplacement =
false;
3269 if (needsReplacement) {
3270 assert(shadow != placeholder);
3272 gutils->
erase(placeholder);
3276 gutils->
erase(placeholder);
3287 if (!shouldCache && !lrc) {
3288 std::map<UsageKey, bool> Seen =
3290 bool primalNeededInReverse =
3294 auto found = gutils->knownRecomputeHeuristic.find(&call);
3295 if (found != gutils->knownRecomputeHeuristic.end()) {
3296 if (!found->second) {
3297 primalNeededInReverse =
true;
3301 shouldCache = primalNeededInReverse;
3305 BuilderZ.SetInsertPoint(newCall->getNextNode());
3315 if (funcName ==
"julia.write_barrier" ||
3316 funcName ==
"julia.write_barrier_binding") {
3317 std::map<UsageKey, bool> Seen =
3319 bool backwardsShadow =
false;
3320 bool forwardsShadow =
true;
3322 if (pair.second.stores.count(&call)) {
3323 backwardsShadow =
true;
3324 forwardsShadow = pair.second.primalInitialize;
3325 if (
auto inst = dyn_cast<Instruction>(pair.first))
3326 if (!forwardsShadow && pair.second.LI &&
3327 pair.second.LI->contains(inst->getParent()))
3328 backwardsShadow =
false;
3336 (forwardsShadow || backwardsShadow)) ||
3340 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
3341 SmallVector<Value *, 1> iargs;
3343 for (
auto &arg : call.args()) {
3347 ptrshadow = gutils->
extractMeta(BuilderZ, ptrshadow, i);
3349 iargs.push_back(ptrshadow);
3357 BuilderZ.CreateCall(called, iargs);
3362 bool forceErase =
false;
3369 if (!pair.second.stores.count(&call))
3376 if (!pair.second.LI || !pair.second.LI->contains(&call)) {
3388 Intrinsic::ID ID = Intrinsic::not_intrinsic;
3405 if (ID != Intrinsic::not_intrinsic) {
3406 SmallVector<Value *, 2> orig_ops(call.getNumOperands());
3407 for (
unsigned i = 0; i < call.getNumOperands(); ++i) {
3408 orig_ops[i] = call.getOperand(i);
3427 if (
auto assembly = dyn_cast<InlineAsm>(call.getCalledOperand())) {
3428 if (assembly->getAsmString() ==
"maxpd $1, $0") {
3443 SmallVector<Value *, 2> orig_ops(call.getNumOperands());
3444 for (
unsigned i = 0; i < call.getNumOperands(); ++i) {
3445 orig_ops[i] = call.getOperand(i);
3460 if (funcName ==
"realloc") {
3464 IRBuilder<> Builder2(&call);
3469 auto rule = [&](Value *ip) {
3475 llvm::Value *args[2] = {
3477 CallInst *CI = Builder2.CreateCall(
3478 call.getFunctionType(), call.getCalledFunction(), args, Defs);
3479 CI->setAttributes(call.getAttributes());
3480 CI->setCallingConv(call.getCallingConv());
3481 CI->setTailCallKind(call.getTailCallKind());
3482 CI->setDebugLoc(dbgLoc);
3487 call.getType(), Builder2, rule,
3491 PHINode *placeholder = cast<PHINode>(&*found->second);
3495 gutils->
erase(placeholder);
3511 PHINode *placeholder = cast<PHINode>(&*found->second);
3512 IRBuilder<> bb(placeholder);
3514 SmallVector<Value *, 8> args;
3515 for (
auto &arg : call.args()) {
3524 Value *anti = placeholder;
3531 bool backwardsShadow =
false;
3532 bool forwardsShadow =
true;
3533 bool inLoop =
false;
3534 bool isAlloca = isa<AllocaInst>(&call);
3538 backwardsShadow =
true;
3539 forwardsShadow = found->second.primalInitialize;
3541 if (found->second.LI &&
3542 found->second.LI->contains(call.getParent()))
3548 if (!forwardsShadow) {
3552 *gutils->
oldFunc->getParent(), placeholder->getType());
3557 gutils->
erase(placeholder);
3560 }
else if (inLoop) {
3568 placeholder->setName(
"");
3570 bb.SetInsertPoint(placeholder);
3577 return shadowHandlers[funcName](bb, &call, args, gutils);
3579 if (anti->getType() != placeholder->getType()) {
3580 llvm::errs() <<
"orig: " << call <<
"\n";
3581 llvm::errs() <<
"placeholder: " << *placeholder <<
"\n";
3582 llvm::errs() <<
"anti: " << *anti <<
"\n";
3585 bb.SetInsertPoint(placeholder);
3588 gutils->
erase(placeholder);
3591 if (
auto inst = dyn_cast<Instruction>(anti))
3592 bb.SetInsertPoint(inst);
3594 if (!backwardsShadow)
3598 bool zeroed =
false;
3600 Value *prev =
nullptr;
3604 bb.CreateCall(call.getFunctionType(), call.getCalledOperand(),
3605 args, call.getName() +
"'mi");
3606 cast<CallInst>(anti)->setAttributes(call.getAttributes());
3607 cast<CallInst>(anti)->setCallingConv(call.getCallingConv());
3608 cast<CallInst>(anti)->setTailCallKind(call.getTailCallKind());
3609 cast<CallInst>(anti)->setDebugLoc(dbgLoc);
3611 if (anti->getType()->isPointerTy()) {
3612 cast<CallInst>(anti)->addAttributeAtIndex(
3613 AttributeList::ReturnIndex, Attribute::NoAlias);
3614 cast<CallInst>(anti)->addAttributeAtIndex(
3615 AttributeList::ReturnIndex, Attribute::NonNull);
3617 if (funcName ==
"malloc" || funcName ==
"_Znwm" ||
3618 funcName ==
"??2@YAPAXI@Z" ||
3619 funcName ==
"??2@YAPEAX_K@Z") {
3620 if (
auto ci = dyn_cast<ConstantInt>(args[0])) {
3621 unsigned derefBytes = ci->getLimitedValue();
3624 cast<CallInst>(anti)->addDereferenceableRetAttr(derefBytes);
3625 cal->addDereferenceableRetAttr(derefBytes);
3626#if !defined(FLANG) && !defined(ROCM)
3627 AttrBuilder B(ci->getContext());
3631 B.addDereferenceableOrNullAttr(derefBytes);
3632 cast<CallInst>(anti)->setAttributes(
3633 cast<CallInst>(anti)->getAttributes().addRetAttributes(
3634 call.getContext(), B));
3635 cal->setAttributes(cal->getAttributes().addRetAttributes(
3636 call.getContext(), B));
3637 cal->addAttributeAtIndex(AttributeList::ReturnIndex,
3638 Attribute::NoAlias);
3639 cal->addAttributeAtIndex(AttributeList::ReturnIndex,
3640 Attribute::NonNull);
3643 if (funcName ==
"julia.gc_alloc_obj" ||
3644 funcName ==
"jl_gc_alloc_typed" ||
3645 funcName ==
"ijl_gc_alloc_typed") {
3647 bool used = unnecessaryInstructions.find(&call) ==
3648 unnecessaryInstructions.end();
3650 idx, wrap(prev), used);
3675 if (&*bb.GetInsertPoint() == placeholder)
3676 bb.SetInsertPoint(placeholder->getNextNode());
3678 gutils->
erase(placeholder);
3680 if (!backwardsShadow)
3684 if (
auto MD =
hasMetadata(&call,
"enzyme_fromstack")) {
3686 bb.SetInsertPoint(cast<Instruction>(anti));
3688 if (funcName ==
"malloc")
3690 else if (funcName ==
"julia.gc_alloc_obj" ||
3691 funcName ==
"jl_gc_alloc_typed" ||
3692 funcName ==
"ijl_gc_alloc_typed")
3695 llvm_unreachable(
"Unknown allocation to upgrade");
3697 Type *elTy = Type::getInt8Ty(call.getContext());
3698 if (MD->getNumOperands() == 2) {
3699 elTy = (Type *)cast<ConstantInt>(
3700 cast<ConstantAsMetadata>(MD->getOperand(1))
3702 ->getLimitedValue();
3703 Value *tsize = ConstantInt::get(
3704 Size->getType(), (gutils->
newFunc->getParent()
3706 .getTypeAllocSizeInBits(elTy) +
3710 Size = bb.CreateUDiv(Size, tsize,
"",
true);
3712 std::string name =
"";
3713#if LLVM_VERSION_MAJOR < 17
3714 if (call.getContext().supportsTypedPointers()) {
3715 for (
auto U : call.users()) {
3716 if (
hasMetadata(cast<Instruction>(U),
"enzyme_caststack")) {
3717 if (MD->getNumOperands() == 1) {
3718 elTy = U->getType()->getPointerElementType();
3719 Value *tsize = ConstantInt::get(
3723 .getTypeAllocSizeInBits(elTy) +
3727 Size = bb.CreateUDiv(Size, tsize,
"",
true);
3729 name = (U->getName() +
"'ai").str();
3735 auto rule = [&](Value *anti) {
3736 bb.SetInsertPoint(cast<Instruction>(anti));
3737 Value *replacement = bb.CreateAlloca(elTy, Size, name);
3738 if (name.size() == 0)
3739 replacement->takeName(anti);
3742 auto Alignment = cast<ConstantInt>(cast<ConstantAsMetadata>(
3745 ->getLimitedValue();
3747 cast<AllocaInst>(replacement)
3748 ->setAlignment(Align(Alignment));
3750#if LLVM_VERSION_MAJOR < 17
3751 if (call.getContext().supportsTypedPointers()) {
3752 if (anti->getType()->getPointerElementType() != elTy)
3753 replacement = bb.CreatePointerCast(
3755 getUnqual(anti->getType()->getPointerElementType()));
3758 auto PT = cast<PointerType>(anti->getType());
3759 if (PT->getAddressSpace()) {
3760 replacement = bb.CreateAddrSpaceCast(replacement, PT);
3761 cast<Instruction>(replacement)
3764 MDNode::get(replacement->getContext(), {}));
3766 gutils->
replaceAWithB(cast<Instruction>(anti), replacement);
3767 bb.SetInsertPoint(cast<Instruction>(anti)->getNextNode());
3768 gutils->
erase(cast<Instruction>(anti));
3796 IRBuilder<> Builder2(&call);
3799 Value *tofree =
lookup(anti, Builder2);
3801 assert(tofree->getType());
3802 for (
size_t i = 0; i < gutils->
getWidth(); i++) {
3809 gutils->
TLI, &call, gutils);
3811 CI->addAttributeAtIndex(AttributeList::FirstArgIndex,
3812 Attribute::NonNull);
3814 auto ident = MDNode::getDistinct(
3816 {ConstantAsMetadata::get(
3817 combined ? ConstantInt::getTrue(CI->getContext())
3818 : ConstantInt::getFalse(CI->getContext()))});
3823 cast<Instruction>(anti_i)->setMetadata(
3824 "enzyme_cache_alloc", MDNode::get(CI->getContext(), {ident}));
3825 CI->setMetadata(
"enzyme_cache_free",
3826 MDNode::get(CI->getContext(), {ident}));
3832 IRBuilder<> Builder2(&call);
3835 SmallVector<Value *, 2> args;
3836 for (
unsigned i = 0; i < call.arg_size(); ++i) {
3837 auto arg = call.getArgOperand(i);
3849 CallInst *CI = Builder2.CreateCall(
3850 call.getFunctionType(), call.getCalledFunction(), args, Defs);
3851 CI->setAttributes(call.getAttributes());
3852 CI->setCallingConv(call.getCallingConv());
3853 CI->setTailCallKind(call.getTailCallKind());
3854 CI->setDebugLoc(dbgLoc);
3856 if (funcName ==
"julia.gc_alloc_obj" ||
3857 funcName ==
"jl_gc_alloc_typed" ||
3858 funcName ==
"ijl_gc_alloc_typed") {
3860 bool used = unnecessaryInstructions.find(&call) ==
3861 unnecessaryInstructions.end();
3874 PHINode *placeholder = cast<PHINode>(&*found->second);
3878 gutils->
erase(placeholder);
3892 bool primalNeededInReverse =
3902 auto found = gutils->knownRecomputeHeuristic.find(&call);
3903 if (found != gutils->knownRecomputeHeuristic.end() && !found->second) {
3904 primalNeededInReverse =
true;
3907 bool cacheWholeAllocation = gutils->needsCacheWholeAllocation(&call);
3908 if (cacheWholeAllocation) {
3909 primalNeededInReverse =
true;
3912 auto restoreFromStack = [&](MDNode *MD) {
3913 IRBuilder<> B(newCall);
3915 if (funcName ==
"malloc")
3916 Size = call.getArgOperand(0);
3917 else if (funcName ==
"julia.gc_alloc_obj" ||
3918 funcName ==
"jl_gc_alloc_typed" ||
3919 funcName ==
"ijl_gc_alloc_typed")
3920 Size = call.getArgOperand(1);
3922 llvm_unreachable(
"Unknown allocation to upgrade");
3923 Size = gutils->getNewFromOriginal(Size);
3925 if (isa<ConstantInt>(Size)) {
3926 B.SetInsertPoint(gutils->inversionAllocs);
3928 Type *elTy = Type::getInt8Ty(call.getContext());
3929 if (MD->getNumOperands() == 2) {
3930 elTy = (Type *)cast<ConstantInt>(
3931 cast<ConstantAsMetadata>(MD->getOperand(1))->getValue())
3932 ->getLimitedValue();
3933 Value *tsize = ConstantInt::get(Size->getType(),
3934 (gutils->newFunc->getParent()
3936 .getTypeAllocSizeInBits(elTy) +
3939 Size = B.CreateUDiv(Size, tsize,
"",
true);
3941 Instruction *I =
nullptr;
3942#if LLVM_VERSION_MAJOR < 17
3943 if (call.getContext().supportsTypedPointers()) {
3944 for (
auto U : call.users()) {
3945 if (
hasMetadata(cast<Instruction>(U),
"enzyme_caststack")) {
3946 if (MD->getNumOperands() == 1) {
3947 elTy = U->getType()->getPointerElementType();
3948 Value *tsize = ConstantInt::get(
3949 Size->getType(), (gutils->newFunc->getParent()
3951 .getTypeAllocSizeInBits(elTy) +
3955 Size = B.CreateUDiv(Size, tsize,
"",
true);
3957 I = gutils->getNewFromOriginal(cast<Instruction>(U));
3963 Value *replacement = B.CreateAlloca(elTy, Size);
3964 for (
auto MD : {
"enzyme_active",
"enzyme_inactive",
"enzyme_type",
3965 "enzymejl_allocart",
"enzymejl_allocart_name",
3966 "enzymejl_gc_alloc_rt"})
3967 if (
auto M = call.getMetadata(MD))
3968 cast<AllocaInst>(replacement)->setMetadata(MD, M);
3970 replacement->takeName(I);
3972 replacement->takeName(newCall);
3975 cast<ConstantAsMetadata>(MD->getOperand(0))->getValue())
3976 ->getLimitedValue();
3979 cast<AllocaInst>(replacement)->setAlignment(Align(Alignment));
3981#if LLVM_VERSION_MAJOR < 17
3982 if (call.getContext().supportsTypedPointers()) {
3983 if (call.getType()->getPointerElementType() != elTy)
3984 replacement = B.CreatePointerCast(
3985 replacement,
getUnqual(call.getType()->getPointerElementType()));
3988 auto PT = cast<PointerType>(call.getType());
3989 if (PT->getAddressSpace()) {
3990 replacement = B.CreateAddrSpaceCast(replacement, PT);
3991 cast<Instruction>(replacement)
3992 ->setMetadata(
"enzyme_backstack",
3993 MDNode::get(replacement->getContext(), {}));
3995 gutils->replaceAWithB(newCall, replacement);
3996 gutils->erase(newCall);
4001 auto found = gutils->rematerializableAllocations.find(&call);
4002 if (found != gutils->rematerializableAllocations.end()) {
4005 if (gutils->allocationsToBeRematerialized.count(&call)) {
4006 assert(!unnecessaryValues.count(&call));
4015 auto AllocationLoop = gutils->OrigLI->getLoopFor(call.getParent());
4019 assert(found->second.LI);
4020 if (
auto MD =
hasMetadata(&call,
"enzyme_fromstack")) {
4022 gutils->rematerializedPrimalOrShadowAllocations.push_back(
4025 restoreFromStack(MD);
4032 cast<PointerType>(call.getType())->getAddressSpace() == 10) {
4034 gutils->rematerializedPrimalOrShadowAllocations.push_back(
4043 IRBuilder<> Builder2(&call);
4045 auto dbgLoc = gutils->getNewFromOriginal(call.getDebugLoc());
4047 Builder2,
lookup(newCall, Builder2), funcName, dbgLoc,
4048 gutils->TLI, &call, gutils);
4050 auto ident = MDNode::getDistinct(
4051 freecall->getContext(),
4052 {ConstantAsMetadata::get(
4053 ConstantInt::getTrue(freecall->getContext()))});
4054 newCall->setMetadata(
4055 "enzyme_cache_alloc",
4056 MDNode::get(freecall->getContext(), {ident}));
4057 freecall->setMetadata(
4058 "enzyme_cache_free",
4059 MDNode::get(freecall->getContext(), {ident}));
4062 gutils->rematerializedPrimalOrShadowAllocations.push_back(
4069 }
else if (!cacheWholeAllocation) {
4070 if (unnecessaryValues.count(&call)) {
4079 else if (
auto MD =
hasMetadata(&call,
"enzyme_fromstack")) {
4080 restoreFromStack(MD);
4091 bool hasPDFree = gutils->allocationsWithGuaranteedFree.count(&call);
4092 if (!primalNeededInReverse && hasPDFree) {
4093 if (unnecessaryValues.count(&call)) {
4101 if (
auto MD =
hasMetadata(&call,
"enzyme_fromstack")) {
4102 restoreFromStack(MD);
4111 cast<PointerType>(call.getType())->getAddressSpace() == 10) {
4116 if (!primalNeededInReverse) {
4119 auto pn = BuilderZ.CreatePHI(call.getType(), 1,
4120 call.getName() +
"_replacementJ");
4121 gutils->fictiousPHIs[pn] = &call;
4122 gutils->replaceAWithB(newCall, pn);
4123 gutils->erase(newCall);
4126 gutils->cacheForReverse(BuilderZ, newCall,
4138 if ((primalNeededInReverse &&
4139 !gutils->unnecessaryIntermediates.count(&call)) ||
4143 MDNode::getDistinct(newCall->getContext(),
4144 {ConstantAsMetadata::get(ConstantInt::getFalse(
4145 newCall->getContext()))});
4146 newCall->setMetadata(
"enzyme_cache_alloc",
4147 MDNode::get(newCall->getContext(), {ident}));
4149 Value *nop = gutils->cacheForReverse(
4155 IRBuilder<> Builder2(&call);
4157 auto dbgLoc = gutils->getNewFromOriginal(call.getDebugLoc());
4160 dbgLoc, gutils->TLI, &call, gutils);
4163 auto ident = MDNode::getDistinct(
4164 freecall->getContext(),
4165 {ConstantAsMetadata::get(
4166 combined ? ConstantInt::getTrue(freecall->getContext())
4167 : ConstantInt::getFalse(freecall->getContext()))});
4169 newCall->setMetadata(
"enzyme_cache_alloc",
4170 MDNode::get(freecall->getContext(), {ident}));
4171 freecall->setMetadata(
"enzyme_cache_free",
4172 MDNode::get(freecall->getContext(), {ident}));
4181 auto pn = BuilderZ.CreatePHI(call.getType(), 1,
4182 call.getName() +
"_replacementB");
4183 gutils->fictiousPHIs[pn] = &call;
4184 gutils->replaceAWithB(newCall, pn);
4185 gutils->erase(newCall);
4191 if (funcName ==
"julia.gc_loaded") {
4199 if (
auto placeholder = dyn_cast<PHINode>(&*ifound->second)) {
4204 gutils->invertedPointers.erase(ifound);
4205 gutils->erase(placeholder);
4210 gutils->invertedPointers.erase(ifound);
4211 auto res = gutils->invertPointerM(&call, BuilderZ);
4213 gutils->replaceAWithB(placeholder, res);
4214 gutils->erase(placeholder);
4221 if (funcName ==
"julia.pointer_from_objref") {
4230 auto placeholder = cast<PHINode>(&*ifound->second);
4234 gutils, &call, Mode, oldUnreachable);
4237 gutils->
erase(placeholder);
4242 Value *ptrshadow = gutils->
invertPointerM(call.getArgOperand(0), BuilderZ);
4245 call.getType(), BuilderZ,
4246 [&](Value *v) -> Value * { return BuilderZ.CreateCall(called, {v}); },
4250 gutils->
erase(placeholder);
4254 if (funcName.contains(
"__enzyme_todense")) {
4255 if (gutils->isConstantValue(&call)) {
4256 eraseIfUnused(call);
4260 auto ifound = gutils->invertedPointers.find(&call);
4261 assert(ifound != gutils->invertedPointers.end());
4263 auto placeholder = cast<PHINode>(&*ifound->second);
4267 gutils, &call, Mode, oldUnreachable);
4269 gutils->invertedPointers.erase(ifound);
4270 gutils->erase(placeholder);
4271 eraseIfUnused(call);
4277 SmallVector<Value *, 3> args;
4278 SmallVector<bool, 3> argIsShadow;
4279 for (
size_t i = 0; i < 2; i++) {
4280 args.push_back(gutils->getNewFromOriginal(call.getArgOperand(i)));
4281 argIsShadow.push_back(
false);
4283 for (
size_t i = 2; i < call.arg_size(); ++i) {
4284 auto arg = call.getArgOperand(i);
4285 if (gutils->isConstantValue(arg)) {
4286 args.push_back(gutils->getNewFromOriginal(arg));
4287 argIsShadow.push_back(
false);
4289 args.push_back(gutils->invertPointerM(arg, BuilderZ));
4290 argIsShadow.push_back(
true);
4294 Value *res = UndefValue::get(gutils->getShadowType(call.getType()));
4295 if (gutils->getWidth() == 1) {
4296 res = BuilderZ.CreateCall(called, args);
4298 for (
size_t w = 0; w < gutils->getWidth(); ++w) {
4299 SmallVector<Value *, 3> targs = {args[0], args[1]};
4300 for (
size_t i = 2; i < call.arg_size(); ++i)
4301 targs.push_back(argIsShadow[i]
4305 auto tres = BuilderZ.CreateCall(called, targs);
4306 res = BuilderZ.CreateInsertValue(res, tres, w);
4310 gutils->replaceAWithB(placeholder, res);
4311 gutils->erase(placeholder);
4312 eraseIfUnused(call);
4316 if (funcName ==
"memcpy" || funcName ==
"memmove") {
4317 auto ID = (funcName ==
"memcpy") ? Intrinsic::memcpy : Intrinsic::memmove;
4318 visitMemTransferCommon(ID, MaybeAlign(1),
4319 MaybeAlign(1), call,
4320 call.getArgOperand(0), call.getArgOperand(1),
4321 gutils->getNewFromOriginal(call.getArgOperand(2)),
4322 ConstantInt::getFalse(call.getContext()));
4325 if (funcName ==
"memset" || funcName ==
"memset_pattern16" ||
4326 funcName ==
"__memset_chk") {
4327 visitMemSetCommon(call);
4330 if (funcName ==
"enzyme_zerotype") {
4331 IRBuilder<> BuilderZ(&call);
4332 getForwardBuilder(BuilderZ);
4334 bool backwardsShadow =
false;
4335 bool forwardsShadow =
true;
4336 for (
auto pair : gutils->backwardsOnlyShadows) {
4337 if (pair.second.stores.count(&call)) {
4338 backwardsShadow =
true;
4339 forwardsShadow = pair.second.primalInitialize;
4340 if (
auto inst = dyn_cast<Instruction>(pair.first))
4341 if (!forwardsShadow && pair.second.LI &&
4342 pair.second.LI->contains(inst->getParent()))
4343 backwardsShadow =
false;
4354 eraseIfUnused(call,
true,
false);
4356 eraseIfUnused(call);
4358 Value *orig_op0 = call.getArgOperand(0);
4361 if (gutils->isConstantValue(orig_op0)) {
4366 Value *op0 = gutils->invertPointerM(orig_op0, BuilderZ);
4367 Value *op1 = gutils->getNewFromOriginal(call.getArgOperand(1));
4368 Value *op2 = gutils->getNewFromOriginal(call.getArgOperand(2));
4369 auto Defs = gutils->getInvertedBundles(
4376 SmallVector<Value *, 4> args = {op0, op1, op2};
4378 BuilderZ.CreateCall(call.getCalledFunction(), args, Defs);
4379 llvm::SmallVector<unsigned int, 9> ToCopy2(
MD_ToCopy);
4380 ToCopy2.push_back(LLVMContext::MD_noalias);
4381 cal->copyMetadata(call, ToCopy2);
4382 cal->setAttributes(call.getAttributes());
4383 if (
auto m =
hasMetadata(&call,
"enzyme_zerostack"))
4384 cal->setMetadata(
"enzyme_zerostack", m);
4385 cal->setCallingConv(call.getCallingConv());
4386 cal->setTailCallKind(call.getTailCallKind());
4387 cal->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
4393 if (funcName ==
"cuStreamCreate") {
4394 Value *val =
nullptr;
4396#if LLVM_VERSION_MAJOR < 17
4397 if (call.getContext().supportsTypedPointers()) {
4398 if (isa<PointerType>(call.getArgOperand(0)->getType()))
4399 PT = call.getArgOperand(0)->getType()->getPointerElementType();
4404 val = gutils->getNewFromOriginal(call.getOperand(0));
4405 if (!isa<PointerType>(val->getType()))
4406 val = BuilderZ.CreateIntToPtr(val,
getUnqual(PT));
4407 val = BuilderZ.CreateLoad(PT, val);
4408 val = gutils->cacheForReverse(BuilderZ, val,
4412 PHINode *toReplace =
4413 BuilderZ.CreatePHI(PT, 1, call.getName() +
"_psxtmp");
4414 val = gutils->cacheForReverse(BuilderZ, toReplace,
4419 IRBuilder<> Builder2(&call);
4420 getReverseBuilder(Builder2);
4421 val = gutils->lookupM(val, Builder2);
4423 *gutils->newFunc->getParent(), called,
"cuStreamDestroy",
4424 FunctionType::get(call.getType(), {PT},
false));
4425 Value *nargs[] = {val};
4426 Builder2.CreateCall(FreeFunc, nargs);
4429 eraseIfUnused(call,
true,
false);
4432 if (funcName ==
"cuStreamDestroy") {
4435 eraseIfUnused(call,
true,
false);
4438 if (funcName ==
"cuStreamSynchronize") {
4441 IRBuilder<> Builder2(&call);
4442 getReverseBuilder(Builder2);
4443 Value *nargs[] = {gutils->lookupM(
4444 gutils->getNewFromOriginal(call.getOperand(0)), Builder2)};
4445 auto callval = call.getCalledOperand();
4446 Builder2.CreateCall(call.getFunctionType(), callval, nargs);
4449 eraseIfUnused(call,
true,
false);
4452 if (funcName ==
"posix_memalign" || funcName ==
"cuMemAllocAsync" ||
4453 funcName ==
"cuMemAlloc" || funcName ==
"cuMemAlloc_v2" ||
4454 funcName ==
"cudaMalloc" || funcName ==
"cudaMallocAsync" ||
4455 funcName ==
"cudaMallocHost" || funcName ==
"cudaMallocFromPoolAsync") {
4456 bool constval = gutils->isConstantInstruction(&call);
4460#if LLVM_VERSION_MAJOR < 17
4461 if (call.getContext().supportsTypedPointers()) {
4462 if (isa<PointerType>(call.getArgOperand(0)->getType()))
4463 PT = call.getArgOperand(0)->getType()->getPointerElementType();
4467 Value *stream =
nullptr;
4468 if (funcName ==
"cuMemAllocAsync")
4469 stream = gutils->getNewFromOriginal(call.getArgOperand(2));
4470 else if (funcName ==
"cudaMallocAsync")
4471 stream = gutils->getNewFromOriginal(call.getArgOperand(2));
4472 else if (funcName ==
"cudaMallocFromPoolAsync")
4473 stream = gutils->getNewFromOriginal(call.getArgOperand(3));
4475 auto M = gutils->newFunc->getParent();
4482 gutils->invertPointerM(call.getArgOperand(0), BuilderZ);
4483 SmallVector<Value *, 1> args;
4484 SmallVector<ValueType, 1> valtys;
4485 args.push_back(ptrshadow);
4487 for (
size_t i = 1; i < call.arg_size(); ++i) {
4488 args.push_back(gutils->getNewFromOriginal(call.getArgOperand(i)));
4492 auto Defs = gutils->getInvertedBundles(&call, valtys, BuilderZ,
4495 val = applyChainRule(
4497 [&](Value *ptrshadow) {
4498 args[0] = ptrshadow;
4500 BuilderZ.CreateCall(called, args, Defs);
4501 if (!isa<PointerType>(ptrshadow->getType()))
4502 ptrshadow = BuilderZ.CreateIntToPtr(ptrshadow,
getUnqual(PT));
4503 Value *val = BuilderZ.CreateLoad(PT, ptrshadow);
4506 BuilderZ.CreateBitCast(val,
getInt8PtrTy(call.getContext()));
4509 ConstantInt::get(Type::getInt8Ty(call.getContext()), 0);
4510 auto len_arg = gutils->getNewFromOriginal(
4511 call.getArgOperand((funcName ==
"posix_memalign") ? 2 : 1));
4513 if (funcName ==
"posix_memalign" ||
4514 funcName ==
"cudaMallocHost") {
4515 BuilderZ.CreateMemSet(dst_arg, val_arg, len_arg, MaybeAlign());
4516 }
else if (funcName ==
"cudaMalloc") {
4517 Type *tys[] = {PT, val_arg->getType(), len_arg->getType()};
4519 *M, called,
"cudaMemset",
4520 FunctionType::get(call.getType(), tys,
false));
4521 Value *nargs[] = {dst_arg, val_arg, len_arg};
4522 auto memset = cast<CallInst>(BuilderZ.CreateCall(F, nargs));
4523 memset->addParamAttr(0, Attribute::NonNull);
4524 }
else if (funcName ==
"cudaMallocAsync" ||
4525 funcName ==
"cudaMallocFromPoolAsync") {
4526 Type *tys[] = {PT, val_arg->getType(), len_arg->getType(),
4529 *M, called,
"cudaMemsetAsync",
4530 FunctionType::get(call.getType(), tys,
false));
4531 Value *nargs[] = {dst_arg, val_arg, len_arg, stream};
4532 auto memset = cast<CallInst>(BuilderZ.CreateCall(F, nargs));
4533 memset->addParamAttr(0, Attribute::NonNull);
4534 }
else if (funcName ==
"cuMemAllocAsync") {
4535 Type *tys[] = {PT, val_arg->getType(), len_arg->getType(),
4538 *M, called,
"cuMemsetD8Async",
4539 FunctionType::get(call.getType(), tys,
false));
4540 Value *nargs[] = {dst_arg, val_arg, len_arg, stream};
4541 auto memset = cast<CallInst>(BuilderZ.CreateCall(F, nargs));
4542 memset->addParamAttr(0, Attribute::NonNull);
4543 }
else if (funcName ==
"cuMemAlloc" ||
4544 funcName ==
"cuMemAlloc_v2") {
4545 Type *tys[] = {PT, val_arg->getType(), len_arg->getType()};
4548 funcName ==
"cuMemAlloc_v2" ?
"cuMemsetD8_v2"
4550 FunctionType::get(call.getType(), tys,
false));
4551 Value *nargs[] = {dst_arg, val_arg, len_arg};
4552 auto memset = cast<CallInst>(BuilderZ.CreateCall(F, nargs));
4553 memset->addParamAttr(0, Attribute::NonNull);
4555 llvm_unreachable(
"unhandled allocation");
4563 val = gutils->cacheForReverse(
4566 PHINode *toReplace = BuilderZ.CreatePHI(gutils->getShadowType(PT), 1,
4567 call.getName() +
"_psxtmp");
4568 val = gutils->cacheForReverse(
4575 IRBuilder<> Builder2(&call);
4576 getReverseBuilder(Builder2);
4577 Value *tofree = gutils->lookupM(val, Builder2, ValueToValueMapTy(),
4580 Type *VoidTy = Type::getVoidTy(M->getContext());
4583 Value *streamL =
nullptr;
4585 streamL = gutils->lookupM(stream, Builder2);
4589 [&](Value *tofree) {
4590 if (funcName ==
"posix_memalign") {
4592 M->getOrInsertFunction(
"free", VoidTy, IntPtrTy);
4593 Builder2.CreateCall(FreeFunc, tofree);
4594 }
else if (funcName ==
"cuMemAllocAsync") {
4596 *M, called,
"cuMemFreeAsync",
4597 FunctionType::get(VoidTy, {IntPtrTy, streamL->getType()},
4599 Value *nargs[] = {tofree, streamL};
4600 Builder2.CreateCall(FreeFunc, nargs);
4601 }
else if (funcName ==
"cuMemAlloc" ||
4602 funcName ==
"cuMemAlloc_v2") {
4604 *M, called,
"cuMemFree",
4605 FunctionType::get(VoidTy, {IntPtrTy},
false));
4606 Value *nargs[] = {tofree};
4607 Builder2.CreateCall(FreeFunc, nargs);
4608 }
else if (funcName ==
"cudaMalloc") {
4610 *M, called,
"cudaFree",
4611 FunctionType::get(VoidTy, {IntPtrTy},
false));
4612 Value *nargs[] = {tofree};
4613 Builder2.CreateCall(FreeFunc, nargs);
4614 }
else if (funcName ==
"cudaMallocAsync" ||
4615 funcName ==
"cudaMallocFromPoolAsync") {
4617 *M, called,
"cudaFreeAsync",
4618 FunctionType::get(VoidTy, {IntPtrTy, streamL->getType()},
4620 Value *nargs[] = {tofree, streamL};
4621 Builder2.CreateCall(FreeFunc, nargs);
4622 }
else if (funcName ==
"cudaMallocHost") {
4624 *M, called,
"cudaFreeHost",
4625 FunctionType::get(VoidTy, {IntPtrTy},
false));
4626 Value *nargs[] = {tofree};
4627 Builder2.CreateCall(FreeFunc, nargs);
4629 llvm_unreachable(
"unknown function to free");
4640 eraseIfUnused(call,
true,
false);
4655 IRBuilder<> Builder2(newCall->getNextNode());
4656 auto ptrv = gutils->getNewFromOriginal(call.getOperand(0));
4657 if (!isa<PointerType>(ptrv->getType()))
4658 ptrv = BuilderZ.CreateIntToPtr(ptrv,
getUnqual(PT));
4659 auto load = Builder2.CreateLoad(PT, ptrv,
"posix_preread");
4660 Builder2.SetInsertPoint(&call);
4661 getReverseBuilder(Builder2);
4662 auto tofree = gutils->lookupM(load, Builder2, ValueToValueMapTy(),
4664 Value *streamL =
nullptr;
4665 if (funcName ==
"cuMemAllocAsync")
4666 streamL = gutils->getNewFromOriginal(call.getArgOperand(2));
4667 else if (funcName ==
"cudaMallocAsync")
4668 streamL = gutils->getNewFromOriginal(call.getArgOperand(2));
4669 else if (funcName ==
"cudaMallocFromPoolAsync")
4670 streamL = gutils->getNewFromOriginal(call.getArgOperand(3));
4672 streamL = gutils->lookupM(streamL, Builder2);
4674 auto M = gutils->newFunc->getParent();
4675 Type *VoidTy = Type::getVoidTy(M->getContext());
4678 if (funcName ==
"posix_memalign") {
4679 auto FreeFunc = M->getOrInsertFunction(
"free", VoidTy, IntPtrTy);
4680 Builder2.CreateCall(FreeFunc, tofree);
4681 }
else if (funcName ==
"cuMemAllocAsync") {
4683 *M, called,
"cuMemFreeAsync",
4684 FunctionType::get(VoidTy, {IntPtrTy, streamL->getType()},
false));
4685 Value *nargs[] = {tofree, streamL};
4686 Builder2.CreateCall(FreeFunc, nargs);
4687 }
else if (funcName ==
"cuMemAlloc" || funcName ==
"cuMemAlloc_v2") {
4689 *M, called,
"cuMemFree",
4690 FunctionType::get(VoidTy, {IntPtrTy},
false));
4691 Value *nargs[] = {tofree};
4692 Builder2.CreateCall(FreeFunc, nargs);
4693 }
else if (funcName ==
"cudaMalloc") {
4695 *M, called,
"cudaFree",
4696 FunctionType::get(VoidTy, {IntPtrTy},
false));
4697 Value *nargs[] = {tofree};
4698 Builder2.CreateCall(FreeFunc, nargs);
4699 }
else if (funcName ==
"cudaMallocAsync" ||
4700 funcName ==
"cudaMallocFromPoolAsync") {
4702 *M, called,
"cudaFreeAsync",
4703 FunctionType::get(VoidTy, {IntPtrTy, streamL->getType()},
false));
4704 Value *nargs[] = {tofree, streamL};
4705 Builder2.CreateCall(FreeFunc, nargs);
4706 }
else if (funcName ==
"cudaMallocHost") {
4708 *M, called,
"cudaFreeHost",
4709 FunctionType::get(VoidTy, {IntPtrTy},
false));
4710 Value *nargs[] = {tofree};
4711 Builder2.CreateCall(FreeFunc, nargs);
4713 llvm_unreachable(
"unknown function to free");
4722 assert(gutils->invertedPointers.find(&call) ==
4723 gutils->invertedPointers.end());
4727 if (!gutils->isConstantValue(call.getArgOperand(0))) {
4728 IRBuilder<> Builder2(&call);
4729 getForwardBuilder(Builder2);
4730 auto origfree = call.getArgOperand(0);
4731 auto newfree = gutils->getNewFromOriginal(call.getArgOperand(0));
4732 auto tofree = gutils->invertPointerM(origfree, Builder2);
4735 *call.getModule(), &call, newfree->getType(), gutils->getWidth());
4738 if (
auto instArg = dyn_cast<Instruction>(call.getArgOperand(0)))
4739 used = unnecessaryInstructions.find(instArg) ==
4740 unnecessaryInstructions.end();
4742 SmallVector<Value *, 3> args;
4744 args.push_back(newfree);
4747 Constant::getNullValue(call.getArgOperand(0)->getType()));
4749 auto rule = [&args](Value *tofree) { args.push_back(tofree); };
4750 applyChainRule(Builder2, rule, tofree);
4752 for (
size_t i = 1; i < call.arg_size(); i++) {
4753 args.push_back(gutils->getNewFromOriginal(call.getArgOperand(i)));
4756 auto frees = Builder2.CreateCall(free->getFunctionType(), free, args);
4757 frees->setDebugLoc(gutils->getNewFromOriginal(call.getDebugLoc()));
4759 eraseIfUnused(call);
4762 eraseIfUnused(call);
4764 auto callval = call.getCalledOperand();
4766 for (
auto rmat : gutils->backwardsOnlyShadows) {
4767 if (gutils->allocationsToBeRematerialized.count(rmat.first) &&
4768 rmat.second.frees.count(&call)) {
4769 bool shouldFree =
false;
4770 if (rmat.second.primalInitialize) {
4776 IRBuilder<> Builder2(&call);
4777 getForwardBuilder(Builder2);
4778 auto origfree = call.getArgOperand(0);
4779 auto tofree = gutils->invertPointerM(origfree, Builder2);
4780 if (tofree != origfree) {
4781 SmallVector<Value *, 2> args = {tofree};
4783 Builder2.CreateCall(call.getFunctionType(), callval, args);
4784 CI->setAttributes(call.getAttributes());
4792 for (
auto rmat : gutils->rematerializableAllocations) {
4793 if (gutils->allocationsToBeRematerialized.count(rmat.first) &&
4794 rmat.second.frees.count(&call)) {
4798 eraseIfUnused(call);
4801 eraseIfUnused(call,
true,
false);
4805 std::map<UsageKey, bool> Seen =
4806 gutils->populateSeenFromKnownRecompute();
4807 bool primalNeededInReverse =
4812 auto found = gutils->knownRecomputeHeuristic.find(rmat.first);
4813 if (found != gutils->knownRecomputeHeuristic.end()) {
4814 if (!found->second) {
4815 primalNeededInReverse =
true;
4819 bool cacheWholeAllocation =
4820 gutils->needsCacheWholeAllocation(rmat.first);
4821 if (cacheWholeAllocation) {
4822 primalNeededInReverse =
true;
4826 if (!cacheWholeAllocation) {
4827 eraseIfUnused(call);
4830 assert(!unnecessaryValues.count(rmat.first));
4831 (void)primalNeededInReverse;
4832 assert(primalNeededInReverse);
4837 if (gutils->forwardDeallocations.count(&call)) {
4839 eraseIfUnused(call,
true,
false);
4841 eraseIfUnused(call);
4845 if (gutils->postDominatingFrees.count(&call)) {
4846 eraseIfUnused(call,
true,
false);
4850 if (call.getMetadata(
"enzyme_cache_free")) {
4851 bool hasGuaranteedFree =
false;
4852 for (
const auto &pair : gutils->allocationsWithGuaranteedFree) {
4853 if (pair.second.count(&call)) {
4854 hasGuaranteedFree =
true;
4858 if (!hasGuaranteedFree) {
4859 eraseIfUnused(call,
true,
false);
4865 if (isa<ConstantPointerNull>(val)) {
4866 llvm::errs() <<
"removing free of null pointer\n";
4867 eraseIfUnused(call,
true,
false);
4872 llvm::errs() <<
"freeing without malloc " << *val <<
" in " << call <<
"\n";
4873 eraseIfUnused(call,
true,
false);
4877 if (call.hasFnAttr(
"enzyme_sample")) {
4882 bool constval = gutils->isConstantInstruction(&call);
4887 IRBuilder<> Builder2(&call);
4888 getReverseBuilder(Builder2);
4890 auto trace = call.getArgOperand(call.arg_size() - 1);
4891 auto address = call.getArgOperand(0);
4893 auto dtrace = lookup(gutils->getNewFromOriginal(trace), Builder2);
4894 auto daddress = lookup(gutils->getNewFromOriginal(address), Builder2);
4897 if (TR.query(&call)[{-1}].isPossiblePointer()) {
4898 dchoice = gutils->invertPointerM(&call, Builder2);
4900 dchoice = diffe(&call, Builder2);
4903 if (call.hasMetadata(
"enzyme_gradient_setter")) {
4904 auto gradient_setter = cast<Function>(
4905 cast<ValueAsMetadata>(
4906 call.getMetadata(
"enzyme_gradient_setter")->getOperand(0).get())
4910 Builder2, gradient_setter->getFunctionType(), gradient_setter,
4911 daddress, dchoice, dtrace);
4917 if (call.hasFnAttr(
"enzyme_insert_argument")) {
4918 IRBuilder<> Builder2(&call);
4919 getReverseBuilder(Builder2);
4921 auto name = call.getArgOperand(0);
4922 auto arg = call.getArgOperand(1);
4923 auto trace = call.getArgOperand(2);
4925 auto gradient_setter = cast<Function>(
4926 cast<ValueAsMetadata>(
4927 call.getMetadata(
"enzyme_gradient_setter")->getOperand(0).get())
4930 auto dtrace = lookup(gutils->getNewFromOriginal(trace), Builder2);
4931 auto dname = lookup(gutils->getNewFromOriginal(name), Builder2);
4934 if (TR.query(arg)[{-1}].isPossiblePointer()) {
4935 darg = gutils->invertPointerM(arg, Builder2);
4937 darg = diffe(arg, Builder2);
4941 gradient_setter->getFunctionType(),
4942 gradient_setter, dname, darg, dtrace);