Enzyme main
Loading...
Searching...
No Matches
Utils.cpp
Go to the documentation of this file.
1//===- Utils.cpp - Definition of miscellaneous utilities ------------------===//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file defines miscellaneous utilities that are used as part of the
22// AD process.
23//
24//===----------------------------------------------------------------------===//
25#include "Utils.h"
26#include "GradientUtils.h"
28
29#if LLVM_VERSION_MAJOR >= 16
30#include "llvm/Analysis/ScalarEvolution.h"
31#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
32#else
33#include "SCEV/ScalarEvolution.h"
34#include "SCEV/ScalarEvolutionExpander.h"
35#endif
36
37#include "TypeAnalysis/TBAA.h"
38#include "llvm/IR/BasicBlock.h"
39#include "llvm/IR/DerivedTypes.h"
40#include "llvm/IR/Function.h"
41#include "llvm/IR/GetElementPtrTypeIterator.h"
42#include "llvm/IR/IRBuilder.h"
43#include "llvm/IR/InlineAsm.h"
44#include "llvm/IR/Module.h"
45#include "llvm/IR/Type.h"
46#include "llvm/IR/Verifier.h"
47
48#if LLVM_VERSION_MAJOR >= 16
49#include "llvm/TargetParser/Triple.h"
50#else
51#include "llvm/ADT/Triple.h"
52#endif
53
54#include "llvm-c/Core.h"
55
56#include "BlasAttributor.inc"
57#include "LibraryFuncs.h"
58
59using namespace llvm;
60
61extern "C" {
62LLVMValueRef (*CustomErrorHandler)(const char *, LLVMValueRef, ErrorType,
63 const void *, LLVMValueRef,
64 LLVMBuilderRef) = nullptr;
65LLVMValueRef (*CustomAllocator)(LLVMBuilderRef, LLVMTypeRef,
66 /*Count*/ LLVMValueRef,
67 /*Align*/ LLVMValueRef, uint8_t,
68 LLVMValueRef *) = nullptr;
69void (*CustomZero)(LLVMBuilderRef, LLVMTypeRef,
70 /*Ptr*/ LLVMValueRef, uint8_t) = nullptr;
71LLVMValueRef (*CustomDeallocator)(LLVMBuilderRef, LLVMValueRef) = nullptr;
72void (*CustomRuntimeInactiveError)(LLVMBuilderRef, LLVMValueRef,
73 LLVMValueRef) = nullptr;
74LLVMValueRef *(*EnzymePostCacheStore)(LLVMValueRef, LLVMBuilderRef,
75 uint64_t *size) = nullptr;
76LLVMTypeRef (*EnzymeDefaultTapeType)(LLVMContextRef) = nullptr;
77LLVMValueRef (*EnzymeUndefinedValueForType)(LLVMModuleRef, LLVMTypeRef,
78 uint8_t) = nullptr;
79
80LLVMValueRef (*EnzymeSanitizeDerivatives)(LLVMValueRef, LLVMValueRef toset,
81 LLVMBuilderRef,
82 LLVMValueRef) = nullptr;
83
84extern llvm::cl::opt<bool> EnzymeZeroCache;
85
86// default to false because lacpy is slow
87llvm::cl::opt<bool>
88 EnzymeLapackCopy("enzyme-lapack-copy", cl::init(false), cl::Hidden,
89 cl::desc("Use blas copy calls to cache matrices"));
90llvm::cl::opt<bool>
91 EnzymeBlasCopy("enzyme-blas-copy", cl::init(true), cl::Hidden,
92 cl::desc("Use blas copy calls to cache vectors"));
93llvm::cl::opt<bool>
94 EnzymeFastMath("enzyme-fast-math", cl::init(true), cl::Hidden,
95 cl::desc("Use fast math on derivative compuation"));
96llvm::cl::opt<bool> EnzymeMemmoveWarning(
97 "enzyme-memmove-warning", cl::init(true), cl::Hidden,
98 cl::desc("Warn if using memmove implementation as a fallback for memmove"));
99llvm::cl::opt<bool> EnzymeRuntimeError(
100 "enzyme-runtime-error", cl::init(false), cl::Hidden,
101 cl::desc("Emit Runtime errors instead of compile time ones"));
102
103llvm::cl::opt<bool> EnzymeCheckDerivativeNaN(
104 "enzyme-check-nan", cl::init(false), cl::Hidden,
105 cl::desc("Add NaN checks to all derivative intermediate values"));
106
107llvm::cl::opt<bool> EnzymeNonPower2Cache(
108 "enzyme-non-power2-cache", cl::init(false), cl::Hidden,
109 cl::desc("Disable caching of integers which are not a power of 2"));
110}
111
112#define addAttribute addAttributeAtIndex
113#define getAttribute getAttributeAtIndex
114
115bool isFlangRuntimeNoFree(llvm::StringRef name) {
116 for (auto prefix : {"_FortranAio", "_FortranAModInteger", "_FortranAModReal",
117 "_FortranAModuloInteger", "_FortranAModuloReal"})
118 if (startsWith(name, prefix))
119 return true;
120 static const char *Names[] = {
121 "_FortranAFlush",
122 "_FortranAIndex1",
123 "_FortranAIndex2",
124 "_FortranAIndex4",
125 "_FortranAScan1",
126 "_FortranAScan2",
127 "_FortranAScan4",
128 "_FortranAVerify1",
129 "_FortranAVerify2",
130 "_FortranAVerify4",
131 "_FortranACharacterCompareScalar",
132 "_FortranACharacterCompareScalar1",
133 "_FortranACharacterCompareScalar2",
134 "_FortranACharacterCompareScalar4",
135 };
136 return llvm::is_contained(Names, name);
137}
138
139bool attributeKnownFunctions(llvm::Function &F) {
140 bool changed = false;
141 if (F.getName() == "fprintf") {
142 for (auto &arg : F.args()) {
143 if (arg.getType()->isPointerTy()) {
144 addFunctionNoCapture(&F, arg.getArgNo());
145 changed = true;
146 }
147 }
148 }
149 if (F.getName().contains("__enzyme_float") ||
150 F.getName().contains("__enzyme_double") ||
151 F.getName().contains("__enzyme_integer") ||
152 F.getName().contains("__enzyme_pointer") ||
153 F.getName().contains("__enzyme_todense") ||
154 F.getName().contains("__enzyme_ignore_derivatives") ||
155 F.getName().contains("__enzyme_iter") ||
156 F.getName().contains("__enzyme_virtualreverse")) {
157 changed = true;
158#if LLVM_VERSION_MAJOR >= 16
159 F.setOnlyReadsMemory();
160 F.setOnlyWritesMemory();
161#else
162 F.addFnAttr(Attribute::ReadNone);
163#endif
164 if (!(F.getName().contains("__enzyme_todense") ||
165 F.getName().contains("__enzyme_ignore_derivatives"))) {
166 for (auto &arg : F.args()) {
167 if (arg.getType()->isPointerTy()) {
168 arg.addAttr(Attribute::ReadNone);
169 addFunctionNoCapture(&F, arg.getArgNo());
170 }
171 }
172 }
173 }
174 if (F.getName() == "memcmp") {
175 changed = true;
176#if LLVM_VERSION_MAJOR >= 16
177 F.setOnlyAccessesArgMemory();
178 F.setOnlyReadsMemory();
179#else
180 F.addFnAttr(Attribute::ArgMemOnly);
181 F.addFnAttr(Attribute::ReadOnly);
182#endif
183 F.addFnAttr(Attribute::NoUnwind);
184 F.addFnAttr(Attribute::NoRecurse);
185 F.addFnAttr(Attribute::WillReturn);
186 F.addFnAttr(Attribute::NoFree);
187 F.addFnAttr(Attribute::NoSync);
188 for (int i = 0; i < 2; i++)
189 if (F.getFunctionType()->getParamType(i)->isPointerTy()) {
191 F.addParamAttr(i, Attribute::ReadOnly);
192 }
193 }
194
195 if (F.getName() ==
196 "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm") {
197 changed = true;
198 F.addFnAttr(Attribute::NoFree);
199 }
200 // Canonical MPI name across calling conventions (C "MPI_Recv", "PMPI_Recv"
201 // and Fortran "mpi_recv_" etc.). Parameter indices are shared between the
202 // ABIs; the Fortran ABI only appends a trailing `ierr` argument.
203 StringRef canonMPIName = canonicalizeMPIName(F.getName());
204 if (canonMPIName == "MPI_Irecv") {
205 auto FT = F.getFunctionType();
206 bool PointerABI = true;
207 changed = true;
208 F.addFnAttr(Attribute::NoUnwind);
209 F.addFnAttr(Attribute::NoRecurse);
210 F.addFnAttr(Attribute::WillReturn);
211 F.addFnAttr(Attribute::NoFree);
212 F.addFnAttr(Attribute::NoSync);
213 if (FT->getParamType(0)->isPointerTy()) {
214 F.addParamAttr(0, Attribute::WriteOnly);
215 } else {
216 PointerABI = false;
217 }
218 // OpenMPI vs MPICH
219 if (FT->getParamType(2)->isPointerTy()) {
221 F.addParamAttr(2, Attribute::ReadOnly);
222 }
223 if (FT->getParamType(6)->isPointerTy()) {
224 F.addParamAttr(6, Attribute::WriteOnly);
225 } else {
226 PointerABI = false;
227 }
228 if (PointerABI) {
229#if LLVM_VERSION_MAJOR >= 16
230 F.setOnlyAccessesInaccessibleMemOrArgMem();
231#else
232 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
233#endif
234 }
235 }
236 auto name = getFuncName(&F);
237 if (canonMPIName == "MPI_Isend") {
238 auto FT = F.getFunctionType();
239 bool PointerABI = true;
240 changed = true;
241 F.addFnAttr(Attribute::NoUnwind);
242 F.addFnAttr(Attribute::NoRecurse);
243 F.addFnAttr(Attribute::WillReturn);
244 F.addFnAttr(Attribute::NoFree);
245 F.addFnAttr(Attribute::NoSync);
246 if (FT->getParamType(0)->isPointerTy()) {
247 F.addParamAttr(0, Attribute::ReadOnly);
248 } else {
249 PointerABI = false;
250 }
251 // OpenMPI vs MPICH
252 if (FT->getParamType(2)->isPointerTy()) {
254 F.addParamAttr(2, Attribute::ReadOnly);
255 }
256 if (FT->getParamType(6)->isPointerTy()) {
257 F.addParamAttr(6, Attribute::WriteOnly);
258 } else {
259 PointerABI = false;
260 }
261 if (PointerABI) {
262#if LLVM_VERSION_MAJOR >= 16
263 F.setOnlyAccessesInaccessibleMemOrArgMem();
264#else
265 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
266#endif
267 }
268 }
269 if (canonMPIName == "MPI_Comm_rank" || canonMPIName == "MPI_Comm_size") {
270 auto FT = F.getFunctionType();
271 bool PointerABI = true;
272 changed = true;
273 F.addFnAttr(Attribute::NoUnwind);
274 F.addFnAttr(Attribute::NoRecurse);
275 F.addFnAttr(Attribute::WillReturn);
276 F.addFnAttr(Attribute::NoFree);
277 F.addFnAttr(Attribute::NoSync);
278
279 // OpenMPI vs MPICH
280 if (FT->getParamType(0)->isPointerTy()) {
282 F.addParamAttr(0, Attribute::ReadOnly);
283 }
284 if (FT->getParamType(1)->isPointerTy()) {
285 F.addParamAttr(1, Attribute::WriteOnly);
287 } else {
288 PointerABI = false;
289 }
290 if (PointerABI) {
291#if LLVM_VERSION_MAJOR >= 16
292 F.setOnlyAccessesInaccessibleMemOrArgMem();
293#else
294 F.addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
295#endif
296 }
297 }
298 if (canonMPIName == "MPI_Wait") {
299 changed = true;
300 F.addFnAttr(Attribute::NoUnwind);
301 F.addFnAttr(Attribute::NoRecurse);
302 F.addFnAttr(Attribute::WillReturn);
303 F.addFnAttr(Attribute::NoFree);
304 F.addFnAttr(Attribute::NoSync);
305 if (F.getFunctionType()->getParamType(0)->isPointerTy()) {
307 }
308 if (F.getFunctionType()->getParamType(1)->isPointerTy()) {
309 F.addParamAttr(1, Attribute::WriteOnly);
311 }
312 }
313 if (canonMPIName == "MPI_Waitall") {
314 changed = true;
315 F.addFnAttr(Attribute::NoUnwind);
316 F.addFnAttr(Attribute::NoRecurse);
317 F.addFnAttr(Attribute::WillReturn);
318 F.addFnAttr(Attribute::NoFree);
319 F.addFnAttr(Attribute::NoSync);
320 if (F.getFunctionType()->getParamType(1)->isPointerTy()) {
322 }
323 if (F.getFunctionType()->getParamType(2)->isPointerTy()) {
324 F.addParamAttr(2, Attribute::WriteOnly);
326 }
327 }
328 // Map of MPI function name to the arg index of its type argument
329 std::map<std::string, int> MPI_TYPE_ARGS = {
330 {"MPI_Send", 2}, {"MPI_Ssend", 2}, {"MPI_Bsend", 2},
331 {"MPI_Recv", 2}, {"MPI_Brecv", 2}, {"PMPI_Send", 2},
332 {"PMPI_Ssend", 2}, {"PMPI_Bsend", 2}, {"PMPI_Recv", 2},
333 {"PMPI_Brecv", 2},
334
335 {"MPI_Isend", 2}, {"MPI_Irecv", 2}, {"PMPI_Isend", 2},
336 {"PMPI_Irecv", 2},
337
338 {"MPI_Reduce", 3}, {"PMPI_Reduce", 3},
339
340 {"MPI_Allreduce", 3}, {"PMPI_Allreduce", 3}};
341 {
342 auto found = MPI_TYPE_ARGS.find(canonMPIName.str());
343 if (found != MPI_TYPE_ARGS.end()) {
344 for (auto user : F.users()) {
345 if (auto CI = dyn_cast<CallBase>(user))
346 if (CI->getCalledFunction() == &F) {
347 if (Constant *C =
348 dyn_cast<Constant>(CI->getArgOperand(found->second))) {
349 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
350 C = CE->getOperand(0);
351 }
352 if (auto GV = dyn_cast<GlobalVariable>(C)) {
353 if (GV->getName() == "ompi_mpi_cxx_bool") {
354 changed = true;
355 CI->addAttribute(
356 AttributeList::FunctionIndex,
357 Attribute::get(CI->getContext(), "enzyme_inactive"));
358 }
359 }
360 }
361 }
362 }
363 }
364 }
365
366 if (F.getName() == "omp_get_max_threads" ||
367 F.getName() == "omp_get_thread_num") {
368 changed = true;
369#if LLVM_VERSION_MAJOR >= 16
370 F.setOnlyAccessesInaccessibleMemory();
371 F.setOnlyReadsMemory();
372#else
373 F.addFnAttr(Attribute::InaccessibleMemOnly);
374 F.addFnAttr(Attribute::ReadOnly);
375#endif
376 }
377 if (F.getName() == "frexp" || F.getName() == "frexpf" ||
378 F.getName() == "frexpl") {
379 changed = true;
380#if LLVM_VERSION_MAJOR >= 16
381 F.setOnlyAccessesArgMemory();
382#else
383 F.addFnAttr(Attribute::ArgMemOnly);
384#endif
385 F.addParamAttr(1, Attribute::WriteOnly);
386 }
387 if (F.getName() == "__fd_sincos_1" || F.getName() == "__fd_cos_1" ||
388 F.getName() == "__mth_i_ipowi") {
389 changed = true;
390#if LLVM_VERSION_MAJOR >= 16
391 F.setOnlyReadsMemory();
392 F.setOnlyWritesMemory();
393#else
394 F.addFnAttr(Attribute::ReadNone);
395#endif
396 }
397
398 const char *NonEscapingFns[] = {
399 "julia.ptls_states",
400 "julia.get_pgcstack",
401 "lgamma_r",
402 "memcmp",
403 "_ZNSt6chrono3_V212steady_clock3nowEv",
404 "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_"
405 "createERmm",
406 "_ZNKSt8__detail20_Prime_rehash_policy14_M_need_rehashEmmm",
407 "fprintf",
408 "fwrite",
409 "fputc",
410 "strtol",
411 "getenv",
412 "memchr",
413 "cublasSetMathMode",
414 "cublasSetStream_v2",
415 "cuMemPoolTrimTo",
416 "cuDeviceGetMemPool",
417 "cuStreamSynchronize",
418 "cuStreamDestroy",
419 "cuStreamQuery",
420 "cuCtxGetCurrent",
421 "cuDeviceGet",
422 "cuDeviceGetName",
423 "cuDriverGetVersion",
424 "cudaRuntimeGetVersion",
425 "cuDeviceGetCount",
426 "cuMemPoolGetAttribute",
427 "cuMemGetInfo_v2",
428 "cuDeviceGetAttribute",
429 "cuDevicePrimaryCtxRetain",
430 };
431 for (auto fname : NonEscapingFns)
432 if (name == fname) {
433 changed = true;
434 F.addAttribute(
435 AttributeList::FunctionIndex,
436 Attribute::get(F.getContext(), "enzyme_no_escaping_allocation"));
437 }
438 // With -enzyme-global-activity a call to a function without body may free
439 // memory or return an allocation needed in the reverse pass, and Enzyme
440 // then differentiates even an inactive call to it.
441 // These LLVM flang runtime queries are also in KnownInactiveFunctions.
442 const char *FlangRuntimeQueries[] = {
443 // LLVM flang runtime: time
444 "_FortranACpuTime",
445 "_FortranADateAndTime",
446 "_FortranAEtime",
447 "_FortranASystemClockCount",
448 "_FortranASystemClockCountRate",
449 "_FortranASystemClockCountMax",
450 "_FortranATimef",
451 // LLVM flang runtime: command line and environment
452 "_FortranAArgumentCount",
453 "_FortranAGetCommand",
454 "_FortranAGetCommandArgument",
455 "_FortranAGetEnvVariable",
456 "_FortranAGetCwd",
457 "_FortranAHostnm",
458 "_FortranAGetPID",
459 "_FortranAGetUID",
460 "_FortranAGetGID",
461 };
462 if (llvm::is_contained(FlangRuntimeQueries, name)) {
463 changed = true;
464 F.addFnAttr(Attribute::NoFree);
465 F.addAttribute(
466 AttributeList::FunctionIndex,
467 Attribute::get(F.getContext(), "enzyme_no_escaping_allocation"));
468 }
469 // These LLVM flang runtime functions do not free memory of the program
470 // (see isFlangRuntimeNoFree, used by CreateNoFree) and, like the
471 // termination calls, do not return or store an allocation into it.
472 if (isFlangRuntimeNoFree(name) || name == "_FortranAStopStatement" ||
473 name == "_FortranAStopStatementText" ||
474 name == "_FortranAReportFatalUserError" || name == "_FortranAExit" ||
475 name == "_FortranAAbort") {
476 changed = true;
477 F.addAttribute(
478 AttributeList::FunctionIndex,
479 Attribute::get(F.getContext(), "enzyme_no_escaping_allocation"));
480 }
481 // The OpenMP runtime calls of worksharing loops and synchronization neither
482 // free memory of the program nor hand an allocation to it.
483 const char *OpenMPRuntimeCalls[] = {
484 "__kmpc_global_thread_num",
485 "__kmpc_for_static_init_4",
486 "__kmpc_for_static_init_4u",
487 "__kmpc_for_static_init_8",
488 "__kmpc_for_static_init_8u",
489 "__kmpc_for_static_fini",
490 "__kmpc_barrier",
491 "__kmpc_critical",
492 "__kmpc_end_critical",
493 "omp_get_thread_num",
494 "omp_get_num_threads",
495 "omp_get_max_threads",
496 };
497 if (llvm::is_contained(OpenMPRuntimeCalls, name)) {
498 changed = true;
499 F.addFnAttr(Attribute::NoFree);
500 F.addAttribute(
501 AttributeList::FunctionIndex,
502 Attribute::get(F.getContext(), "enzyme_no_escaping_allocation"));
503 }
504 changed |= attributeTablegen(F);
505 return changed;
506}
507
508void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj,
509 bool isTape) {
510 if (CustomZero) {
511 CustomZero(wrap(&Builder), wrap(T), wrap(obj), isTape);
512 } else {
513 Builder.CreateStore(Constant::getNullValue(T), obj);
514 }
515}
516
517llvm::SmallVector<llvm::Instruction *, 2> PostCacheStore(llvm::StoreInst *SI,
518 llvm::IRBuilder<> &B) {
519 SmallVector<llvm::Instruction *, 2> res;
521 uint64_t size = 0;
522 auto ptr = EnzymePostCacheStore(wrap(SI), wrap(&B), &size);
523 for (size_t i = 0; i < size; i++) {
524 res.push_back(cast<Instruction>(unwrap(ptr[i])));
525 }
526 free(ptr);
527 }
528 return res;
529}
530
531llvm::PointerType *getDefaultAnonymousTapeType(llvm::LLVMContext &C) {
533 return cast<PointerType>(unwrap(EnzymeDefaultTapeType(wrap(&C))));
534 return getInt8PtrTy(C);
535}
536
537Function *getOrInsertExponentialAllocator(Module &M, Function *newFunc,
538 bool ZeroInit, llvm::Type *RT) {
539 bool custom = true;
540 llvm::PointerType *allocType;
541 {
542 auto i64 = Type::getInt64Ty(newFunc->getContext());
543 BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", newFunc);
544 IRBuilder<> B(BB);
545 auto P = B.CreatePHI(i64, 1);
546 CallInst *malloccall;
547 Instruction *SubZero = nullptr;
548 CreateAllocation(B, RT, P, "tapemem", &malloccall, &SubZero)->getType();
549 if (auto F = getFunctionFromCall(malloccall)) {
550 custom = F->getName() != "malloc";
551 }
552 allocType = cast<PointerType>(malloccall->getType());
553 if (ZeroInit && !SubZero)
554 ZeroInit = false;
555 BB->eraseFromParent();
556 }
557
558 Type *types[] = {allocType, Type::getInt64Ty(M.getContext()),
559 Type::getInt64Ty(M.getContext())};
560 std::string name = "__enzyme_exponentialallocation";
561 if (ZeroInit)
562 name += "zero";
563 if (custom)
564 name += ".custom@" + std::to_string((size_t)RT);
565
566 FunctionType *FT = FunctionType::get(allocType, types, false);
567 AttributeList AL;
568 if (newFunc->hasFnAttribute("enzymejl_world")) {
569 AL = AL.addFnAttribute(newFunc->getContext(),
570 newFunc->getFnAttribute("enzymejl_world"));
571 }
572 Function *F = cast<Function>(M.getOrInsertFunction(name, FT, AL).getCallee());
573
574 if (!F->empty())
575 return F;
576
577 F->setLinkage(Function::LinkageTypes::InternalLinkage);
578 F->addFnAttr(Attribute::AlwaysInline);
579 F->addFnAttr(Attribute::NoUnwind);
580 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
581 BasicBlock *grow = BasicBlock::Create(M.getContext(), "grow", F);
582 BasicBlock *ok = BasicBlock::Create(M.getContext(), "ok", F);
583
584 IRBuilder<> B(entry);
585
586 Argument *ptr = F->arg_begin();
587 ptr->setName("ptr");
588 Argument *size = ptr + 1;
589 size->setName("size");
590 Argument *tsize = size + 1;
591 tsize->setName("tsize");
592
593 Value *hasOne = B.CreateICmpNE(
594 B.CreateAnd(size, ConstantInt::get(size->getType(), 1, false)),
595 ConstantInt::get(size->getType(), 0, false));
596 auto popCnt = getIntrinsicDeclaration(&M, Intrinsic::ctpop, {types[1]});
597
598 B.CreateCondBr(
599 B.CreateAnd(B.CreateICmpULT(B.CreateCall(popCnt, {size}),
600 ConstantInt::get(types[1], 3, false)),
601 hasOne),
602 grow, ok);
603
604 B.SetInsertPoint(grow);
605
606 auto lz =
607 B.CreateCall(getIntrinsicDeclaration(&M, Intrinsic::ctlz, {types[1]}),
608 {size, ConstantInt::getTrue(M.getContext())});
609 Value *next =
610 B.CreateShl(tsize, B.CreateSub(ConstantInt::get(types[1], 64, false), lz,
611 "", true, true));
612
613 Value *gVal;
614
615 Value *halfNext = B.CreateLShr(next, ConstantInt::get(next->getType(), 1));
616 Value *isFirstSize =
617 B.CreateICmpEQ(size, ConstantInt::get(size->getType(), 1));
618 Value *prevSize = B.CreateSelect(
619 isFirstSize, ConstantInt::get(next->getType(), 0), halfNext);
620
621 auto Arch = llvm::Triple(M.getTargetTriple()).getArch();
622 bool forceMalloc = Arch == Triple::nvptx || Arch == Triple::nvptx64;
623
624 if (!custom && !forceMalloc) {
625 auto reallocF = M.getOrInsertFunction("realloc", allocType, allocType,
626 Type::getInt64Ty(M.getContext()));
627
628 Value *args[] = {B.CreatePointerCast(ptr, allocType), next};
629 gVal = B.CreateCall(reallocF, args);
630 } else {
631 Value *tsize = ConstantInt::get(
632 next->getType(),
633 newFunc->getParent()->getDataLayout().getTypeAllocSizeInBits(RT) / 8);
634 auto elSize = B.CreateUDiv(next, tsize, "", /*isExact*/ true);
635 Instruction *SubZero = nullptr;
636 gVal = CreateAllocation(B, RT, elSize, "", nullptr, &SubZero);
637
638 Type *bTy =
639 getPointerType(Type::getInt8Ty(gVal->getContext()),
640 cast<PointerType>(gVal->getType())->getAddressSpace());
641 gVal = B.CreatePointerCast(gVal, bTy);
642 auto pVal = B.CreatePointerCast(ptr, gVal->getType());
643
644 Value *margs[] = {gVal, pVal, prevSize,
645 ConstantInt::getFalse(M.getContext())};
646 Type *tys[] = {margs[0]->getType(), margs[1]->getType(),
647 margs[2]->getType()};
648 auto memsetF = getIntrinsicDeclaration(&M, Intrinsic::memcpy, tys);
649 B.CreateCall(memsetF, margs);
650 if (SubZero) {
651 ZeroInit = false;
652 IRBuilder<> BB(SubZero);
653 Value *zeroSize = BB.CreateSub(next, prevSize);
654 Value *tmp = SubZero->getOperand(0);
655 Type *tmpT = tmp->getType();
656 tmp = BB.CreatePointerCast(tmp, bTy);
657 tmp = BB.CreateInBoundsGEP(Type::getInt8Ty(tmp->getContext()), tmp,
658 prevSize);
659 tmp = BB.CreatePointerCast(tmp, tmpT);
660 SubZero->setOperand(0, tmp);
661 SubZero->setOperand(2, zeroSize);
662 }
663 }
664
665 if (ZeroInit) {
666 Value *zeroSize = B.CreateSub(next, prevSize);
667
668 Value *margs[] = {B.CreateInBoundsGEP(B.getInt8Ty(), gVal, prevSize),
669 B.getInt8(0), zeroSize, B.getFalse()};
670 Type *tys[] = {margs[0]->getType(), margs[2]->getType()};
671 auto memsetF = getIntrinsicDeclaration(&M, Intrinsic::memset, tys);
672 B.CreateCall(memsetF, margs);
673 }
674 gVal = B.CreatePointerCast(gVal, ptr->getType());
675
676 B.CreateBr(ok);
677 B.SetInsertPoint(ok);
678 auto phi = B.CreatePHI(ptr->getType(), 2);
679 phi->addIncoming(gVal, grow);
680 phi->addIncoming(ptr, entry);
681 B.CreateRet(phi);
682 return F;
683}
684
685llvm::Value *CreateReAllocation(llvm::IRBuilder<> &B, llvm::Value *prev,
686 llvm::Type *T, llvm::Value *OuterCount,
687 llvm::Value *InnerCount,
688 const llvm::Twine &Name,
689 llvm::CallInst **caller, bool ZeroMem) {
690 auto newFunc = B.GetInsertBlock()->getParent();
691
692 Value *tsize = ConstantInt::get(
693 InnerCount->getType(),
694 newFunc->getParent()->getDataLayout().getTypeAllocSizeInBits(T) / 8);
695
696 Value *idxs[] = {
697 /*ptr*/
698 prev,
699 /*incrementing value to increase when it goes past a power of two*/
700 OuterCount,
701 /*buffer size (element x subloops)*/
702 B.CreateMul(tsize, InnerCount, "", /*NUW*/ true,
703 /*NSW*/ true)};
704
705 auto realloccall =
706 B.CreateCall(getOrInsertExponentialAllocator(*newFunc->getParent(),
707 newFunc, ZeroMem, T),
708 idxs, Name);
709 if (caller)
710 *caller = realloccall;
711 return realloccall;
712}
713
714Value *CreateAllocation(IRBuilder<> &Builder, llvm::Type *T, Value *Count,
715 const Twine &Name, CallInst **caller,
716 Instruction **ZeroMem, bool isDefault) {
717 Value *res;
718 auto &M = *Builder.GetInsertBlock()->getParent()->getParent();
719 auto AlignI = M.getDataLayout().getTypeAllocSizeInBits(T) / 8;
720
721 // On 32-bit targets (wasm32-wasi, 32-bit ARM/x86, etc.) the C ABI's
722 // malloc takes size_t = i32; if upstream tape-cache math widened
723 // Count to i64, feeding it straight into CreateMalloc emits
724 // `call i8* @malloc(i64 ...)` against wasi-libc's `malloc(i32)` and
725 // traps at load with signature_mismatch:malloc. Truncate Count to the
726 // target IntPtrType only when Count is WIDER than IntPtrTy -- narrower
727 // Counts are already handled correctly by LLVM's usual i64-widening
728 // before the malloc call on 64-bit hosts, and altering that path would
729 // rearrange the golden IR that existing FileCheck tests record.
730 //
731 // CustomAllocator is a user-supplied callback whose type contract we
732 // preserve unchanged.
733 Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext(), 0);
734 Value *AllocCount = Count;
735 Type *AllocSizeTy = Count->getType();
736 if (!CustomAllocator && AllocSizeTy->isIntegerTy() &&
737 IntPtrTy->isIntegerTy() &&
738 AllocSizeTy->getIntegerBitWidth() > IntPtrTy->getIntegerBitWidth()) {
739 AllocCount = Builder.CreateTrunc(Count, IntPtrTy, Name + ".size");
740 AllocSizeTy = IntPtrTy;
741 }
742 auto Align = ConstantInt::get(AllocSizeTy, AlignI);
743
744 CallInst *malloccall = nullptr;
745 if (CustomAllocator) {
746 LLVMValueRef wzeromem = nullptr;
747 res = unwrap(CustomAllocator(wrap(&Builder), wrap(T), wrap(Count),
748 wrap(Align), isDefault,
749 ZeroMem ? &wzeromem : nullptr));
750 if (isa<UndefValue>(res))
751 return res;
752 if (isa<Constant>(res))
753 return res;
754 if (auto I = dyn_cast<Instruction>(res))
755 I->setName(Name);
756
757 malloccall = dyn_cast<CallInst>(res);
758 if (malloccall == nullptr) {
759 malloccall = cast<CallInst>(cast<Instruction>(res)->getOperand(0));
760 }
761 if (ZeroMem) {
762 *ZeroMem = cast_or_null<Instruction>(unwrap(wzeromem));
763 ZeroMem = nullptr;
764 }
765 } else {
766#if LLVM_VERSION_MAJOR >= 24
767 res = Builder.CreateMalloc(AllocSizeTy, Align, AllocCount, nullptr, Name);
768#elif LLVM_VERSION_MAJOR > 17
769 res =
770 Builder.CreateMalloc(AllocSizeTy, T, Align, AllocCount, nullptr, Name);
771#else
772 if (Builder.GetInsertPoint() == Builder.GetInsertBlock()->end()) {
773 res = CallInst::CreateMalloc(Builder.GetInsertBlock(), AllocSizeTy, T,
774 Align, AllocCount, nullptr, Name);
775 Builder.SetInsertPoint(Builder.GetInsertBlock());
776 } else {
777 res = CallInst::CreateMalloc(&*Builder.GetInsertPoint(), AllocSizeTy, T,
778 Align, AllocCount, nullptr, Name);
779 }
780 if (!cast<Instruction>(res)->getParent())
781 Builder.Insert(cast<Instruction>(res));
782#endif
783
784 malloccall = dyn_cast<CallInst>(res);
785 if (malloccall == nullptr) {
786 malloccall = cast<CallInst>(cast<Instruction>(res)->getOperand(0));
787 }
788
789 // Assert computation of size of array doesn't wrap. Match against the
790 // (possibly-cast) size operand `AllocCount` that CreateMalloc actually
791 // consumed -- on 64-bit hosts where no cast was needed this is still
792 // pointer-equal to `Count`, so the identity check still fires.
793 if (auto BI = dyn_cast<BinaryOperator>(malloccall->getArgOperand(0))) {
794 if (BI->getOpcode() == BinaryOperator::Mul) {
795 if ((BI->getOperand(0) == Align && BI->getOperand(1) == AllocCount) ||
796 (BI->getOperand(1) == Align && BI->getOperand(0) == AllocCount))
797 BI->setHasNoSignedWrap(true);
798 BI->setHasNoUnsignedWrap(true);
799 }
800 }
801
802 if (auto ci = dyn_cast<ConstantInt>(Count)) {
803#if LLVM_VERSION_MAJOR >= 14
804 malloccall->addDereferenceableRetAttr(ci->getLimitedValue() * AlignI);
805#if !defined(FLANG) && !defined(ROCM)
806 AttrBuilder B(ci->getContext());
807#else
808 AttrBuilder B;
809#endif
810 B.addDereferenceableOrNullAttr(ci->getLimitedValue() * AlignI);
811 malloccall->setAttributes(malloccall->getAttributes().addRetAttributes(
812 malloccall->getContext(), B));
813#else
814 malloccall->addDereferenceableAttr(llvm::AttributeList::ReturnIndex,
815 ci->getLimitedValue() * AlignI);
816 malloccall->addDereferenceableOrNullAttr(llvm::AttributeList::ReturnIndex,
817 ci->getLimitedValue() * AlignI);
818#endif
819 // malloccall->removeAttribute(llvm::AttributeList::ReturnIndex,
820 // Attribute::DereferenceableOrNull);
821 }
822#if LLVM_VERSION_MAJOR >= 14
823 malloccall->addAttributeAtIndex(AttributeList::ReturnIndex,
824 Attribute::NoAlias);
825 malloccall->addAttributeAtIndex(AttributeList::ReturnIndex,
826 Attribute::NonNull);
827#else
828 malloccall->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias);
829 malloccall->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
830#endif
831 }
832 if (caller) {
833 *caller = malloccall;
834 }
835 if (ZeroMem) {
836 auto PT = cast<PointerType>(malloccall->getType());
837 Value *tozero = malloccall;
838
839 bool needsCast = false;
840#if LLVM_VERSION_MAJOR < 17
841#if LLVM_VERSION_MAJOR >= 15
842 if (PT->getContext().supportsTypedPointers()) {
843#endif
844 needsCast = !PT->getPointerElementType()->isIntegerTy(8);
845#if LLVM_VERSION_MAJOR >= 15
846 }
847#endif
848#endif
849 if (needsCast)
850 tozero = Builder.CreatePointerCast(
851 tozero, getPointerType(Type::getInt8Ty(PT->getContext()),
852 PT->getAddressSpace()));
853 // Use AllocCount (the possibly-cast size operand feeding CreateMalloc),
854 // not the pre-cast Count. When we did cast (wasm32 case), Count is
855 // wider than IntPtrTy and mixing it with Align (IntPtrTy) here would
856 // emit invalid IR like `mul i32 8, i64 %n`. On 64-bit hosts where no
857 // cast was needed, AllocCount is pointer-equal to Count so behavior
858 // matches the pre-patch code.
859 Value *args[] = {
860 tozero, ConstantInt::get(Type::getInt8Ty(malloccall->getContext()), 0),
861 Builder.CreateMul(Align, AllocCount, "", true, true),
862 ConstantInt::getFalse(malloccall->getContext())};
863 Type *tys[] = {args[0]->getType(), args[2]->getType()};
864
865 *ZeroMem = Builder.CreateCall(
866 getIntrinsicDeclaration(&M, Intrinsic::memset, tys), args);
867 }
868 return res;
869}
870
871CallInst *CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree) {
872 CallInst *res = nullptr;
873
874 if (CustomDeallocator) {
875 res = dyn_cast_or_null<CallInst>(
876 unwrap(CustomDeallocator(wrap(&Builder), wrap(ToFree))));
877 } else {
878
879 ToFree =
880 Builder.CreatePointerCast(ToFree, getInt8PtrTy(ToFree->getContext()));
881#if LLVM_VERSION_MAJOR > 17
882 res = cast<CallInst>(Builder.CreateFree(ToFree));
883#else
884 if (Builder.GetInsertPoint() == Builder.GetInsertBlock()->end()) {
885 res = cast<CallInst>(
886 CallInst::CreateFree(ToFree, Builder.GetInsertBlock()));
887 Builder.SetInsertPoint(Builder.GetInsertBlock());
888 } else {
889 res = cast<CallInst>(
890 CallInst::CreateFree(ToFree, &*Builder.GetInsertPoint()));
891 }
892 if (!cast<Instruction>(res)->getParent())
893 Builder.Insert(cast<Instruction>(res));
894#endif
895#if LLVM_VERSION_MAJOR >= 14
896 res->addAttributeAtIndex(AttributeList::FirstArgIndex, Attribute::NonNull);
897#else
898 res->addAttribute(AttributeList::FirstArgIndex, Attribute::NonNull);
899#endif
900 }
901 return res;
902}
903
904EnzymeWarning::EnzymeWarning(const llvm::Twine &RemarkName,
905 const llvm::DiagnosticLocation &Loc,
906 const llvm::Instruction *CodeRegion)
907 : EnzymeWarning(RemarkName, Loc, CodeRegion->getParent()->getParent()) {}
908
909EnzymeWarning::EnzymeWarning(const llvm::Twine &RemarkName,
910 const llvm::DiagnosticLocation &Loc,
911 const llvm::Function *CodeRegion)
912 : DiagnosticInfoUnsupported(*CodeRegion, RemarkName, Loc, DS_Warning) {}
913
914EnzymeFailure::EnzymeFailure(const llvm::Twine &RemarkName,
915 const llvm::DiagnosticLocation &Loc,
916 const llvm::Instruction *CodeRegion)
917 : EnzymeFailure(RemarkName, Loc, CodeRegion->getParent()->getParent()) {}
918
919EnzymeFailure::EnzymeFailure(const llvm::Twine &RemarkName,
920 const llvm::DiagnosticLocation &Loc,
921 const llvm::Function *CodeRegion)
922 : DiagnosticInfoUnsupported(*CodeRegion, RemarkName, Loc) {}
923
924/// Convert a floating type to a string
925static inline std::string tofltstr(Type *T) {
926 if (auto VT = dyn_cast<VectorType>(T)) {
927#if LLVM_VERSION_MAJOR >= 12
928 auto len = VT->getElementCount().getFixedValue();
929#else
930 auto len = VT->getNumElements();
931#endif
932 return "vec" + std::to_string(len) + tofltstr(VT->getElementType());
933 }
934 switch (T->getTypeID()) {
935 case Type::HalfTyID:
936 return "half";
937 case Type::FloatTyID:
938 return "float";
939 case Type::DoubleTyID:
940 return "double";
941 case Type::X86_FP80TyID:
942 return "x87d";
943 case Type::BFloatTyID:
944 return "bf16";
945 case Type::FP128TyID:
946 return "quad";
947 case Type::PPC_FP128TyID:
948 return "ppcddouble";
949 default:
950 llvm_unreachable("Invalid floating type");
951 }
952}
953
954Constant *getString(Module &M, StringRef Str) {
955 llvm::Constant *s = llvm::ConstantDataArray::getString(M.getContext(), Str);
956 auto *gv = new llvm::GlobalVariable(
957 M, s->getType(), true, llvm::GlobalValue::PrivateLinkage, s, ".str");
958 gv->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
959 Value *Idxs[2] = {ConstantInt::get(Type::getInt32Ty(M.getContext()), 0),
960 ConstantInt::get(Type::getInt32Ty(M.getContext()), 0)};
961 return ConstantExpr::getInBoundsGetElementPtr(s->getType(), gv, Idxs);
962}
963
964void emit_backtrace(llvm::Instruction *inst, llvm::raw_ostream &ss) {
965 SmallPtrSet<llvm::Instruction *, 8> visited;
966 while (true) {
967 if (visited.contains(inst))
968 break;
969 visited.insert(inst);
970
971 // Print debug info for this instruction
972 if (auto dbgLoc = inst->getDebugLoc()) {
973 auto *loc = dbgLoc.get();
974 while (loc) {
975 if (auto *scope = loc->getScope()) {
976 StringRef name = scope->getName();
977 // Remove trailing semicolons (Julia-style function name decoration)
978 while (!name.empty() && name.back() == ';')
979 name = name.drop_back();
980 if (auto *file = scope->getFile()) {
981 StringRef dir = file->getDirectory();
982 StringRef fn = file->getFilename();
983 ss << " in '" << name << "' at ";
984 if (!dir.empty())
985 ss << dir << "/";
986 ss << fn << ":" << loc->getLine() << "\n";
987 } else {
988 ss << " in '" << name << "' at unknown:" << loc->getLine() << "\n";
989 }
990 }
991 loc = loc->getInlinedAt();
992 }
993 }
994
995 // Move up the call chain
996 Function *f = inst->getParent()->getParent();
997
998 // Collect callers with debug info
999 SmallVector<CallInst *, 4> callersWithDbg;
1000 for (auto *U : f->users()) {
1001 auto *CI = dyn_cast<CallInst>(U);
1002 if (!CI)
1003 continue;
1004 if (!CI->getDebugLoc())
1005 continue;
1006 callersWithDbg.push_back(CI);
1007 }
1008
1009 if (callersWithDbg.empty())
1010 break;
1011
1012 // Deduplicate by debug location MDNode
1013 SmallVector<CallInst *, 4> uniqueCallSites;
1014 SmallPtrSet<const MDNode *, 4> seenMD;
1015 for (auto *CI : callersWithDbg) {
1016 if (seenMD.insert(CI->getDebugLoc().getAsMDNode()).second)
1017 uniqueCallSites.push_back(CI);
1018 }
1019
1020 if (uniqueCallSites.size() > 1) {
1021 ss << " (multiple call sites)\n";
1022 break;
1023 } else if (uniqueCallSites.size() == 1) {
1024 inst = uniqueCallSites[0];
1025 continue;
1026 }
1027 break;
1028 }
1029}
1030
1031void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal,
1032 llvm::Value *shadow, const char *Message,
1033 llvm::DebugLoc &&loc, llvm::Instruction *orig) {
1034 Module &M = *B.GetInsertBlock()->getParent()->getParent();
1035 std::string name = "__enzyme_runtimeinactiveerr";
1037 static int count = 0;
1038 name += std::to_string(count);
1039 count++;
1040 }
1041 FunctionType *FT = FunctionType::get(Type::getVoidTy(M.getContext()),
1042 {getInt8PtrTy(M.getContext()),
1043 getInt8PtrTy(M.getContext()),
1044 getInt8PtrTy(M.getContext())},
1045 false);
1046
1047 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1048
1049 if (F->empty()) {
1050 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1051 F->addFnAttr(Attribute::AlwaysInline);
1054
1055 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1056 BasicBlock *error = BasicBlock::Create(M.getContext(), "error", F);
1057 BasicBlock *end = BasicBlock::Create(M.getContext(), "end", F);
1058
1059 auto prim = F->arg_begin();
1060 prim->setName("primal");
1061 auto shadow = prim + 1;
1062 shadow->setName("shadow");
1063 auto msg = prim + 2;
1064 msg->setName("msg");
1065
1066 IRBuilder<> EB(entry);
1067 EB.CreateCondBr(EB.CreateICmpEQ(prim, shadow), error, end);
1068
1069 EB.SetInsertPoint(error);
1070
1072 CustomRuntimeInactiveError(wrap(&EB), wrap(msg), wrap(orig));
1073 } else {
1074 FunctionType *FT =
1075 FunctionType::get(Type::getInt32Ty(M.getContext()),
1076 {getInt8PtrTy(M.getContext())}, false);
1077
1078 auto PutsF = M.getOrInsertFunction("puts", FT);
1079 EB.CreateCall(PutsF, msg);
1080
1081 FunctionType *FT2 =
1082 FunctionType::get(Type::getVoidTy(M.getContext()),
1083 {Type::getInt32Ty(M.getContext())}, false);
1084
1085 auto ExitF = M.getOrInsertFunction("exit", FT2);
1086 EB.CreateCall(ExitF,
1087 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
1088 }
1089 EB.CreateUnreachable();
1090
1091 EB.SetInsertPoint(end);
1092 EB.CreateRetVoid();
1093 }
1094
1095 std::string Message2 = Message;
1097 std::string str;
1098 raw_string_ostream ss(str);
1099 ss << Message << "\n";
1100 emit_backtrace(orig, ss);
1101 Message2 = ss.str();
1102 }
1103 Value *args[] = {B.CreatePointerCast(primal, getInt8PtrTy(M.getContext())),
1104 B.CreatePointerCast(shadow, getInt8PtrTy(M.getContext())),
1105 getString(M, Message2)};
1106 auto call = B.CreateCall(F, args);
1107 call->setDebugLoc(loc);
1108}
1109
1110Type *BlasInfo::fpType(LLVMContext &ctx, bool to_scalar) const {
1111 if (floatType == "d" || floatType == "D") {
1112 return Type::getDoubleTy(ctx);
1113 } else if (floatType == "s" || floatType == "S") {
1114 return Type::getFloatTy(ctx);
1115 } else if (floatType == "c" || floatType == "C") {
1116 if (to_scalar)
1117 return Type::getFloatTy(ctx);
1118 return VectorType::get(Type::getFloatTy(ctx), 2, false);
1119 } else if (floatType == "z" || floatType == "Z") {
1120 if (to_scalar)
1121 return Type::getDoubleTy(ctx);
1122 return VectorType::get(Type::getDoubleTy(ctx), 2, false);
1123 } else {
1124 assert(false && "Unreachable");
1125 return nullptr;
1126 }
1127}
1128
1129IntegerType *BlasInfo::intType(LLVMContext &ctx) const {
1130 if (is64)
1131 return IntegerType::get(ctx, 64);
1132 else
1133 return IntegerType::get(ctx, 32);
1134}
1135
1136bool isAtomic(Value *origptr, bool AtomicAdd, Function *newFunc) {
1137 if (!AtomicAdd)
1138 return false;
1139 if (!origptr || !newFunc)
1140 return AtomicAdd;
1141
1142 auto TmpOrig = getBaseObject(origptr);
1143
1144 // atomics
1145 bool Atomic = AtomicAdd;
1146 auto Arch = llvm::Triple(newFunc->getParent()->getTargetTriple()).getArch();
1147
1148 // No need to do atomic on local memory for CUDA since it can't be raced
1149 // upon
1150 if (isa<AllocaInst>(TmpOrig) &&
1151 (Arch == Triple::nvptx || Arch == Triple::nvptx64 ||
1152 Arch == Triple::amd_target)) {
1153 Atomic = false;
1154 }
1155 return Atomic;
1156}
1157
1158/// Create function for type that is equivalent to memcpy but adds to
1159/// destination rather than a direct copy; dst, src, numelems
1161 Module &M, Type *elementType, unsigned dstalign, unsigned srcalign,
1162 unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity,
1163 bool atomic) {
1164 assert(elementType->isFloatingPointTy());
1165 std::string name = "__enzyme_memcpy";
1166 if (bitwidth != 64)
1167 name += std::to_string(bitwidth);
1168 name += "add_" + tofltstr(elementType) + "da" + std::to_string(dstalign) +
1169 "sa" + std::to_string(srcalign);
1170 if (dstaddr)
1171 name += "dadd" + std::to_string(dstaddr);
1172 if (srcaddr)
1173 name += "sadd" + std::to_string(srcaddr);
1174 if (runtimeActivity)
1175 name += "_runtime_activity";
1176 if (atomic)
1177 name += "_atomic";
1178 std::vector<Type *> argTys = {getPointerType(elementType, dstaddr),
1179 getPointerType(elementType, srcaddr),
1180 IntegerType::get(M.getContext(), bitwidth)};
1181 if (runtimeActivity) {
1182 argTys.push_back(Type::getInt1Ty(M.getContext())); // dst_inactive
1183 argTys.push_back(Type::getInt1Ty(M.getContext())); // src_inactive
1184 }
1185 FunctionType *FT =
1186 FunctionType::get(Type::getVoidTy(M.getContext()), argTys, false);
1187
1188 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1189
1190 if (!F->empty())
1191 return F;
1192
1193 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1194#if LLVM_VERSION_MAJOR >= 16
1195 F->setOnlyAccessesArgMemory();
1196#else
1197 F->addFnAttr(Attribute::ArgMemOnly);
1198#endif
1199 F->addFnAttr(Attribute::NoUnwind);
1200 F->addFnAttr(Attribute::AlwaysInline);
1203
1204 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1205 BasicBlock *body = BasicBlock::Create(M.getContext(), "for.body", F);
1206 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
1207
1208 Argument *dst = &*F->arg_begin();
1209 dst->setName("dst");
1210 Argument *src = dst + 1;
1211 src->setName("src");
1212 Argument *num = src + 1;
1213 num->setName("num");
1214
1215 Argument *dst_inactive = nullptr;
1216 Argument *src_inactive = nullptr;
1217 if (runtimeActivity) {
1218 dst_inactive = num + 1;
1219 dst_inactive->setName("dst_inactive");
1220 src_inactive = dst_inactive + 1;
1221 src_inactive->setName("src_inactive");
1222 }
1223
1224 BasicBlock *checkSrc = nullptr;
1225 BasicBlock *memsetDst = nullptr;
1226 if (runtimeActivity) {
1227 checkSrc = BasicBlock::Create(M.getContext(), "check_src", F, body);
1228 memsetDst = BasicBlock::Create(M.getContext(), "memset_dst", F, body);
1229 }
1230
1231 {
1232 IRBuilder<> B(entry);
1233 Value *cond = B.CreateICmpEQ(num, ConstantInt::get(num->getType(), 0));
1234 if (runtimeActivity) {
1235 cond = B.CreateOr(cond, dst_inactive);
1236 B.CreateCondBr(cond, end, checkSrc);
1237
1238 B.SetInsertPoint(checkSrc);
1239 B.CreateCondBr(src_inactive, memsetDst, body);
1240
1241 B.SetInsertPoint(memsetDst);
1242 auto elSize = (M.getDataLayout().getTypeSizeInBits(elementType) + 7) / 8;
1243 Value *dst_i8 = B.CreatePointerCast(
1244 dst, getPointerType(Type::getInt8Ty(M.getContext()), dstaddr));
1245 B.CreateMemSet(dst_i8, B.getInt8(0),
1246 B.CreateMul(num, ConstantInt::get(num->getType(), elSize),
1247 "", /*HasNUW*/ true, /*HasNSW*/ true),
1248 MaybeAlign(dstalign));
1249 B.CreateBr(end);
1250 } else {
1251 B.CreateCondBr(cond, end, body);
1252 }
1253 }
1254
1255 auto elSize = (M.getDataLayout().getTypeSizeInBits(elementType) + 7) / 8;
1256 {
1257 IRBuilder<> B(body);
1258 B.setFastMathFlags(getFast());
1259 PHINode *idx = B.CreatePHI(num->getType(), 2, "idx");
1260 idx->addIncoming(ConstantInt::get(num->getType(), 0),
1261 runtimeActivity ? checkSrc : entry);
1262
1263 Value *dsti = B.CreateInBoundsGEP(elementType, dst, idx, "dst.i");
1264 LoadInst *dstl = B.CreateLoad(elementType, dsti, "dst.i.l");
1265 StoreInst *dsts = B.CreateStore(Constant::getNullValue(elementType), dsti);
1266
1267 if (dstalign) {
1268 // If the element size is already aligned to current alignment, do nothing
1269 // e.g. elsize = double = 8, dstalign = 2
1270 if (elSize % dstalign == 0) {
1271
1272 } else if (dstalign % elSize == 0) {
1273 // Otherwise if the dst alignment is a multiple of the element size,
1274 // use the element size as the new alignment. e.g. elsize = double = 8
1275 // and alignment = 16
1276 dstalign = elSize;
1277 } else {
1278 // else alignment only applies for first element, and we lose after all
1279 // other iterattions, assume nothing
1280 dstalign = 1;
1281 }
1282 }
1283
1284 if (srcalign) {
1285 // If the element size is already aligned to current alignment, do nothing
1286 // e.g. elsize = double = 8, dstalign = 2
1287 if (elSize % srcalign == 0) {
1288
1289 } else if (srcalign % elSize == 0) {
1290 // Otherwise if the dst alignment is a multiple of the element size,
1291 // use the element size as the new alignment. e.g. elsize = double = 8
1292 // and alignment = 16
1293 srcalign = elSize;
1294 } else {
1295 // else alignment only applies for first element, and we lose after all
1296 // other iterattions, assume nothing
1297 srcalign = 1;
1298 }
1299 }
1300
1301 if (dstalign) {
1302 dstl->setAlignment(Align(dstalign));
1303 dsts->setAlignment(Align(dstalign));
1304 }
1305
1306 Value *srci = B.CreateInBoundsGEP(elementType, src, idx, "src.i");
1307 if (atomic) {
1308 B.CreateAtomicRMW(AtomicRMWInst::BinOp::FAdd, srci, dstl,
1309 MaybeAlign(srcalign), AtomicOrdering::Monotonic,
1310 SyncScope::System);
1311 } else {
1312 LoadInst *srcl = B.CreateLoad(elementType, srci, "src.i.l");
1313 StoreInst *srcs = B.CreateStore(B.CreateFAdd(srcl, dstl), srci);
1314 if (srcalign) {
1315 srcl->setAlignment(Align(srcalign));
1316 srcs->setAlignment(Align(srcalign));
1317 }
1318 }
1319
1320 Value *next =
1321 B.CreateNUWAdd(idx, ConstantInt::get(num->getType(), 1), "idx.next");
1322 idx->addIncoming(next, body);
1323 B.CreateCondBr(B.CreateICmpEQ(num, next), end, body);
1324 }
1325
1326 {
1327 IRBuilder<> B(end);
1328 B.CreateRetVoid();
1329 }
1330 return F;
1331}
1332
1333Value *lookup_with_layout(IRBuilder<> &B, Type *fpType, Value *layout,
1334 Value *const base, Value *lda, Value *row,
1335 Value *col) {
1336 Type *intType = row->getType();
1337 Value *is_row_maj =
1338 layout ? B.CreateICmpEQ(layout, ConstantInt::get(layout->getType(), 101))
1339 : B.getFalse();
1340 Value *offset = nullptr;
1341 if (col) {
1342 offset = B.CreateMul(
1343 row, CreateSelect(B, is_row_maj, lda, ConstantInt::get(intType, 1)));
1344 offset = B.CreateAdd(
1345 offset,
1346 B.CreateMul(col, CreateSelect(B, is_row_maj,
1347 ConstantInt::get(intType, 1), lda)));
1348 } else {
1349 offset = B.CreateMul(row, lda);
1350 }
1351 if (!base)
1352 return offset;
1353
1354 Value *ptr = base;
1355 if (base->getType()->isIntegerTy())
1356 ptr = B.CreateIntToPtr(ptr, getUnqual(fpType));
1357
1358#if LLVM_VERSION_MAJOR < 17
1359#if LLVM_VERSION_MAJOR >= 15
1360 if (ptr->getContext().supportsTypedPointers()) {
1361#endif
1362 if (fpType != ptr->getType()->getPointerElementType()) {
1363 ptr = B.CreatePointerCast(
1364 ptr,
1365 getPointerType(fpType,
1366 cast<PointerType>(ptr->getType())->getAddressSpace()));
1367 }
1368#if LLVM_VERSION_MAJOR >= 15
1369 }
1370#endif
1371#endif
1372 ptr = B.CreateGEP(fpType, ptr, offset);
1373
1374 if (base->getType()->isIntegerTy()) {
1375 ptr = B.CreatePtrToInt(ptr, base->getType());
1376 } else if (ptr->getType() != base->getType()) {
1377 ptr = B.CreatePointerCast(ptr, base->getType());
1378 }
1379 return ptr;
1380}
1381
1382void copy_lower_to_upper(llvm::IRBuilder<> &B, llvm::Type *fpType,
1383 BlasInfo blas, bool byRef, llvm::Value *layout,
1384 llvm::Value *islower, llvm::Value *A, llvm::Value *lda,
1385 llvm::Value *N) {
1386
1387 const bool cublasv2 =
1388 blas.prefix == "cublas" && StringRef(blas.suffix).contains("v2");
1389
1390 const bool cublas = blas.prefix == "cublas";
1391 auto &M = *B.GetInsertBlock()->getParent()->getParent();
1392
1393 llvm::Type *intType = N->getType();
1394 // add spmv diag update call if not already present
1395 auto fnc_name = "__enzyme_copy_lower_to_upper" + blas.floatType +
1396 blas.prefix + blas.suffix;
1397
1398 SmallVector<Type *, 1> tys = {islower->getType(), A->getType(),
1399 lda->getType(), N->getType()};
1400 if (layout)
1401 tys.insert(tys.begin(), layout->getType());
1402 auto ltuFT = FunctionType::get(B.getVoidTy(), tys, false);
1403
1404 auto F0 = M.getOrInsertFunction(fnc_name, ltuFT);
1405
1406 SmallVector<Value *, 1> args = {islower, A, lda, N};
1407 if (layout)
1408 args.insert(args.begin(), layout);
1409 auto C = B.CreateCall(F0, args);
1410 auto F = getFunctionFromCall(C);
1411 assert(F);
1412 if (!F->empty()) {
1413 return;
1414 }
1415
1416 // now add the implementation for the call
1417 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1418#if LLVM_VERSION_MAJOR >= 16
1419 F->setOnlyAccessesArgMemory();
1420#else
1421 F->addFnAttr(Attribute::ArgMemOnly);
1422#endif
1423 F->addFnAttr(Attribute::NoUnwind);
1424 F->addFnAttr(Attribute::AlwaysInline);
1425 if (A->getType()->isPointerTy())
1426 addFunctionNoCapture(F, 1 + ((bool)layout));
1427
1428 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1429 BasicBlock *loop = BasicBlock::Create(M.getContext(), "loop", F);
1430 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
1431
1432 auto arg = F->arg_begin();
1433 Argument *layoutarg = nullptr;
1434 if (layout) {
1435 layoutarg = arg;
1436 layoutarg->setName("layout");
1437 arg++;
1438 }
1439 auto islowerarg = arg;
1440 islowerarg->setName("islower");
1441 arg++;
1442 auto Aarg = arg;
1443 Aarg->setName("A");
1444 arg++;
1445 auto ldaarg = arg;
1446 ldaarg->setName("lda");
1447 arg++;
1448 auto Narg = arg;
1449 Narg->setName("N");
1450
1451 IRBuilder<> EB(entry);
1452
1453 auto one = ConstantInt::get(intType, 1);
1454 auto zero = ConstantInt::get(intType, 0);
1455
1456 Value *N_minus_1 = EB.CreateSub(Narg, one);
1457
1458 IRBuilder<> LB(loop);
1459
1460 auto i = LB.CreatePHI(intType, 2);
1461 i->addIncoming(zero, entry);
1462 auto i_plus_one = LB.CreateAdd(i, one, "", true, true);
1463 i->addIncoming(i_plus_one, loop);
1464
1465 // Each lookup_with_layout's two selects are bound in the order they are
1466 // emitted; the array elements themselves stay in place, since a braced
1467 // initializer is evaluated left to right.
1468 Value *countArg = to_blas_callconv(LB, LB.CreateSub(N_minus_1, i), byRef,
1469 cublas, nullptr, EB);
1470
1471 Value *aCol = CreateSelect(LB, islowerarg, i, i_plus_one);
1472 Value *aRow = CreateSelect(LB, islowerarg, i_plus_one, i);
1473 Value *aArg =
1474 lookup_with_layout(LB, fpType, layoutarg, Aarg, ldaarg, aRow, aCol);
1475
1476 Value *incACol = CreateSelect(LB, islowerarg, zero, one);
1477 Value *incARow = CreateSelect(LB, islowerarg, one, zero);
1478 Value *incAArg =
1480 lookup_with_layout(LB, fpType, layoutarg, nullptr,
1481 ldaarg, incARow, incACol),
1482 byRef, cublas, nullptr, EB);
1483
1484 Value *bCol = CreateSelect(LB, islowerarg, i_plus_one, i);
1485 Value *bRow = CreateSelect(LB, islowerarg, i, i_plus_one);
1486 Value *bArg =
1487 lookup_with_layout(LB, fpType, layoutarg, Aarg, ldaarg, bRow, bCol);
1488
1489 Value *incBCol = CreateSelect(LB, islowerarg, one, zero);
1490 Value *incBRow = CreateSelect(LB, islowerarg, zero, one);
1491 Value *incBArg =
1493 lookup_with_layout(LB, fpType, layoutarg, nullptr,
1494 ldaarg, incBRow, incBCol),
1495 byRef, cublas, nullptr, EB);
1496
1497 Value *copyArgs[] = {countArg, aArg, incAArg, bArg, incBArg};
1498
1499 Type *copyTys[] = {copyArgs[0]->getType(), copyArgs[1]->getType(),
1500 copyArgs[2]->getType(), copyArgs[3]->getType(),
1501 copyArgs[4]->getType()};
1502
1503 FunctionType *FT = FunctionType::get(B.getVoidTy(), copyTys, false);
1504
1505 auto copy_name = std::string(blas.prefix) + blas.floatType + "copy" +
1506 (cublasv2 ? "" : blas.suffix);
1507
1508 auto copyfn = M.getOrInsertFunction(copy_name, FT);
1509 LB.CreateCall(copyfn, copyArgs);
1510 if (auto F = GetFunctionFromValue(copyfn.getCallee()))
1512 LB.CreateCondBr(LB.CreateICmpEQ(i_plus_one, N_minus_1), end, loop);
1513
1514 EB.CreateCondBr(EB.CreateICmpSLE(N_minus_1, zero), end, loop);
1515 {
1516 IRBuilder<> B(end);
1517 B.CreateRetVoid();
1518 }
1519
1520 if (llvm::verifyFunction(*F, &llvm::errs())) {
1521 llvm::errs() << *F << "\n";
1522 report_fatal_error("helper function failed verification");
1523 }
1524}
1525
1526void callMemcpyStridedBlas(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas,
1527 llvm::ArrayRef<llvm::Value *> args,
1528 llvm::Type *copy_retty,
1529 llvm::ArrayRef<llvm::OperandBundleDef> bundles) {
1530 const bool cublasv2 =
1531 blas.prefix == "cublas" && StringRef(blas.suffix).contains("v2");
1532 auto copy_name = std::string(blas.prefix) + blas.floatType + "copy" +
1533 (cublasv2 ? "" : blas.suffix);
1534
1535 SmallVector<Type *, 1> tys;
1536 for (auto arg : args)
1537 tys.push_back(arg->getType());
1538
1539 FunctionType *FT = FunctionType::get(copy_retty, tys, false);
1540 auto fn = M.getOrInsertFunction(copy_name, FT);
1541 B.CreateCall(fn, args, bundles);
1542 if (auto F = GetFunctionFromValue(fn.getCallee()))
1544}
1545
1546void callMemcpyStridedLapack(llvm::IRBuilder<> &B, llvm::Module &M,
1547 BlasInfo blas, llvm::ArrayRef<llvm::Value *> args,
1548 llvm::ArrayRef<llvm::OperandBundleDef> bundles) {
1549 auto copy_name =
1550 std::string(blas.prefix) + blas.floatType + "lacpy" + blas.suffix;
1551
1552 SmallVector<Type *, 1> tys;
1553 for (auto arg : args)
1554 tys.push_back(arg->getType());
1555
1556 auto FT = FunctionType::get(Type::getVoidTy(M.getContext()), tys, false);
1557 auto fn = M.getOrInsertFunction(copy_name, FT);
1558 B.CreateCall(fn, args, bundles);
1559
1560 if (auto F = GetFunctionFromValue(fn.getCallee()))
1562}
1563
1564void callSPMVDiagUpdate(IRBuilder<> &B, Module &M, BlasInfo blas,
1565 IntegerType *IT, Type *BlasCT, Type *BlasFPT,
1566 Type *BlasPT, Type *BlasIT, Type *fpTy,
1567 ArrayRef<Value *> args,
1568 ArrayRef<OperandBundleDef> bundles, bool byRef,
1569 bool julia_decl) {
1570 // add spmv diag update call if not already present
1571 auto fnc_name = "__enzyme_spmv_diag" + blas.floatType + blas.suffix;
1572
1573 // spmvDiagHelper(uplo, n, alpha, x, incx, ya, incy, APa)
1574 auto FDiagUpdateT = FunctionType::get(
1575 B.getVoidTy(),
1576 {BlasCT, BlasIT, BlasFPT, BlasPT, BlasIT, BlasPT, BlasIT, BlasPT}, false);
1577 Function *F =
1578 cast<Function>(M.getOrInsertFunction(fnc_name, FDiagUpdateT).getCallee());
1579
1580 if (!F->empty()) {
1581 B.CreateCall(F, args, bundles);
1582 return;
1583 }
1584
1585 // now add the implementation for the call
1586 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1587#if LLVM_VERSION_MAJOR >= 16
1588 F->setOnlyAccessesArgMemory();
1589#else
1590 F->addFnAttr(Attribute::ArgMemOnly);
1591#endif
1592 F->addFnAttr(Attribute::NoUnwind);
1593 F->addFnAttr(Attribute::AlwaysInline);
1594 if (!julia_decl) {
1598 F->addParamAttr(3, Attribute::NoAlias);
1599 F->addParamAttr(5, Attribute::NoAlias);
1600 F->addParamAttr(7, Attribute::NoAlias);
1601 F->addParamAttr(3, Attribute::ReadOnly);
1602 F->addParamAttr(5, Attribute::ReadOnly);
1603 if (byRef) {
1605 F->addParamAttr(2, Attribute::NoAlias);
1606 F->addParamAttr(2, Attribute::ReadOnly);
1607 }
1608 }
1609
1610 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1611 BasicBlock *init = BasicBlock::Create(M.getContext(), "init", F);
1612 BasicBlock *uper_code = BasicBlock::Create(M.getContext(), "uper", F);
1613 BasicBlock *lower_code = BasicBlock::Create(M.getContext(), "lower", F);
1614 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
1615
1616 // spmvDiagHelper(uplo, n, alpha, x, incx, ya, incy, APa)
1617 auto blasuplo = F->arg_begin();
1618 blasuplo->setName("blasuplo");
1619 auto blasn = blasuplo + 1;
1620 blasn->setName("blasn");
1621 auto blasalpha = blasn + 1;
1622 blasalpha->setName("blasalpha");
1623 auto blasx = blasalpha + 1;
1624 blasx->setName("blasx");
1625 auto blasincx = blasx + 1;
1626 blasincx->setName("blasincx");
1627 auto blasdy = blasx + 1;
1628 blasdy->setName("blasdy");
1629 auto blasincy = blasdy + 1;
1630 blasincy->setName("blasincy");
1631 auto blasdAP = blasincy + 1;
1632 blasdAP->setName("blasdAP");
1633
1634 // TODO: consider cblas_layout
1635
1636 // https://dl.acm.org/doi/pdf/10.1145/3382191
1637 // Following example is Fortran based, thus 1 indexed
1638 // if(uplo == 'u' .or. uplo == 'U') then
1639 // k = 0
1640 // do i = 1,n
1641 // k = k+i
1642 // APa(k) = APa(k) - alpha*x(1 + (i-1)*incx)*ya(1 + (i-1)*incy)
1643 // end do
1644 // else
1645 // k = 1
1646 // do i = 1,n
1647 // APa(k) = APa(k) - alpha*x(1 + (i-1)*incx)*ya(1 + (i-1)*incy)
1648 // k = k+n-i+1
1649 // end do
1650 // end if
1651 {
1652 IRBuilder<> B1(entry);
1653 Value *n = load_if_ref(B1, IT, blasn, byRef);
1654 Value *incx = load_if_ref(B1, IT, blasincx, byRef);
1655 Value *incy = load_if_ref(B1, IT, blasincy, byRef);
1656 Value *alpha = blasalpha;
1657 if (byRef) {
1658 auto VP = B1.CreatePointerCast(
1659 blasalpha,
1661 fpTy,
1662 cast<PointerType>(blasalpha->getType())->getAddressSpace()));
1663 alpha = B1.CreateLoad(fpTy, VP);
1664 }
1665 Value *is_l = is_lower(B1, blasuplo, byRef, /*cublas*/ false);
1666 B1.CreateCondBr(B1.CreateICmpEQ(n, ConstantInt::get(IT, 0)), end, init);
1667
1668 IRBuilder<> B2(init);
1669 Value *xfloat = B2.CreatePointerCast(
1670 blasx,
1671 getPointerType(fpTy,
1672 cast<PointerType>(blasx->getType())->getAddressSpace()));
1673 Value *dyfloat = B2.CreatePointerCast(
1674 blasdy,
1676 fpTy, cast<PointerType>(blasdy->getType())->getAddressSpace()));
1677 Value *dAPfloat = B2.CreatePointerCast(
1678 blasdAP,
1680 fpTy, cast<PointerType>(blasdAP->getType())->getAddressSpace()));
1681 B2.CreateCondBr(is_l, lower_code, uper_code);
1682
1683 IRBuilder<> B3(uper_code);
1684 B3.setFastMathFlags(getFast());
1685 {
1686 PHINode *iter = B3.CreatePHI(IT, 2, "iteration");
1687 PHINode *kval = B3.CreatePHI(IT, 2, "k");
1688 iter->addIncoming(ConstantInt::get(IT, 0), init);
1689 kval->addIncoming(ConstantInt::get(IT, 0), init);
1690 Value *iternext =
1691 B3.CreateAdd(iter, ConstantInt::get(IT, 1), "iter.next");
1692 // 0, 2, 5, 9, 14, 20, 27, 35, 44, 54, ... are diag elements
1693 Value *kvalnext = B3.CreateAdd(kval, iternext, "k.next");
1694 iter->addIncoming(iternext, uper_code);
1695 kval->addIncoming(kvalnext, uper_code);
1696
1697 Value *xidx = B3.CreateNUWMul(iter, incx, "x.idx");
1698 Value *yidx = B3.CreateNUWMul(iter, incy, "y.idx");
1699 Value *x = B3.CreateInBoundsGEP(fpTy, xfloat, xidx, "x.ptr");
1700 Value *y = B3.CreateInBoundsGEP(fpTy, dyfloat, yidx, "y.ptr");
1701 Value *xval = B3.CreateLoad(fpTy, x, "x.val");
1702 Value *yval = B3.CreateLoad(fpTy, y, "y.val");
1703 Value *xy = B3.CreateFMul(xval, yval, "xy");
1704 Value *xyalpha = B3.CreateFMul(xy, alpha, "xy.alpha");
1705 Value *kptr = B3.CreateInBoundsGEP(fpTy, dAPfloat, kval, "k.ptr");
1706 Value *kvalloaded = B3.CreateLoad(fpTy, kptr, "k.val");
1707 Value *kvalnew = B3.CreateFSub(kvalloaded, xyalpha, "k.val.new");
1708 B3.CreateStore(kvalnew, kptr);
1709
1710 B3.CreateCondBr(B3.CreateICmpEQ(iternext, n), end, uper_code);
1711 }
1712
1713 IRBuilder<> B4(lower_code);
1714 B4.setFastMathFlags(getFast());
1715 {
1716 PHINode *iter = B4.CreatePHI(IT, 2, "iteration");
1717 PHINode *kval = B4.CreatePHI(IT, 2, "k");
1718 iter->addIncoming(ConstantInt::get(IT, 0), init);
1719 kval->addIncoming(ConstantInt::get(IT, 0), init);
1720 Value *iternext =
1721 B4.CreateAdd(iter, ConstantInt::get(IT, 1), "iter.next");
1722 Value *ktmp = B4.CreateAdd(n, ConstantInt::get(IT, 1), "tmp.val");
1723 Value *ktmp2 = B4.CreateSub(ktmp, iternext, "tmp.val.other");
1724 Value *kvalnext = B4.CreateAdd(kval, ktmp2, "k.next");
1725 iter->addIncoming(iternext, lower_code);
1726 kval->addIncoming(kvalnext, lower_code);
1727
1728 Value *xidx = B4.CreateNUWMul(iter, incx, "x.idx");
1729 Value *yidx = B4.CreateNUWMul(iter, incy, "y.idx");
1730 Value *x = B4.CreateInBoundsGEP(fpTy, xfloat, xidx, "x.ptr");
1731 Value *y = B4.CreateInBoundsGEP(fpTy, dyfloat, yidx, "y.ptr");
1732 Value *xval = B4.CreateLoad(fpTy, x, "x.val");
1733 Value *yval = B4.CreateLoad(fpTy, y, "y.val");
1734 Value *xy = B4.CreateFMul(xval, yval, "xy");
1735 Value *xyalpha = B4.CreateFMul(xy, alpha, "xy.alpha");
1736 Value *kptr = B4.CreateInBoundsGEP(fpTy, dAPfloat, kval, "k.ptr");
1737 Value *kvalloaded = B4.CreateLoad(fpTy, kptr, "k.val");
1738 Value *kvalnew = B4.CreateFSub(kvalloaded, xyalpha, "k.val.new");
1739 B4.CreateStore(kvalnew, kptr);
1740
1741 B4.CreateCondBr(B4.CreateICmpEQ(iternext, n), end, lower_code);
1742 }
1743
1744 IRBuilder<> B5(end);
1745 B5.CreateRetVoid();
1746 }
1747 B.CreateCall(F, args, bundles);
1748 return;
1749}
1750
1751llvm::CallInst *
1752getorInsertInnerProd(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas,
1753 IntegerType *IT, Type *BlasPT, Type *BlasIT, Type *fpTy,
1754 llvm::ArrayRef<llvm::Value *> args,
1755 const llvm::ArrayRef<llvm::OperandBundleDef> bundles,
1756 bool byRef, bool cublas, bool julia_decl) {
1757 assert(fpTy->isFloatingPointTy());
1758
1759 // add inner_prod call if not already present
1760 std::string prod_name = "__enzyme_inner_prod" + blas.floatType + blas.suffix;
1761 auto FInnerProdT =
1762 FunctionType::get(fpTy, {BlasIT, BlasIT, BlasPT, BlasIT, BlasPT}, false);
1763 Function *F =
1764 cast<Function>(M.getOrInsertFunction(prod_name, FInnerProdT).getCallee());
1765
1766 if (!F->empty())
1767 return B.CreateCall(F, args, bundles);
1768
1769 // add dot call if not already present
1770 std::string dot_name = blas.prefix + blas.floatType + "dot" + blas.suffix;
1771 auto FDotT =
1772 FunctionType::get(fpTy, {BlasIT, BlasPT, BlasIT, BlasPT, BlasIT}, false);
1773 auto FDot = M.getOrInsertFunction(dot_name, FDotT);
1774
1775 // now add the implementation for the inner_prod call
1776 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1777#if LLVM_VERSION_MAJOR >= 16
1778 F->setOnlyAccessesArgMemory();
1779 F->setOnlyReadsMemory();
1780#else
1781 F->addFnAttr(Attribute::ArgMemOnly);
1782 F->addFnAttr(Attribute::ReadOnly);
1783#endif
1784 F->addFnAttr(Attribute::NoUnwind);
1785 F->addFnAttr(Attribute::AlwaysInline);
1786 if (!julia_decl) {
1789 F->addParamAttr(2, Attribute::NoAlias);
1790 F->addParamAttr(4, Attribute::NoAlias);
1791 F->addParamAttr(2, Attribute::ReadOnly);
1792 F->addParamAttr(4, Attribute::ReadOnly);
1793 }
1794
1795 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1796 BasicBlock *init = BasicBlock::Create(M.getContext(), "init.idx", F);
1797 BasicBlock *fastPath = BasicBlock::Create(M.getContext(), "fast.path", F);
1798 BasicBlock *body = BasicBlock::Create(M.getContext(), "for.body", F);
1799 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
1800
1801 // This is the .td declaration which we need to match
1802 // No need to support ld for the second matrix, as it will
1803 // always be based on a matrix which we allocated (contiguous)
1804 //(FrobInnerProd<> $m, $n, adj<"C">, $ldc, use<"AB">)
1805
1806 auto blasm = F->arg_begin();
1807 blasm->setName("blasm");
1808 auto blasn = blasm + 1;
1809 blasn->setName("blasn");
1810 auto matA = blasn + 1;
1811 matA->setName("A");
1812 auto blaslda = matA + 1;
1813 blaslda->setName("lda");
1814 auto matB = blaslda + 1;
1815 matB->setName("B");
1816
1817 {
1818 IRBuilder<> B1(entry);
1819 Value *blasOne = to_blas_callconv(B1, ConstantInt::get(IT, 1), byRef,
1820 cublas, nullptr, B1, "constant.one");
1821
1822 if (blasOne->getType() != BlasIT)
1823 blasOne = B1.CreatePointerCast(blasOne, BlasIT, "intcast.constant.one");
1824
1825 Value *m = load_if_ref(B1, IT, blasm, byRef);
1826 Value *n = load_if_ref(B1, IT, blasn, byRef);
1827 Value *size = B1.CreateNUWMul(m, n, "mat.size");
1828 Value *blasSize = to_blas_callconv(
1829 B1, size, byRef, cublas, julia_decl ? IT : nullptr, B1, "mat.size");
1830
1831 if (blasSize->getType() != BlasIT)
1832 blasSize = B1.CreatePointerCast(blasSize, BlasIT, "intcast.mat.size");
1833 B1.CreateCondBr(B1.CreateICmpEQ(size, ConstantInt::get(IT, 0)), end, init);
1834
1835 IRBuilder<> B2(init);
1836 B2.setFastMathFlags(getFast());
1837 Value *lda = load_if_ref(B2, IT, blaslda, byRef);
1838 Value *Afloat = B2.CreatePointerCast(
1839 matA, getPointerType(
1840 fpTy, cast<PointerType>(matA->getType())->getAddressSpace()));
1841 Value *Bfloat = B2.CreatePointerCast(
1842 matB, getPointerType(
1843 fpTy, cast<PointerType>(matB->getType())->getAddressSpace()));
1844 B2.CreateCondBr(B2.CreateICmpEQ(m, lda), fastPath, body);
1845
1846 // our second matrix is always continuos, by construction.
1847 // If our first matrix is continuous too (lda == m), then we can
1848 // use a single dot call.
1849 IRBuilder<> B3(fastPath);
1850 B3.setFastMathFlags(getFast());
1851 Value *blasA = B3.CreatePointerCast(matA, BlasPT);
1852 Value *blasB = B3.CreatePointerCast(matB, BlasPT);
1853 Value *fastSum =
1854 B3.CreateCall(FDot, {blasSize, blasA, blasOne, blasB, blasOne});
1855 B3.CreateBr(end);
1856
1857 IRBuilder<> B4(body);
1858 B4.setFastMathFlags(getFast());
1859 PHINode *Aidx = B4.CreatePHI(IT, 2, "Aidx");
1860 PHINode *Bidx = B4.CreatePHI(IT, 2, "Bidx");
1861 PHINode *iter = B4.CreatePHI(IT, 2, "iteration");
1862 PHINode *sum = B4.CreatePHI(fpTy, 2, "sum");
1863 Aidx->addIncoming(ConstantInt::get(IT, 0), init);
1864 Bidx->addIncoming(ConstantInt::get(IT, 0), init);
1865 iter->addIncoming(ConstantInt::get(IT, 0), init);
1866 sum->addIncoming(ConstantFP::get(fpTy, 0.0), init);
1867
1868 Value *Ai = B4.CreateInBoundsGEP(fpTy, Afloat, Aidx, "A.i");
1869 Value *Bi = B4.CreateInBoundsGEP(fpTy, Bfloat, Bidx, "B.i");
1870 Value *AiDot = B4.CreatePointerCast(Ai, BlasPT);
1871 Value *BiDot = B4.CreatePointerCast(Bi, BlasPT);
1872 Value *newDot =
1873 B4.CreateCall(FDot, {blasm, AiDot, blasOne, BiDot, blasOne});
1874
1875 Value *Anext = B4.CreateNUWAdd(Aidx, lda, "Aidx.next");
1876 Value *Bnext = B4.CreateNUWAdd(Aidx, m, "Bidx.next");
1877 Value *iternext = B4.CreateAdd(iter, ConstantInt::get(IT, 1), "iter.next");
1878 Value *sumnext = B4.CreateFAdd(sum, newDot);
1879
1880 iter->addIncoming(iternext, body);
1881 Aidx->addIncoming(Anext, body);
1882 Bidx->addIncoming(Bnext, body);
1883 sum->addIncoming(sumnext, body);
1884
1885 B4.CreateCondBr(B4.CreateICmpEQ(iter, n), end, body);
1886
1887 IRBuilder<> B5(end);
1888 PHINode *res = B5.CreatePHI(fpTy, 3, "res");
1889 res->addIncoming(ConstantFP::get(fpTy, 0.0), entry);
1890 res->addIncoming(sum, body);
1891 res->addIncoming(fastSum, fastPath);
1892 B5.CreateRet(res);
1893 }
1894
1895 auto res = B.CreateCall(F, args, bundles);
1896 if (auto F = GetFunctionFromValue(FDot.getCallee()))
1898 return res;
1899}
1900
1901Function *getOrInsertMemcpyStrided(Module &M, Type *elementType, PointerType *T,
1902 Type *IT, unsigned dstalign,
1903 unsigned srcalign) {
1904 assert(elementType->isFloatingPointTy());
1905 std::string name = "__enzyme_memcpy_" + tofltstr(elementType) + "_" +
1906 std::to_string(cast<IntegerType>(IT)->getBitWidth()) +
1907 "_da" + std::to_string(dstalign) + "sa" +
1908 std::to_string(srcalign) + "stride";
1909 FunctionType *FT =
1910 FunctionType::get(Type::getVoidTy(M.getContext()), {T, T, IT, IT}, false);
1911
1912 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
1913
1914 if (!F->empty())
1915 return F;
1916
1917 F->setLinkage(Function::LinkageTypes::InternalLinkage);
1918#if LLVM_VERSION_MAJOR >= 16
1919 F->setOnlyAccessesArgMemory();
1920#else
1921 F->addFnAttr(Attribute::ArgMemOnly);
1922#endif
1923 F->addFnAttr(Attribute::NoUnwind);
1924 F->addFnAttr(Attribute::AlwaysInline);
1926 F->addParamAttr(0, Attribute::NoAlias);
1928 F->addParamAttr(1, Attribute::NoAlias);
1929 F->addParamAttr(0, Attribute::WriteOnly);
1930 F->addParamAttr(1, Attribute::ReadOnly);
1931
1932 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
1933 BasicBlock *init = BasicBlock::Create(M.getContext(), "init.idx", F);
1934 BasicBlock *body = BasicBlock::Create(M.getContext(), "for.body", F);
1935 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
1936
1937 auto dst = F->arg_begin();
1938 dst->setName("dst");
1939 auto src = dst + 1;
1940 src->setName("src");
1941 auto num = src + 1;
1942 num->setName("num");
1943 auto stride = num + 1;
1944 stride->setName("stride");
1945
1946 {
1947 IRBuilder<> B(entry);
1948 B.CreateCondBr(B.CreateICmpEQ(num, ConstantInt::get(num->getType(), 0)),
1949 end, init);
1950 }
1951
1952 {
1953 IRBuilder<> B2(init);
1954 B2.setFastMathFlags(getFast());
1955 Value *a = B2.CreateNSWSub(ConstantInt::get(num->getType(), 1), num, "a");
1956 Value *negidx = B2.CreateNSWMul(a, stride, "negidx");
1957 // Value *negidx =
1958 // B2.CreateNSWAdd(b, ConstantInt::get(num->getType(), 1),
1959 // "negidx");
1960 Value *isneg =
1961 B2.CreateICmpSLT(stride, ConstantInt::get(num->getType(), 0), "is.neg");
1962 Value *startidx = B2.CreateSelect(
1963 isneg, negidx, ConstantInt::get(num->getType(), 0), "startidx");
1964 B2.CreateBr(body);
1965 //}
1966
1967 //{
1968 IRBuilder<> B(body);
1969 B.setFastMathFlags(getFast());
1970 PHINode *idx = B.CreatePHI(num->getType(), 2, "idx");
1971 PHINode *sidx = B.CreatePHI(num->getType(), 2, "sidx");
1972 idx->addIncoming(ConstantInt::get(num->getType(), 0), init);
1973 sidx->addIncoming(startidx, init);
1974
1975 Value *dsti = B.CreateInBoundsGEP(elementType, dst, idx, "dst.i");
1976 Value *srci = B.CreateInBoundsGEP(elementType, src, sidx, "src.i");
1977 LoadInst *srcl = B.CreateLoad(elementType, srci, "src.i.l");
1978 StoreInst *dsts = B.CreateStore(srcl, dsti);
1979
1980 if (dstalign) {
1981 dsts->setAlignment(Align(dstalign));
1982 }
1983 if (srcalign) {
1984 srcl->setAlignment(Align(srcalign));
1985 }
1986
1987 Value *next =
1988 B.CreateNSWAdd(idx, ConstantInt::get(num->getType(), 1), "idx.next");
1989 Value *snext = B.CreateNSWAdd(sidx, stride, "sidx.next");
1990 idx->addIncoming(next, body);
1991 sidx->addIncoming(snext, body);
1992 B.CreateCondBr(B.CreateICmpEQ(num, next), end, body);
1993 }
1994
1995 {
1996 IRBuilder<> B(end);
1997 B.CreateRetVoid();
1998 }
1999
2000 return F;
2001}
2002
2003Function *getOrInsertMemcpyMat(Module &Mod, Type *elementType, PointerType *PT,
2004 IntegerType *IT, unsigned dstalign,
2005 unsigned srcalign) {
2006 assert(elementType->isFPOrFPVectorTy());
2007#if LLVM_VERSION_MAJOR < 17
2008#if LLVM_VERSION_MAJOR >= 15
2009 if (Mod.getContext().supportsTypedPointers()) {
2010#endif
2011#if LLVM_VERSION_MAJOR >= 13
2012 if (!PT->isOpaquePointerTy())
2013#endif
2014 assert(PT->getPointerElementType() == elementType);
2015#if LLVM_VERSION_MAJOR >= 15
2016 }
2017#endif
2018#endif
2019 std::string name = "__enzyme_memcpy_" + tofltstr(elementType) + "_mat_" +
2020 std::to_string(cast<IntegerType>(IT)->getBitWidth());
2021 FunctionType *FT = FunctionType::get(Type::getVoidTy(Mod.getContext()),
2022 {PT, PT, IT, IT, IT}, false);
2023
2024 Function *F = cast<Function>(Mod.getOrInsertFunction(name, FT).getCallee());
2025
2026 if (!F->empty())
2027 return F;
2028
2029 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2030#if LLVM_VERSION_MAJOR >= 16
2031 F->setOnlyAccessesArgMemory();
2032#else
2033 F->addFnAttr(Attribute::ArgMemOnly);
2034#endif
2035 F->addFnAttr(Attribute::NoUnwind);
2036 F->addFnAttr(Attribute::AlwaysInline);
2038 F->addParamAttr(0, Attribute::NoAlias);
2040 F->addParamAttr(1, Attribute::NoAlias);
2041 F->addParamAttr(0, Attribute::WriteOnly);
2042 F->addParamAttr(1, Attribute::ReadOnly);
2043
2044 BasicBlock *entry = BasicBlock::Create(F->getContext(), "entry", F);
2045 BasicBlock *init = BasicBlock::Create(F->getContext(), "init.idx", F);
2046 BasicBlock *body = BasicBlock::Create(F->getContext(), "for.body", F);
2047 BasicBlock *initend = BasicBlock::Create(F->getContext(), "init.end", F);
2048 BasicBlock *end = BasicBlock::Create(F->getContext(), "for.end", F);
2049
2050 auto dst = F->arg_begin();
2051 dst->setName("dst");
2052 auto src = dst + 1;
2053 src->setName("src");
2054 auto M = src + 1;
2055 M->setName("M");
2056 auto N = M + 1;
2057 N->setName("N");
2058 auto LDA = N + 1;
2059 LDA->setName("LDA");
2060
2061 {
2062 IRBuilder<> B(entry);
2063 Value *l0 = B.CreateICmpEQ(M, ConstantInt::get(IT, 0));
2064 Value *l1 = B.CreateICmpEQ(N, ConstantInt::get(IT, 0));
2065 // Don't copy a 0*0 matrix
2066 B.CreateCondBr(B.CreateOr(l0, l1), end, init);
2067 }
2068
2069 PHINode *j;
2070 {
2071 IRBuilder<> B(init);
2072 j = B.CreatePHI(IT, 2, "j");
2073 j->addIncoming(ConstantInt::get(IT, 0), entry);
2074 B.CreateBr(body);
2075 }
2076
2077 {
2078 IRBuilder<> B(body);
2079 PHINode *i = B.CreatePHI(IT, 2, "i");
2080 i->addIncoming(ConstantInt::get(IT, 0), init);
2081
2082 Value *dsti = B.CreateInBoundsGEP(
2083 elementType, dst,
2084 B.CreateAdd(i, B.CreateMul(j, M, "", true, true), "", true, true),
2085 "dst.i");
2086 Value *srci = B.CreateInBoundsGEP(
2087 elementType, src,
2088 B.CreateAdd(i, B.CreateMul(j, LDA, "", true, true), "", true, true),
2089 "dst.i");
2090 LoadInst *srcl = B.CreateLoad(elementType, srci, "src.i.l");
2091
2092 StoreInst *dsts = B.CreateStore(srcl, dsti);
2093
2094 if (dstalign) {
2095 dsts->setAlignment(Align(dstalign));
2096 }
2097 if (srcalign) {
2098 srcl->setAlignment(Align(srcalign));
2099 }
2100
2101 Value *nexti =
2102 B.CreateAdd(i, ConstantInt::get(IT, 1), "i.next", true, true);
2103 i->addIncoming(nexti, body);
2104 B.CreateCondBr(B.CreateICmpEQ(nexti, M), initend, body);
2105 }
2106
2107 {
2108 IRBuilder<> B(initend);
2109 Value *nextj =
2110 B.CreateAdd(j, ConstantInt::get(IT, 1), "j.next", true, true);
2111 j->addIncoming(nextj, initend);
2112 B.CreateCondBr(B.CreateICmpEQ(nextj, N), end, init);
2113 }
2114
2115 {
2116 IRBuilder<> B(end);
2117 B.CreateRetVoid();
2118 }
2119
2120 return F;
2121}
2122
2124 Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT,
2125 IntegerType *CT, unsigned dstalign, unsigned srcalign, bool zeroSrc) {
2126 assert(elementType->isFPOrFPVectorTy());
2127#if LLVM_VERSION_MAJOR < 17
2128#if LLVM_VERSION_MAJOR >= 15
2129 if (Mod.getContext().supportsTypedPointers()) {
2130#endif
2131#if LLVM_VERSION_MAJOR >= 13
2132 if (!PT->isOpaquePointerTy())
2133#endif
2134 assert(PT->getPointerElementType() == elementType);
2135#if LLVM_VERSION_MAJOR >= 15
2136 }
2137#endif
2138#endif
2139 std::string name = "__enzyme_dmemcpy_" + tofltstr(elementType) + "_mat_" +
2140 std::to_string(cast<IntegerType>(IT)->getBitWidth()) +
2141 (zeroSrc ? "_zero" : "");
2142 FunctionType *FT = FunctionType::get(Type::getVoidTy(Mod.getContext()),
2143 {CT, IT, IT, PT, IT, PT, IT}, false);
2144
2145 Function *F = cast<Function>(Mod.getOrInsertFunction(name, FT).getCallee());
2146
2147 if (!F->empty())
2148 return F;
2149
2150 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2151#if LLVM_VERSION_MAJOR >= 16
2152 F->setOnlyAccessesArgMemory();
2153#else
2154 F->addFnAttr(Attribute::ArgMemOnly);
2155#endif
2156 F->addFnAttr(Attribute::NoUnwind);
2157 F->addFnAttr(Attribute::AlwaysInline);
2158 F->addParamAttr(3, Attribute::NoAlias);
2159 F->addParamAttr(5, Attribute::NoAlias);
2160
2161 BasicBlock *entry = BasicBlock::Create(F->getContext(), "entry", F);
2162 BasicBlock *swtch = BasicBlock::Create(F->getContext(), "swtch", F);
2163 BasicBlock *Ginit = BasicBlock::Create(F->getContext(), "Ginit.idx", F);
2164 BasicBlock *Uinit = BasicBlock::Create(F->getContext(), "Uinit.idx", F);
2165 BasicBlock *Linit = BasicBlock::Create(F->getContext(), "Linit.idx", F);
2166 BasicBlock *end = BasicBlock::Create(F->getContext(), "for.end", F);
2167
2168 auto uplo = F->arg_begin();
2169 uplo->setName("uplo");
2170 auto M = uplo + 1;
2171 M->setName("M");
2172 auto N = M + 1;
2173 N->setName("N");
2174
2175 auto dst = N + 1;
2176 dst->setName("dst");
2177 auto ldst = dst + 1;
2178 ldst->setName("ldst");
2179 auto src = ldst + 1;
2180 src->setName("src");
2181 auto lsrc = src + 1;
2182 lsrc->setName("lsrc");
2183
2184 {
2185 IRBuilder<> B(entry);
2186 Value *l0 = B.CreateICmpEQ(M, ConstantInt::get(IT, 0));
2187 Value *l1 = B.CreateICmpEQ(N, ConstantInt::get(IT, 0));
2188 // Don't copy a 0*0 matrix
2189 B.CreateCondBr(B.CreateOr(l0, l1), end, swtch);
2190 }
2191
2192 {
2193 IRBuilder<> B(swtch);
2194 auto swtchT = B.CreateSwitch(uplo, Ginit);
2195 swtchT->addCase(ConstantInt::get(CT, 'U'), Uinit);
2196 swtchT->addCase(ConstantInt::get(CT, 'L'), Linit);
2197 }
2198
2199 std::pair<char, BasicBlock *> todo[] = {
2200 {'G', Ginit}, {'U', Uinit}, {'L', Linit}};
2201 for (auto &&[direction, init] : todo) {
2202
2203 std::string dir(1, direction);
2204 BasicBlock *body = BasicBlock::Create(F->getContext(), dir + "for.body", F);
2205 BasicBlock *initend =
2206 BasicBlock::Create(F->getContext(), dir + "init.end", F);
2207
2208 Value *istart = ConstantInt::get(IT, 0);
2209 Value *iend = M;
2210
2211 PHINode *j;
2212 {
2213 IRBuilder<> B(init);
2214 j = B.CreatePHI(IT, 2, dir + "j");
2215 j->addIncoming(ConstantInt::get(IT, 0), swtch);
2216
2217 if (direction == 'L') {
2218 istart = j;
2219 } else if (direction == 'U') {
2220 auto jp1 = B.CreateAdd(j, ConstantInt::get(IT, 1), "", true, true);
2221 iend = B.CreateSelect(B.CreateICmpULT(jp1, M), jp1, M);
2222 }
2223
2224 B.CreateBr(body);
2225 }
2226
2227 {
2228 IRBuilder<> B(body);
2229 PHINode *i = B.CreatePHI(IT, 2, dir + "i");
2230 i->addIncoming(istart, init);
2231
2232 Value *srci = B.CreateInBoundsGEP(
2233 elementType, src,
2234 B.CreateAdd(i, B.CreateMul(j, lsrc, "", true, true), "", true, true),
2235 dir + "src.i");
2236
2237 Value *dsti = B.CreateInBoundsGEP(
2238 elementType, dst,
2239 B.CreateAdd(i, B.CreateMul(j, ldst, "", true, true), "", true, true),
2240 dir + "dst.i");
2241 LoadInst *srcl = B.CreateLoad(elementType, srci, dir + "src.i.l");
2242 LoadInst *dstl = B.CreateLoad(elementType, dsti, dir + "dst.i.l");
2243 auto res = B.CreateFAdd(srcl, dstl);
2244 StoreInst *dsts = B.CreateStore(res, dsti);
2245 StoreInst *srcs = nullptr;
2246 if (zeroSrc)
2247 srcs = B.CreateStore(Constant::getNullValue(res->getType()), srci);
2248 if (dstalign) {
2249 dsts->setAlignment(Align(dstalign));
2250 dstl->setAlignment(Align(dstalign));
2251 }
2252 if (srcalign) {
2253 if (zeroSrc)
2254 srcs->setAlignment(Align(srcalign));
2255 srcl->setAlignment(Align(srcalign));
2256 }
2257
2258 Value *nexti =
2259 B.CreateAdd(i, ConstantInt::get(IT, 1), dir + "i.next", true, true);
2260 i->addIncoming(nexti, body);
2261 B.CreateCondBr(B.CreateICmpEQ(nexti, iend), initend, body);
2262 }
2263
2264 {
2265 IRBuilder<> B(initend);
2266 Value *nextj =
2267 B.CreateAdd(j, ConstantInt::get(IT, 1), dir + "j.next", true, true);
2268 j->addIncoming(nextj, initend);
2269 B.CreateCondBr(B.CreateICmpEQ(nextj, N), end, init);
2270 }
2271 }
2272
2273 {
2274 IRBuilder<> B(end);
2275 B.CreateRetVoid();
2276 }
2277
2278 return F;
2279}
2280
2281// TODO implement differential memmove
2283 Module &M, Type *T, unsigned dstalign, unsigned srcalign, unsigned dstaddr,
2284 unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic) {
2286 llvm::errs()
2287 << "warning: didn't implement memmove, using memcpy as fallback "
2288 "which can result in errors\n";
2289 return getOrInsertDifferentialFloatMemcpy(M, T, dstalign, srcalign, dstaddr,
2290 srcaddr, bitwidth, runtimeActivity,
2291 atomic);
2292}
2293
2294FunctionCallee getOrInsertPerCallingConv(Module &M, Function *templateFn,
2295 StringRef callee, FunctionType *FT) {
2296 auto res = M.getOrInsertFunction(
2297 getRenamedPerCallingConv(templateFn->getName(), callee), FT);
2298 if (auto F = dyn_cast<Function>(res.getCallee()))
2299 if (!F->hasFnAttribute("enzyme_math"))
2300 F->addFnAttr("enzyme_math", callee);
2301 return res;
2302}
2303
2304Function *getOrInsertCheckedFree(Module &M, CallInst *call, Type *Ty,
2305 unsigned width) {
2306 FunctionType *FreeTy = call->getFunctionType();
2307 Value *Free = call->getCalledOperand();
2308 AttributeList FreeAttributes = call->getAttributes();
2309 CallingConv::ID CallingConvention = call->getCallingConv();
2310
2311 std::string name = "__enzyme_checked_free_" + std::to_string(width);
2312
2313 auto callname = getFuncNameFromCall(call);
2314 if (callname != "free")
2315 name += "_" + callname.str();
2316
2317 SmallVector<Type *, 3> types;
2318 types.push_back(Ty);
2319 for (unsigned i = 0; i < width; i++) {
2320 types.push_back(Ty);
2321 }
2322#if LLVM_VERSION_MAJOR >= 14
2323 for (size_t i = 1; i < call->arg_size(); i++)
2324#else
2325 for (size_t i = 1; i < call->getNumArgOperands(); i++)
2326#endif
2327 {
2328 types.push_back(call->getArgOperand(i)->getType());
2329 }
2330
2331 FunctionType *FT =
2332 FunctionType::get(Type::getVoidTy(M.getContext()), types, false);
2333
2334 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2335
2336 if (!F->empty())
2337 return F;
2338
2339 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2340#if LLVM_VERSION_MAJOR >= 16
2341 F->setOnlyAccessesArgMemory();
2342#else
2343 F->addFnAttr(Attribute::ArgMemOnly);
2344#endif
2345 F->addFnAttr(Attribute::NoUnwind);
2346 F->addFnAttr(Attribute::AlwaysInline);
2347
2348 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
2349 BasicBlock *free0 = BasicBlock::Create(M.getContext(), "free0", F);
2350 BasicBlock *end = BasicBlock::Create(M.getContext(), "end", F);
2351
2352 IRBuilder<> EntryBuilder(entry);
2353 IRBuilder<> Free0Builder(free0);
2354 IRBuilder<> EndBuilder(end);
2355
2356 auto primal = F->arg_begin();
2357 Argument *first_shadow = F->arg_begin() + 1;
2358 // A CUdeviceptr is passed as an integer, which cannot carry nocapture.
2359 if (Ty->isPointerTy()) {
2362 }
2363
2364 Value *isNotEqual = EntryBuilder.CreateICmpNE(primal, first_shadow);
2365 EntryBuilder.CreateCondBr(isNotEqual, free0, end);
2366
2367 SmallVector<Value *, 1> args = {first_shadow};
2368#if LLVM_VERSION_MAJOR >= 14
2369 for (size_t i = 1; i < call->arg_size(); i++)
2370#else
2371 for (size_t i = 1; i < call->getNumArgOperands(); i++)
2372#endif
2373 {
2374 args.push_back(F->arg_begin() + width + i);
2375 }
2376
2377 CallInst *CI = Free0Builder.CreateCall(FreeTy, Free, args);
2378 CI->setAttributes(FreeAttributes);
2379 CI->setCallingConv(CallingConvention);
2380
2381 if (width > 1) {
2382 Value *checkResult = nullptr;
2383 BasicBlock *free1 = BasicBlock::Create(M.getContext(), "free1", F);
2384 IRBuilder<> Free1Builder(free1);
2385
2386 for (unsigned i = 0; i < width; i++) {
2387 addFunctionNoCapture(F, i + 1);
2388 Argument *shadow = F->arg_begin() + i + 1;
2389
2390 if (i < width - 1) {
2391 Argument *nextShadow = F->arg_begin() + i + 2;
2392 Value *isNotEqual = Free0Builder.CreateICmpNE(shadow, nextShadow);
2393 checkResult = checkResult
2394 ? Free0Builder.CreateAnd(isNotEqual, checkResult)
2395 : isNotEqual;
2396
2397 args[0] = nextShadow;
2398 CallInst *CI = Free1Builder.CreateCall(FreeTy, Free, args);
2399 CI->setAttributes(FreeAttributes);
2400 CI->setCallingConv(CallingConvention);
2401 }
2402 }
2403 Free0Builder.CreateCondBr(checkResult, free1, end);
2404 Free1Builder.CreateBr(end);
2405 } else {
2406 Free0Builder.CreateBr(end);
2407 }
2408
2409 EndBuilder.CreateRetVoid();
2410
2411 return F;
2412}
2413
2414/// Create function to computer nearest power of two
2415llvm::Value *nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V) {
2416 assert(V->getType()->isIntegerTy());
2417 IntegerType *T = cast<IntegerType>(V->getType());
2418 V = B.CreateAdd(V, ConstantInt::get(T, -1, /*IsSigned*/ true));
2419 for (size_t i = 1; i < T->getBitWidth(); i *= 2) {
2420 V = B.CreateOr(V, B.CreateLShr(V, ConstantInt::get(T, i)));
2421 }
2422 V = B.CreateAdd(V, ConstantInt::get(T, 1));
2423 return V;
2424}
2425
2426llvm::Function *getOrInsertDifferentialWaitallSave(llvm::Module &M,
2427 ArrayRef<llvm::Type *> T,
2428 PointerType *reqType) {
2429 std::string name = "__enzyme_differential_waitall_save";
2430 FunctionType *FT = FunctionType::get(getUnqual(reqType), T, false);
2431 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2432
2433 if (!F->empty())
2434 return F;
2435
2436 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2437 F->addFnAttr(Attribute::NoUnwind);
2438 F->addFnAttr(Attribute::AlwaysInline);
2439
2440 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
2441
2442 auto buff = F->arg_begin();
2443 buff->setName("count");
2444 Value *count = buff;
2445 Value *req = buff + 1;
2446 req->setName("req");
2447 Value *dreq = buff + 2;
2448 dreq->setName("dreq");
2449
2450 IRBuilder<> B(entry);
2451 count = B.CreateZExtOrTrunc(count, Type::getInt64Ty(entry->getContext()));
2452
2453 auto ret = CreateAllocation(B, reqType, count);
2454
2455 BasicBlock *loopBlock = BasicBlock::Create(M.getContext(), "loop", F);
2456 BasicBlock *endBlock = BasicBlock::Create(M.getContext(), "end", F);
2457
2458 B.CreateCondBr(B.CreateICmpEQ(count, ConstantInt::get(count->getType(), 0)),
2459 endBlock, loopBlock);
2460
2461 B.SetInsertPoint(loopBlock);
2462 auto idx = B.CreatePHI(count->getType(), 2);
2463 idx->addIncoming(ConstantInt::get(count->getType(), 0), entry);
2464 auto inc = B.CreateAdd(idx, ConstantInt::get(count->getType(), 1));
2465 idx->addIncoming(inc, loopBlock);
2466
2467 Type *reqT = reqType; // req->getType()->getPointerElementType();
2468 Value *idxs[] = {idx};
2469 Value *ireq = B.CreateInBoundsGEP(reqT, req, idxs);
2470 Value *idreq = B.CreateInBoundsGEP(reqT, dreq, idxs);
2471 Value *iout = B.CreateInBoundsGEP(reqType, ret, idxs);
2472 Value *isNull = nullptr;
2473 if (auto GV = M.getNamedValue("ompi_request_null")) {
2474 Value *reql = B.CreatePointerCast(ireq, getUnqual(GV->getType()));
2475 reql = B.CreateLoad(GV->getType(), reql);
2476 isNull = B.CreateICmpEQ(reql, GV);
2477 }
2478
2479 idreq = B.CreatePointerCast(idreq, getUnqual(reqType));
2480 Value *d_reqp = B.CreateLoad(reqType, idreq);
2481 if (isNull)
2482 d_reqp = B.CreateSelect(isNull, Constant::getNullValue(d_reqp->getType()),
2483 d_reqp);
2484
2485 B.CreateStore(d_reqp, iout);
2486
2487 B.CreateCondBr(B.CreateICmpEQ(inc, count), endBlock, loopBlock);
2488
2489 B.SetInsertPoint(endBlock);
2490 B.CreateRet(ret);
2491 return F;
2492}
2493
2494llvm::Function *getOrInsertDifferentialMPI_Wait(llvm::Module &M,
2495 ArrayRef<llvm::Type *> T,
2496 Type *reqType,
2497 StringRef caller) {
2498 llvm::SmallVector<llvm::Type *, 4> types(T.begin(), T.end());
2499 types.push_back(reqType);
2500
2501 auto &&[prefix, _, postfix] = tripleSplitDollar(caller);
2502
2503 std::string name = "__enzyme_differential_mpi_wait";
2504 if (prefix.size() != 0 || postfix.size() != 0) {
2505 name = (Twine(name) + "$" + prefix + "$" + postfix).str();
2506 }
2507 FunctionType *FT =
2508 FunctionType::get(Type::getVoidTy(M.getContext()), types, false);
2509 Function *F = cast<Function>(M.getOrInsertFunction(name, FT).getCallee());
2510
2511 if (!F->empty())
2512 return F;
2513
2514 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2515 F->addFnAttr(Attribute::NoUnwind);
2516 F->addFnAttr(Attribute::AlwaysInline);
2517
2518 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
2519 BasicBlock *isend = BasicBlock::Create(M.getContext(), "invertISend", F);
2520 BasicBlock *irecv = BasicBlock::Create(M.getContext(), "invertIRecv", F);
2521
2522#if 0
2523 /*0 */getInt8PtrTy(call.getContext())
2524 /*1 */i64
2525 /*2 */getInt8PtrTy(call.getContext())
2526 /*3 */i64
2527 /*4 */i64
2528 /*5 */getInt8PtrTy(call.getContext())
2529 /*6 */Type::getInt8Ty(call.getContext())
2530#endif
2531
2532 auto buff = F->arg_begin();
2533 buff->setName("buf");
2534 Value *buf = buff;
2535 Value *count = buff + 1;
2536 count->setName("count");
2537 Value *datatype = buff + 2;
2538 datatype->setName("datatype");
2539 Value *source = buff + 3;
2540 source->setName("source");
2541 Value *tag = buff + 4;
2542 tag->setName("tag");
2543 Value *comm = buff + 5;
2544 comm->setName("comm");
2545 Value *fn = buff + 6;
2546 fn->setName("fn");
2547 Value *d_req = buff + 7;
2548 d_req->setName("d_req");
2549
2550 auto isendfn = M.getFunction(getRenamedPerCallingConv(caller, "MPI_Isend"));
2551 assert(isendfn);
2552 // TODO: what if Isend not defined, but Irecv is?
2553 FunctionType *FuT = isendfn->getFunctionType();
2554
2555 auto irecvfn = cast<Function>(
2556 M.getOrInsertFunction(getRenamedPerCallingConv(caller, "MPI_Irecv"), FuT)
2557 .getCallee());
2558 assert(irecvfn);
2559
2560 IRBuilder<> B(entry);
2561 auto arg = isendfn->arg_begin();
2562 if (arg->getType()->isIntegerTy())
2563 buf = B.CreatePtrToInt(buf, arg->getType());
2564 arg++;
2565 count = B.CreateZExtOrTrunc(count, arg->getType());
2566 arg++;
2567 datatype = B.CreatePointerCast(datatype, arg->getType());
2568 arg++;
2569 source = B.CreateZExtOrTrunc(source, arg->getType());
2570 arg++;
2571 tag = B.CreateZExtOrTrunc(tag, arg->getType());
2572 arg++;
2573 comm = B.CreatePointerCast(comm, arg->getType());
2574 arg++;
2575 if (arg->getType()->isIntegerTy())
2576 d_req = B.CreatePtrToInt(d_req, arg->getType());
2577 Value *args[] = {
2578 buf, count, datatype, source, tag, comm, d_req,
2579 };
2580
2581 B.CreateCondBr(B.CreateICmpEQ(fn, ConstantInt::get(fn->getType(),
2582 (int)MPI_CallType::ISEND)),
2583 isend, irecv);
2584
2585 {
2586 B.SetInsertPoint(isend);
2587 auto fcall = B.CreateCall(irecvfn, args);
2588 fcall->setCallingConv(isendfn->getCallingConv());
2589 B.CreateRetVoid();
2590 }
2591
2592 {
2593 B.SetInsertPoint(irecv);
2594 auto fcall = B.CreateCall(isendfn, args);
2595 fcall->setCallingConv(isendfn->getCallingConv());
2596 B.CreateRetVoid();
2597 }
2598 return F;
2599}
2600
2601llvm::Value *getOrInsertOpFloatSum(llvm::Module &M, llvm::Function *templateFn,
2602 llvm::Type *OpPtr, llvm::Type *OpType,
2603 ConcreteType CT, llvm::Type *intType,
2604 IRBuilder<> &B2) {
2605 std::string name = "__enzyme_mpi_sum" + CT.str();
2606 assert(CT.isFloat());
2607 auto FlT = CT.isFloat();
2608
2609 if (auto Glob = M.getGlobalVariable(name)) {
2610 return B2.CreateLoad(Glob->getValueType(), Glob);
2611 }
2612
2613 llvm::Type *types[] = {getUnqual(FlT), getUnqual(FlT), getUnqual(intType),
2614 OpPtr};
2615 FunctionType *FuT =
2616 FunctionType::get(Type::getVoidTy(M.getContext()), types, false);
2617 Function *F =
2618 cast<Function>(M.getOrInsertFunction(name + "_run", FuT).getCallee());
2619
2620 F->setLinkage(Function::LinkageTypes::InternalLinkage);
2621#if LLVM_VERSION_MAJOR >= 16
2622 F->setOnlyAccessesArgMemory();
2623#else
2624 F->addFnAttr(Attribute::ArgMemOnly);
2625#endif
2626 F->addFnAttr(Attribute::NoUnwind);
2627 F->addFnAttr(Attribute::AlwaysInline);
2629 F->addParamAttr(0, Attribute::ReadOnly);
2632 F->addParamAttr(2, Attribute::ReadOnly);
2634 F->addParamAttr(3, Attribute::ReadNone);
2635
2636 BasicBlock *entry = BasicBlock::Create(M.getContext(), "entry", F);
2637 BasicBlock *body = BasicBlock::Create(M.getContext(), "for.body", F);
2638 BasicBlock *end = BasicBlock::Create(M.getContext(), "for.end", F);
2639
2640 auto src = F->arg_begin();
2641 src->setName("src");
2642 auto dst = src + 1;
2643 dst->setName("dst");
2644 auto lenp = dst + 1;
2645 lenp->setName("lenp");
2646 Value *len;
2647 // TODO consider using datatype arg and asserting same size as assumed
2648 // by type analysis
2649
2650 {
2651 IRBuilder<> B(entry);
2652 len = B.CreateLoad(intType, lenp);
2653 B.CreateCondBr(B.CreateICmpEQ(len, ConstantInt::get(len->getType(), 0)),
2654 end, body);
2655 }
2656
2657 {
2658 IRBuilder<> B(body);
2659 B.setFastMathFlags(getFast());
2660 PHINode *idx = B.CreatePHI(len->getType(), 2, "idx");
2661 idx->addIncoming(ConstantInt::get(len->getType(), 0), entry);
2662
2663 Value *dsti = B.CreateInBoundsGEP(FlT, dst, idx, "dst.i");
2664 LoadInst *dstl = B.CreateLoad(FlT, dsti, "dst.i.l");
2665
2666 Value *srci = B.CreateInBoundsGEP(FlT, src, idx, "src.i");
2667 LoadInst *srcl = B.CreateLoad(FlT, srci, "src.i.l");
2668 B.CreateStore(B.CreateFAdd(srcl, dstl), dsti);
2669
2670 Value *next =
2671 B.CreateNUWAdd(idx, ConstantInt::get(len->getType(), 1), "idx.next");
2672 idx->addIncoming(next, body);
2673 B.CreateCondBr(B.CreateICmpEQ(len, next), end, body);
2674 }
2675
2676 {
2677 IRBuilder<> B(end);
2678 B.CreateRetVoid();
2679 }
2680
2681 llvm::Type *rtypes[] = {getInt8PtrTy(M.getContext()), intType, OpPtr};
2682 FunctionType *RFT = FunctionType::get(intType, rtypes, false);
2683
2684 std::string opCreate =
2685 getRenamedPerCallingConv(templateFn->getName(), "MPI_Op_create");
2686 Constant *RF = M.getNamedValue(opCreate);
2687 if (!RF) {
2688 RF = cast<Function>(
2689 getOrInsertPerCallingConv(M, templateFn, "MPI_Op_create", RFT)
2690 .getCallee());
2691 } else {
2692 RF = ConstantExpr::getBitCast(RF, getUnqual(RFT));
2693 }
2694
2695 GlobalVariable *GV =
2696 new GlobalVariable(M, OpType, false, GlobalVariable::InternalLinkage,
2697 UndefValue::get(OpType), name);
2698
2699 Type *i1Ty = Type::getInt1Ty(M.getContext());
2700 GlobalVariable *initD = new GlobalVariable(
2701 M, i1Ty, false, GlobalVariable::InternalLinkage,
2702 ConstantInt::getFalse(M.getContext()), name + "_initd");
2703
2704 // Finish initializing mpi sum
2705 // https://www.mpich.org/static/docs/v3.2/www3/MPI_Op_create.html
2706 FunctionType *IFT = FunctionType::get(Type::getVoidTy(M.getContext()),
2707 ArrayRef<Type *>(), false);
2708 Function *initializerFunction = cast<Function>(
2709 M.getOrInsertFunction(name + "initializer", IFT).getCallee());
2710
2711 initializerFunction->setLinkage(Function::LinkageTypes::InternalLinkage);
2712 initializerFunction->addFnAttr(Attribute::NoUnwind);
2713
2714 {
2715 BasicBlock *entry =
2716 BasicBlock::Create(M.getContext(), "entry", initializerFunction);
2717 BasicBlock *run =
2718 BasicBlock::Create(M.getContext(), "run", initializerFunction);
2719 BasicBlock *end =
2720 BasicBlock::Create(M.getContext(), "end", initializerFunction);
2721 IRBuilder<> B(entry);
2722
2723 B.CreateCondBr(B.CreateLoad(initD->getValueType(), initD), end, run);
2724
2725 B.SetInsertPoint(run);
2726 Value *args[] = {ConstantExpr::getPointerCast(F, rtypes[0]),
2727 ConstantInt::get(rtypes[1], 1, false),
2728 ConstantExpr::getPointerCast(GV, rtypes[2])};
2729 B.CreateCall(RFT, RF, args);
2730 B.CreateStore(ConstantInt::getTrue(M.getContext()), initD);
2731 B.CreateBr(end);
2732 B.SetInsertPoint(end);
2733 B.CreateRetVoid();
2734 }
2735
2736 B2.CreateCall(M.getFunction(name + "initializer"));
2737 return B2.CreateLoad(GV->getValueType(), GV);
2738}
2739
2740void mayExecuteAfter(llvm::SmallVectorImpl<llvm::Instruction *> &results,
2741 llvm::Instruction *inst,
2742 const llvm::SetVector<Instruction *> &stores,
2743 const llvm::Loop *region) {
2744 using namespace llvm;
2745 std::map<BasicBlock *, SmallVector<Instruction *, 1>> maybeBlocks;
2746 BasicBlock *instBlk = inst->getParent();
2747 for (auto store : stores) {
2748 BasicBlock *storeBlk = store->getParent();
2749 if (instBlk == storeBlk) {
2750 // if store doesn't come before, exit.
2751
2752 if (store != inst) {
2753 BasicBlock::const_iterator It = storeBlk->begin();
2754 for (; &*It != store && &*It != inst; ++It)
2755 /*empty*/;
2756 // if inst comes first (e.g. before store) in the
2757 // block, return true
2758 if (&*It == inst) {
2759 results.push_back(store);
2760 }
2761 }
2762 maybeBlocks[storeBlk].push_back(store);
2763 } else {
2764 maybeBlocks[storeBlk].push_back(store);
2765 }
2766 }
2767
2768 if (maybeBlocks.size() == 0)
2769 return;
2770
2771 llvm::SmallVector<BasicBlock *, 2> todo;
2772 for (auto B : successors(instBlk)) {
2773 if (region && region->getHeader() == B) {
2774 continue;
2775 }
2776 todo.push_back(B);
2777 }
2778
2779 SmallPtrSet<BasicBlock *, 2> seen;
2780 while (todo.size()) {
2781 auto cur = todo.back();
2782 todo.pop_back();
2783 if (seen.count(cur))
2784 continue;
2785 seen.insert(cur);
2786 auto found = maybeBlocks.find(cur);
2787 if (found != maybeBlocks.end()) {
2788 for (auto store : found->second)
2789 results.push_back(store);
2790 maybeBlocks.erase(found);
2791 }
2792 for (auto B : successors(cur)) {
2793 if (region && region->getHeader() == B) {
2794 continue;
2795 }
2796 todo.push_back(B);
2797 }
2798 }
2799}
2800
2801const SCEV *evaluateAtIterationWithoutExt(const SCEVAddRecExpr *AR,
2802 const SCEV *It, ScalarEvolution &SE) {
2803 Type *Ty = AR->getType();
2804 unsigned N = AR->getNumOperands();
2805 if (!Ty->isIntegerTy() || !It->getType()->isIntegerTy() || N > 4)
2806 return AR->evaluateAtIteration(It, SE);
2807
2808 // The binomial coefficients are formed in the wider of the two types, so
2809 // that no bit of the iteration number is dropped before the halving (the
2810 // value of I*(I-1)/2 modulo 2^W depends on bit W of I).
2811 Type *CalcTy = SE.getTypeSizeInBits(It->getType()) > SE.getTypeSizeInBits(Ty)
2812 ? It->getType()
2813 : Ty;
2814 const SCEV *I = SE.getTruncateOrZeroExtend(It, CalcTy);
2815 const SCEV *Res = AR->getOperand(0);
2816 if (N > 1)
2817 Res = SE.getAddExpr(
2818 Res, SE.getMulExpr(AR->getOperand(1), SE.getTruncateOrNoop(I, Ty)));
2819 if (N > 2) {
2820 // I*(I-1)/2 as (I/2)*(I-1) + (I%2)*((I-1)/2): whichever of I and I-1 is
2821 // even is halved exactly, the other term is then zero.
2822 const SCEV *One = SE.getOne(CalcTy);
2823 const SCEV *Two = SE.getConstant(CalcTy, 2);
2824 const SCEV *Half = SE.getUDivExpr(I, Two);
2825 const SCEV *IM1 = SE.getMinusSCEV(I, One);
2826 const SCEV *Parity = SE.getMinusSCEV(I, SE.getMulExpr(Two, Half));
2827 const SCEV *Tri =
2828 SE.getAddExpr(SE.getMulExpr(Half, IM1),
2829 SE.getMulExpr(Parity, SE.getUDivExpr(IM1, Two)));
2830 Res = SE.getAddExpr(
2831 Res, SE.getMulExpr(AR->getOperand(2), SE.getTruncateOrNoop(Tri, Ty)));
2832 if (N > 3) {
2833 // I*(I-1)*(I-2)/6 = (I*(I-1)/2) * (I-2) / 3, and exact division by the
2834 // odd number 3 is multiplication by its inverse modulo 2^W (found by
2835 // Newton iteration, which doubles the number of correct bits each step).
2836 unsigned Bits = SE.getTypeSizeInBits(CalcTy);
2837 APInt Three(Bits, 3), Inv(Bits, 3);
2838 for (unsigned Correct = 3; Correct < Bits; Correct *= 2)
2839 Inv *= APInt(Bits, 2) - Three * Inv;
2840 const SCEV *Tet = SE.getMulExpr(
2841 SE.getMulExpr(Tri, SE.getMinusSCEV(I, Two)), SE.getConstant(Inv));
2842 Res = SE.getAddExpr(
2843 Res, SE.getMulExpr(AR->getOperand(3), SE.getTruncateOrNoop(Tet, Ty)));
2844 }
2845 }
2846 return Res;
2847}
2848
2850 llvm::ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT,
2851 llvm::Instruction *maybeReader, const llvm::SCEV *LoadStart,
2852 const llvm::SCEV *LoadEnd, llvm::Instruction *maybeWriter,
2853 const llvm::SCEV *StoreStart, const llvm::SCEV *StoreEnd,
2854 llvm::Loop *scope) {
2855 // The store may either occur directly after the load in the current loop
2856 // nest, or prior to the load in a subsequent iteration of the loop nest
2857 // Generally:
2858 // L0 -> scope -> L1 -> L2 -> L3 -> load_L4 -> load_L5 ... Load
2859 // \-> store_L4 -> store_L5 ... Store
2860 // We begin by finding the common ancestor of the two loops, which may
2861 // be none.
2862 Loop *anc = getAncestor(LI.getLoopFor(maybeReader->getParent()),
2863 LI.getLoopFor(maybeWriter->getParent()));
2864
2865 // The surrounding scope must contain the ancestor
2866 if (scope) {
2867 assert(anc);
2868 assert(scope == anc || scope->contains(anc));
2869 }
2870
2871 // Consider the case where the load and store don't share any common loops.
2872 // That is to say, there's no loops in [scope, ancestor) we need to consider
2873 // having a store in a later iteration overwrite the load of a previous
2874 // iteration.
2875 //
2876 // An example of this overwriting would be a "left shift"
2877 // for (int j = 1; j<N; j++) {
2878 // load A[j]
2879 // store A[j-1]
2880 // }
2881 //
2882 // Ignoring such ancestors, if we compare the two regions to have no direct
2883 // overlap we can return that it doesn't overwrite memory if the regions
2884 // don't overlap at any level of region expansion. That is to say, we can
2885 // expand the start or end, for any loop to be the worst case scenario
2886 // given the loop bounds.
2887 //
2888 // However, now let us consider the case where there are surrounding loops.
2889 // If the storing boundary is represented by an induction variable of one
2890 // of these common loops, we must conseratively expand it all the way to the
2891 // end. We will also mark the loops we may expand. If we encounter all
2892 // intervening loops in this fashion, and it is proven safe in these cases,
2893 // the region does not overlap. However, if we don't encounter all surrounding
2894 // loops in our induction expansion, we may simply be repeating the write
2895 // which we should also ensure we say the region may overlap (due to the
2896 // repetition).
2897 //
2898 // Since we also have a Loop scope, we can ignore any common loops at the
2899 // scope level or above
2900
2901 /// We force all ranges for all loops in range ... [scope, anc], .... cur
2902 /// to expand the number of iterations
2903
2904 SmallPtrSet<const Loop *, 1> visitedAncestors;
2905 auto skipLoop = [&](const Loop *L) {
2906 assert(L);
2907 if (scope && L->contains(scope))
2908 return false;
2909
2910 if (anc && (anc == L || anc->contains(L))) {
2911 visitedAncestors.insert(L);
2912 return true;
2913 }
2914 return false;
2915 };
2916
2917 // Check the boounds of an [... endprev][startnext ...] for potential
2918 // overlaps. The boolean EndIsStore is true of the EndPev represents
2919 // the store and should have its loops expanded, or if that should
2920 // apply to StartNed.
2921 auto hasOverlap = [&](const SCEV *EndPrev, const SCEV *StartNext,
2922 bool EndIsStore) {
2923 for (auto slim = StartNext; slim != SE.getCouldNotCompute();) {
2924 bool sskip = false;
2925 if (!EndIsStore)
2926 if (auto startL = dyn_cast<SCEVAddRecExpr>(slim))
2927 if (skipLoop(startL->getLoop()) &&
2928 SE.isKnownNonPositive(startL->getStepRecurrence(SE))) {
2929 sskip = true;
2930 }
2931
2932 if (!sskip)
2933 for (auto elim = EndPrev; elim != SE.getCouldNotCompute();) {
2934 {
2935
2936 bool eskip = false;
2937 if (EndIsStore)
2938 if (auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2939 if (skipLoop(endL->getLoop()) &&
2940 SE.isKnownNonNegative(endL->getStepRecurrence(SE))) {
2941 eskip = true;
2942 }
2943 }
2944
2945 // Moreover because otherwise SE cannot "groupScevByComplexity"
2946 // we need to ensure that if both slim/elim are AddRecv
2947 // they must be in the same loop, or one loop must dominate
2948 // the other.
2949 if (!eskip) {
2950
2951 if (auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2952 auto EH = endL->getLoop()->getHeader();
2953 if (auto startL = dyn_cast<SCEVAddRecExpr>(slim)) {
2954 auto SH = startL->getLoop()->getHeader();
2955 if (EH != SH && !DT.dominates(EH, SH) &&
2956 !DT.dominates(SH, EH))
2957 eskip = true;
2958 }
2959 }
2960 }
2961 if (!eskip) {
2962 auto sub = SE.getMinusSCEV(slim, elim);
2963 if (sub != SE.getCouldNotCompute() && SE.isKnownNonNegative(sub))
2964 return false;
2965 }
2966 }
2967
2968 if (auto endL = dyn_cast<SCEVAddRecExpr>(elim)) {
2969 if (SE.isKnownNonPositive(endL->getStepRecurrence(SE))) {
2970 elim = endL->getStart();
2971 continue;
2972 } else if (SE.isKnownNonNegative(endL->getStepRecurrence(SE))) {
2973#if LLVM_VERSION_MAJOR >= 12
2974 auto ebd = SE.getSymbolicMaxBackedgeTakenCount(endL->getLoop());
2975#else
2976 auto ebd = SE.getBackedgeTakenCount(endL->getLoop());
2977#endif
2978 if (ebd == SE.getCouldNotCompute())
2979 break;
2980 elim = evaluateAtIterationWithoutExt(endL, ebd, SE);
2981 continue;
2982 }
2983 }
2984 break;
2985 }
2986
2987 if (auto startL = dyn_cast<SCEVAddRecExpr>(slim)) {
2988 if (SE.isKnownNonNegative(startL->getStepRecurrence(SE))) {
2989 slim = startL->getStart();
2990 continue;
2991 } else if (SE.isKnownNonPositive(startL->getStepRecurrence(SE))) {
2992#if LLVM_VERSION_MAJOR >= 12
2993 auto sbd = SE.getSymbolicMaxBackedgeTakenCount(startL->getLoop());
2994#else
2995 auto sbd = SE.getBackedgeTakenCount(startL->getLoop());
2996#endif
2997 if (sbd == SE.getCouldNotCompute())
2998 break;
2999 slim = evaluateAtIterationWithoutExt(startL, sbd, SE);
3000 continue;
3001 }
3002 }
3003 break;
3004 }
3005 return true;
3006 };
3007
3008 // There is no overwrite if either the stores all occur before the loads
3009 // [S, S+Size][start load, L+Size]
3010 visitedAncestors.clear();
3011 if (!hasOverlap(StoreEnd, LoadStart, /*EndIsStore*/ true)) {
3012 // We must have seen all common loops as induction variables
3013 // to be legal, lest we have a repetition of the store.
3014 bool legal = true;
3015 for (const Loop *L = anc; L != scope; L = L->getParentLoop()) {
3016 if (!visitedAncestors.count(L))
3017 legal = false;
3018 }
3019 if (legal)
3020 return false;
3021 }
3022
3023 // There is no overwrite if either the loads all occur before the stores
3024 // [start load, L+Size] [S, S+Size]
3025 visitedAncestors.clear();
3026 if (!hasOverlap(LoadEnd, StoreStart, /*EndIsStore*/ false)) {
3027 // We must have seen all common loops as induction variables
3028 // to be legal, lest we have a repetition of the store.
3029 bool legal = true;
3030 for (const Loop *L = anc; L != scope; L = L->getParentLoop()) {
3031 if (!visitedAncestors.count(L))
3032 legal = false;
3033 }
3034 if (legal)
3035 return false;
3036 }
3037 return true;
3038}
3039
3040bool overwritesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA,
3041 llvm::TargetLibraryInfo &TLI, ScalarEvolution &SE,
3042 llvm::LoopInfo &LI, llvm::DominatorTree &DT,
3043 llvm::Instruction *maybeReader,
3044 llvm::Instruction *maybeWriter,
3045 llvm::Loop *scope) {
3046 using namespace llvm;
3047 if (!writesToMemoryReadBy(TR, AA, TLI, maybeReader, maybeWriter))
3048 return false;
3049 const SCEV *LoadBegin = SE.getCouldNotCompute();
3050 const SCEV *LoadEnd = SE.getCouldNotCompute();
3051
3052 const SCEV *StoreBegin = SE.getCouldNotCompute();
3053 const SCEV *StoreEnd = SE.getCouldNotCompute();
3054
3055 Value *loadPtr = nullptr;
3056 Value *storePtr = nullptr;
3057 if (auto LI = dyn_cast<LoadInst>(maybeReader)) {
3058 loadPtr = LI->getPointerOperand();
3059 LoadBegin = SE.getSCEV(LI->getPointerOperand());
3060 if (LoadBegin != SE.getCouldNotCompute() &&
3061 !LoadBegin->getType()->isIntegerTy()) {
3062 auto &DL = maybeWriter->getModule()->getDataLayout();
3063 auto width = cast<IntegerType>(DL.getIndexType(LoadBegin->getType()))
3064 ->getBitWidth();
3065#if LLVM_VERSION_MAJOR >= 18
3066 auto TS = SE.getConstant(
3067 APInt(width, (int64_t)DL.getTypeStoreSize(LI->getType())));
3068#else
3069 auto TS = SE.getConstant(
3070 APInt(width, DL.getTypeStoreSize(LI->getType()).getFixedSize()));
3071#endif
3072 LoadEnd = SE.getAddExpr(LoadBegin, TS);
3073 }
3074 }
3075 if (auto SI = dyn_cast<StoreInst>(maybeWriter)) {
3076 storePtr = SI->getPointerOperand();
3077 StoreBegin = SE.getSCEV(SI->getPointerOperand());
3078 if (StoreBegin != SE.getCouldNotCompute() &&
3079 !StoreBegin->getType()->isIntegerTy()) {
3080 auto &DL = maybeWriter->getModule()->getDataLayout();
3081 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3082 ->getBitWidth();
3083#if LLVM_VERSION_MAJOR >= 18
3084 auto TS =
3085 SE.getConstant(APInt(width, (int64_t)DL.getTypeStoreSize(
3086 SI->getValueOperand()->getType())));
3087#else
3088 auto TS = SE.getConstant(
3089 APInt(width, DL.getTypeStoreSize(SI->getValueOperand()->getType())
3090 .getFixedSize()));
3091#endif
3092 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3093 }
3094 }
3095 if (auto MS = dyn_cast<MemSetInst>(maybeWriter)) {
3096 storePtr = MS->getArgOperand(0);
3097 StoreBegin = SE.getSCEV(MS->getArgOperand(0));
3098 if (StoreBegin != SE.getCouldNotCompute() &&
3099 !StoreBegin->getType()->isIntegerTy()) {
3100 if (auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3101 auto &DL = MS->getModule()->getDataLayout();
3102 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3103 ->getBitWidth();
3104 auto TS =
3105 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3106 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3107 }
3108 }
3109 }
3110 if (auto MS = dyn_cast<MemTransferInst>(maybeWriter)) {
3111 storePtr = MS->getArgOperand(0);
3112 StoreBegin = SE.getSCEV(MS->getArgOperand(0));
3113 if (StoreBegin != SE.getCouldNotCompute() &&
3114 !StoreBegin->getType()->isIntegerTy()) {
3115 if (auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3116 auto &DL = MS->getModule()->getDataLayout();
3117 auto width = cast<IntegerType>(DL.getIndexType(StoreBegin->getType()))
3118 ->getBitWidth();
3119 auto TS =
3120 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3121 StoreEnd = SE.getAddExpr(StoreBegin, TS);
3122 }
3123 }
3124 }
3125 if (auto MS = dyn_cast<MemTransferInst>(maybeReader)) {
3126 loadPtr = MS->getArgOperand(1);
3127 LoadBegin = SE.getSCEV(MS->getArgOperand(1));
3128 if (LoadBegin != SE.getCouldNotCompute() &&
3129 !LoadBegin->getType()->isIntegerTy()) {
3130 if (auto Len = dyn_cast<ConstantInt>(MS->getArgOperand(2))) {
3131 auto &DL = MS->getModule()->getDataLayout();
3132 auto width = cast<IntegerType>(DL.getIndexType(LoadBegin->getType()))
3133 ->getBitWidth();
3134 auto TS =
3135 SE.getConstant(APInt(width, Len->getValue().getLimitedValue()));
3136 LoadEnd = SE.getAddExpr(LoadBegin, TS);
3137 }
3138 }
3139 }
3140
3141 if (loadPtr && storePtr)
3142 if (auto alias = arePointersGuaranteedNoAlias(TLI, AA, DT, LI, loadPtr,
3143 storePtr, true))
3144 if (*alias)
3145 return false;
3146
3147 if (!overwritesToMemoryReadByLoop(SE, LI, DT, maybeReader, LoadBegin, LoadEnd,
3148 maybeWriter, StoreBegin, StoreEnd, scope))
3149 return false;
3150
3151 return true;
3152}
3153
3154/// Return whether maybeReader can read from memory written to by maybeWriter
3155bool writesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA,
3156 llvm::TargetLibraryInfo &TLI,
3157 llvm::Instruction *maybeReader,
3158 llvm::Instruction *maybeWriter) {
3159 assert(maybeReader->getParent()->getParent() ==
3160 maybeWriter->getParent()->getParent());
3161 using namespace llvm;
3162 if (isa<StoreInst>(maybeReader))
3163 return false;
3164 if (isa<FenceInst>(maybeReader)) {
3165 return false;
3166 }
3167 if (auto call = dyn_cast<CallInst>(maybeWriter)) {
3168 StringRef funcName = getFuncNameFromCall(call);
3169
3170 if (isDebugFunction(call->getCalledFunction()))
3171 return false;
3172
3173 if (isCertainPrint(funcName) || isAllocationFunction(funcName, TLI) ||
3174 isDeallocationFunction(funcName, TLI)) {
3175 return false;
3176 }
3177
3178 if (isReadOnlyOrThrow(call))
3179 return false;
3180
3181 if (isMemFreeLibMFunction(funcName)) {
3182 return false;
3183 }
3184 if (funcName == "jl_array_copy" || funcName == "ijl_array_copy")
3185 return false;
3186
3187 if (funcName == "jl_genericmemory_copy_slice" ||
3188 funcName == "ijl_genericmemory_copy_slice")
3189 return false;
3190
3191 if (funcName == "jl_new_array" || funcName == "ijl_new_array")
3192 return false;
3193
3194 if (funcName == "julia.safepoint")
3195 return false;
3196
3197 if (funcName == "jl_idtable_rehash" || funcName == "ijl_idtable_rehash")
3198 return false;
3199
3200 // Isend only writes to inaccessible mem only
3201 if (funcName == "MPI_Send" || funcName == "PMPI_Send") {
3202 return false;
3203 }
3204 // Wait only overwrites memory in the status and request.
3205 if (funcName == "MPI_Wait" || funcName == "PMPI_Wait" ||
3206 funcName == "MPI_Waitall" || funcName == "PMPI_Waitall") {
3207#if LLVM_VERSION_MAJOR > 11
3208 auto loc = LocationSize::afterPointer();
3209#else
3210 auto loc = MemoryLocation::UnknownSize;
3211#endif
3212 size_t off = (funcName == "MPI_Wait" || funcName == "PMPI_Wait") ? 0 : 1;
3213 // No alias with status
3214 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(off + 1),
3215 loc))) {
3216 // No alias with request
3217 if (!isRefSet(AA.getModRefInfo(maybeReader,
3218 call->getArgOperand(off + 0), loc)))
3219 return false;
3220 auto R = parseTBAA(
3221 *maybeReader,
3222 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3223 nullptr)[{-1}];
3224 // Could still conflict with the mpi_request unless a non pointer
3225 // type.
3226 if (R != BaseType::Unknown && R != BaseType::Anything &&
3227 R != BaseType::Pointer)
3228 return false;
3229 }
3230 }
3231 // Isend only writes to inaccessible mem and request.
3232 if (funcName == "MPI_Isend" || funcName == "PMPI_Isend") {
3233 auto R = parseTBAA(
3234 *maybeReader,
3235 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3236 nullptr)[{-1}];
3237 // Could still conflict with the mpi_request, unless either
3238 // synchronous, or a non pointer type.
3239 if (R != BaseType::Unknown && R != BaseType::Anything &&
3240 R != BaseType::Pointer)
3241 return false;
3242#if LLVM_VERSION_MAJOR > 11
3243 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3244 LocationSize::afterPointer())))
3245 return false;
3246#else
3247 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3248 MemoryLocation::UnknownSize)))
3249 return false;
3250#endif
3251 return false;
3252 }
3253 if (funcName == "MPI_Irecv" || funcName == "PMPI_Irecv" ||
3254 funcName == "MPI_Recv" || funcName == "PMPI_Recv") {
3256 if (Constant *C = dyn_cast<Constant>(call->getArgOperand(2))) {
3257 while (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
3258 C = CE->getOperand(0);
3259 }
3260 if (auto GV = dyn_cast<GlobalVariable>(C)) {
3261 if (GV->getName() == "ompi_mpi_double") {
3262 type = ConcreteType(Type::getDoubleTy(C->getContext()));
3263 } else if (GV->getName() == "ompi_mpi_float") {
3264 type = ConcreteType(Type::getFloatTy(C->getContext()));
3265 }
3266 }
3267 }
3268 if (type.isKnown()) {
3269 auto R = parseTBAA(
3270 *maybeReader,
3271 maybeReader->getParent()->getParent()->getParent()->getDataLayout(),
3272 nullptr)[{-1}];
3273 if (R.isKnown() && type != R) {
3274 // Could still conflict with the mpi_request, unless either
3275 // synchronous, or a non pointer type.
3276 if (funcName == "MPI_Recv" || funcName == "PMPI_Recv" ||
3278 return false;
3279#if LLVM_VERSION_MAJOR > 11
3280 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3281 LocationSize::afterPointer())))
3282 return false;
3283#else
3284 if (!isRefSet(AA.getModRefInfo(maybeReader, call->getArgOperand(6),
3285 MemoryLocation::UnknownSize)))
3286 return false;
3287#endif
3288 }
3289 }
3290 }
3291 if (auto II = dyn_cast<IntrinsicInst>(call)) {
3292 if (II->getIntrinsicID() == Intrinsic::stacksave)
3293 return false;
3294 if (II->getIntrinsicID() == Intrinsic::stackrestore)
3295 return false;
3296 if (II->getIntrinsicID() == Intrinsic::trap)
3297 return false;
3298#if LLVM_VERSION_MAJOR >= 13
3299 if (II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
3300 return false;
3301#endif
3302 }
3303
3304 if (auto iasm = dyn_cast<InlineAsm>(call->getCalledOperand())) {
3305 if (StringRef(iasm->getAsmString()).contains("exit"))
3306 return false;
3307 }
3308 }
3309 if (auto call = dyn_cast<CallInst>(maybeReader)) {
3310 StringRef funcName = getFuncNameFromCall(call);
3311
3312 if (isDebugFunction(call->getCalledFunction()))
3313 return false;
3314
3315 if (isAllocationFunction(funcName, TLI) ||
3316 isDeallocationFunction(funcName, TLI)) {
3317 return false;
3318 }
3319
3320 if (isMemFreeLibMFunction(funcName)) {
3321 return false;
3322 }
3323
3324 if (auto II = dyn_cast<IntrinsicInst>(call)) {
3325 if (II->getIntrinsicID() == Intrinsic::stacksave)
3326 return false;
3327 if (II->getIntrinsicID() == Intrinsic::stackrestore)
3328 return false;
3329 if (II->getIntrinsicID() == Intrinsic::trap)
3330 return false;
3331#if LLVM_VERSION_MAJOR >= 13
3332 if (II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
3333 return false;
3334#endif
3335 }
3336 }
3337 if (auto call = dyn_cast<InvokeInst>(maybeWriter)) {
3338 StringRef funcName = getFuncNameFromCall(call);
3339
3340 if (isDebugFunction(call->getCalledFunction()))
3341 return false;
3342
3343 if (isAllocationFunction(funcName, TLI) ||
3344 isDeallocationFunction(funcName, TLI)) {
3345 return false;
3346 }
3347
3348 if (isMemFreeLibMFunction(funcName)) {
3349 return false;
3350 }
3351 if (funcName == "jl_array_copy" || funcName == "ijl_array_copy")
3352 return false;
3353
3354 if (funcName == "jl_genericmemory_copy_slice" ||
3355 funcName == "ijl_genericmemory_copy_slice")
3356 return false;
3357
3358 if (funcName == "jl_idtable_rehash" || funcName == "ijl_idtable_rehash")
3359 return false;
3360
3361 if (auto iasm = dyn_cast<InlineAsm>(call->getCalledOperand())) {
3362 if (StringRef(iasm->getAsmString()).contains("exit"))
3363 return false;
3364 }
3365 }
3366 if (auto call = dyn_cast<InvokeInst>(maybeReader)) {
3367 StringRef funcName = getFuncNameFromCall(call);
3368
3369 if (isDebugFunction(call->getCalledFunction()))
3370 return false;
3371
3372 if (isAllocationFunction(funcName, TLI) ||
3373 isDeallocationFunction(funcName, TLI)) {
3374 return false;
3375 }
3376
3377 if (isMemFreeLibMFunction(funcName)) {
3378 return false;
3379 }
3380 }
3381 assert(maybeWriter->mayWriteToMemory());
3382 assert(maybeReader->mayReadFromMemory());
3383
3384 if (auto li = dyn_cast<LoadInst>(maybeReader)) {
3385 if (TR) {
3386 auto TT = TR->query(li)[{-1}];
3387 if (TT != BaseType::Unknown && TT != BaseType::Anything) {
3388 if (auto si = dyn_cast<StoreInst>(maybeWriter)) {
3389 auto TT2 = TR->query(si->getValueOperand())[{-1}];
3390 if (TT2 != BaseType::Unknown && TT2 != BaseType::Anything) {
3391 if (TT != TT2)
3392 return false;
3393 }
3394 auto &dl = li->getParent()->getParent()->getParent()->getDataLayout();
3395 auto len =
3396 (dl.getTypeSizeInBits(si->getValueOperand()->getType()) + 7) / 8;
3397 TT2 = TR->query(si->getPointerOperand()).Lookup(len, dl)[{-1}];
3398 if (TT2 != BaseType::Unknown && TT2 != BaseType::Anything) {
3399 if (TT != TT2)
3400 return false;
3401 }
3402 }
3403 }
3404 }
3405 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(li)));
3406 }
3407 if (auto rmw = dyn_cast<AtomicRMWInst>(maybeReader)) {
3408 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(rmw)));
3409 }
3410 if (auto xch = dyn_cast<AtomicCmpXchgInst>(maybeReader)) {
3411 return isModSet(AA.getModRefInfo(maybeWriter, MemoryLocation::get(xch)));
3412 }
3413 if (auto mti = dyn_cast<MemTransferInst>(maybeReader)) {
3414 return isModSet(
3415 AA.getModRefInfo(maybeWriter, MemoryLocation::getForSource(mti)));
3416 }
3417
3418 if (auto si = dyn_cast<StoreInst>(maybeWriter)) {
3419 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(si)));
3420 }
3421 if (auto rmw = dyn_cast<AtomicRMWInst>(maybeWriter)) {
3422 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(rmw)));
3423 }
3424 if (auto xch = dyn_cast<AtomicCmpXchgInst>(maybeWriter)) {
3425 return isRefSet(AA.getModRefInfo(maybeReader, MemoryLocation::get(xch)));
3426 }
3427 if (auto mti = dyn_cast<MemIntrinsic>(maybeWriter)) {
3428 return isRefSet(
3429 AA.getModRefInfo(maybeReader, MemoryLocation::getForDest(mti)));
3430 }
3431
3432 if (auto cb = dyn_cast<CallInst>(maybeReader)) {
3433 return isModOrRefSet(AA.getModRefInfo(maybeWriter, cb));
3434 }
3435 if (auto cb = dyn_cast<InvokeInst>(maybeReader)) {
3436 return isModOrRefSet(AA.getModRefInfo(maybeWriter, cb));
3437 }
3438 llvm::errs() << " maybeReader: " << *maybeReader
3439 << " maybeWriter: " << *maybeWriter << "\n";
3440 llvm_unreachable("unknown inst2");
3441}
3442
3443// Find the base pointer of ptr and the offset in bytes from the start of
3444// the returned base pointer to this value.
3445AllocaInst *getBaseAndOffset(Value *ptr, size_t &offset) {
3446 offset = 0;
3447 while (true) {
3448 if (auto CI = dyn_cast<CastInst>(ptr)) {
3449 ptr = CI->getOperand(0);
3450 continue;
3451 }
3452 if (auto CI = dyn_cast<GetElementPtrInst>(ptr)) {
3453 auto &DL = CI->getParent()->getParent()->getParent()->getDataLayout();
3454#if LLVM_VERSION_MAJOR >= 20
3455 SmallMapVector<Value *, APInt, 4> VariableOffsets;
3456#else
3457 MapVector<Value *, APInt> VariableOffsets;
3458#endif
3459 auto width = sizeof(size_t) * 8;
3460 APInt Offset(width, 0);
3461 bool success = collectOffset(cast<GEPOperator>(CI), DL, width,
3462 VariableOffsets, Offset);
3463 if (!success || VariableOffsets.size() != 0 || Offset.isNegative()) {
3464 return nullptr;
3465 }
3466 offset += Offset.getZExtValue();
3467 ptr = CI->getOperand(0);
3468 continue;
3469 }
3470 if (isa<AllocaInst>(ptr)) {
3471 break;
3472 }
3473 if (auto LI = dyn_cast<LoadInst>(ptr)) {
3474 if (auto S = simplifyLoad(LI)) {
3475 ptr = S;
3476 continue;
3477 }
3478 }
3479 return nullptr;
3480 }
3481 return cast<AllocaInst>(ptr);
3482}
3483
3484// Find all user instructions of AI, returning tuples of <instruction, value,
3485// byte offet from AI> Unlike a simple get users, this will recurse through any
3486// constant gep offsets and casts
3487SmallVector<std::tuple<Instruction *, Value *, size_t>, 1>
3488findAllUsersOf(Value *AI) {
3489 SmallVector<std::pair<Value *, size_t>, 1> todo;
3490 todo.emplace_back(AI, 0);
3491
3492 SmallVector<std::tuple<Instruction *, Value *, size_t>, 1> users;
3493 while (todo.size()) {
3494 auto pair = todo.pop_back_val();
3495 Value *ptr = pair.first;
3496 size_t suboff = pair.second;
3497
3498 for (auto U : ptr->users()) {
3499 if (auto CI = dyn_cast<CastInst>(U)) {
3500 todo.emplace_back(CI, suboff);
3501 continue;
3502 }
3503 if (auto CI = dyn_cast<GetElementPtrInst>(U)) {
3504 auto &DL = CI->getParent()->getParent()->getParent()->getDataLayout();
3505#if LLVM_VERSION_MAJOR >= 20
3506 SmallMapVector<Value *, APInt, 4> VariableOffsets;
3507#else
3508 MapVector<Value *, APInt> VariableOffsets;
3509#endif
3510 auto width = sizeof(size_t) * 8;
3511 APInt Offset(width, 0);
3512 bool success = collectOffset(cast<GEPOperator>(CI), DL, width,
3513 VariableOffsets, Offset);
3514
3515 if (!success || VariableOffsets.size() != 0 || Offset.isNegative()) {
3516 users.emplace_back(cast<Instruction>(U), ptr, suboff);
3517 continue;
3518 }
3519 todo.emplace_back(CI, suboff + Offset.getZExtValue());
3520 continue;
3521 }
3522 users.emplace_back(cast<Instruction>(U), ptr, suboff);
3523 continue;
3524 }
3525 }
3526 return users;
3527}
3528
3529// Given a pointer, find all values of size `valSz` which could be loaded from
3530// that pointer when indexed at offset. If it is impossible to guarantee that
3531// the set contains all such values, set legal to false
3532SmallVector<std::pair<Value *, size_t>, 1>
3533getAllLoadedValuesFrom(AllocaInst *ptr0, size_t offset, size_t valSz,
3534 bool &legal) {
3535 SmallVector<std::pair<Value *, size_t>, 1> options;
3536
3537 auto todo = findAllUsersOf(ptr0);
3538 std::set<std::tuple<Instruction *, Value *, size_t>> seen;
3539
3540 while (todo.size()) {
3541 auto pair = todo.pop_back_val();
3542 if (seen.count(pair))
3543 continue;
3544 seen.insert(pair);
3545 Instruction *U = std::get<0>(pair);
3546 Value *ptr = std::get<1>(pair);
3547 size_t suboff = std::get<2>(pair);
3548
3549 // Read only users do not set the memory inside of ptr
3550 if (isa<LoadInst>(U)) {
3551 continue;
3552 }
3553 if (auto MTI = dyn_cast<MemTransferInst>(U))
3554 if (MTI->getOperand(0) != ptr) {
3555 continue;
3556 }
3557 if (auto I = dyn_cast<Instruction>(U)) {
3558 if (!I->mayWriteToMemory() && I->getType()->isVoidTy())
3559 continue;
3560 }
3561
3562 if (auto SI = dyn_cast<StoreInst>(U)) {
3563 auto &DL = SI->getParent()->getParent()->getParent()->getDataLayout();
3564
3565 // We are storing into the ptr
3566 if (SI->getPointerOperand() == ptr) {
3567 auto storeSz =
3568 (DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType()) + 7) /
3569 8;
3570 // If store is before the load would start
3571 if (storeSz + suboff <= offset)
3572 continue;
3573 // if store starts after load would start
3574 if (offset + valSz <= suboff)
3575 continue;
3576
3577 if (valSz <= storeSz) {
3578 assert(offset >= suboff);
3579 options.emplace_back(SI->getValueOperand(), offset - suboff);
3580 continue;
3581 }
3582 }
3583
3584 // We capture our pointer of interest, if it is stored into an alloca,
3585 // all loads of said alloca would potentially store into.
3586 if (SI->getValueOperand() == ptr) {
3587 if (suboff == 0) {
3588 size_t mid_offset = 0;
3589 if (auto AI2 =
3590 getBaseAndOffset(SI->getPointerOperand(), mid_offset)) {
3591 bool sublegal = true;
3592 auto ptrSz = (DL.getTypeStoreSizeInBits(ptr->getType()) + 7) / 8;
3593 auto subPtrs =
3594 getAllLoadedValuesFrom(AI2, mid_offset, ptrSz, sublegal);
3595 if (!sublegal) {
3596 legal = false;
3597 return options;
3598 }
3599 for (auto &&[subPtr, subOff] : subPtrs) {
3600 if (subOff != 0)
3601 return options;
3602 for (const auto &pair3 : findAllUsersOf(subPtr)) {
3603 todo.emplace_back(std::move(pair3));
3604 }
3605 }
3606 continue;
3607 }
3608 }
3609 }
3610 }
3611
3612 if (auto II = dyn_cast<IntrinsicInst>(U)) {
3613 if (II->getCalledFunction()->getName() == "llvm.enzyme.lifetime_start" ||
3614 II->getCalledFunction()->getName() == "llvm.enzyme.lifetime_end")
3615 continue;
3616 if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
3617 II->getIntrinsicID() == Intrinsic::lifetime_end)
3618 continue;
3619 }
3620
3621 // If we copy into the ptr at a location that includes the offset, consider
3622 // all sub uses
3623 if (auto MTI = dyn_cast<MemTransferInst>(U)) {
3624 if (auto CI = dyn_cast<ConstantInt>(MTI->getLength())) {
3625 if (MTI->getOperand(0) == ptr) {
3626 auto storeSz = CI->getValue();
3627
3628 // If store is before the load would start
3629 if ((storeSz + suboff).ule(offset))
3630 continue;
3631
3632 // if store starts after load would start
3633 if (offset + valSz <= suboff)
3634 continue;
3635
3636 if (suboff == 0 && CI->getValue().uge(offset + valSz)) {
3637 size_t midoffset = 0;
3638 auto AI2 = getBaseAndOffset(MTI->getOperand(1), midoffset);
3639 if (!AI2) {
3640 legal = false;
3641 return options;
3642 }
3643 if (midoffset != 0) {
3644 legal = false;
3645 return options;
3646 }
3647 for (const auto &pair3 : findAllUsersOf(AI2)) {
3648 todo.emplace_back(std::move(pair3));
3649 }
3650 continue;
3651 }
3652 }
3653 }
3654 }
3655
3656 legal = false;
3657 return options;
3658 }
3659
3660 return options;
3661}
3662
3663// Perform mem2reg/sroa to identify the innermost value being represented.
3664Value *simplifyLoad(Value *V, size_t valSz, size_t preOffset) {
3665 if (auto LI = dyn_cast<LoadInst>(V)) {
3666 if (valSz == 0) {
3667 auto &DL = LI->getParent()->getParent()->getParent()->getDataLayout();
3668 valSz = (DL.getTypeSizeInBits(LI->getType()) + 7) / 8;
3669 }
3670
3671 Value *ptr = LI->getPointerOperand();
3672 size_t offset = 0;
3673
3674 if (auto ptr2 = simplifyLoad(ptr)) {
3675 ptr = ptr2;
3676 }
3677 auto AI = getBaseAndOffset(ptr, offset);
3678 if (!AI) {
3679 return nullptr;
3680 }
3681 offset += preOffset;
3682
3683 bool legal = true;
3684 auto opts = getAllLoadedValuesFrom(AI, offset, valSz, legal);
3685
3686 if (!legal) {
3687 return nullptr;
3688 }
3689 std::set<Value *> res;
3690 for (auto &&[opt, startOff] : opts) {
3691 Value *v2 = simplifyLoad(opt, valSz, startOff);
3692 if (v2)
3693 res.insert(v2);
3694 else
3695 res.insert(opt);
3696 }
3697 if (res.size() != 1) {
3698 return nullptr;
3699 }
3700 Value *retval = *res.begin();
3701 return retval;
3702 }
3703 if (auto EVI = dyn_cast<ExtractValueInst>(V)) {
3704 IRBuilder<> B(EVI);
3705 auto em =
3706 GradientUtils::extractMeta(B, EVI->getAggregateOperand(),
3707 EVI->getIndices(), "", /*fallback*/ false);
3708 if (em != nullptr) {
3709 if (auto SL2 = simplifyLoad(em, valSz))
3710 em = SL2;
3711 return em;
3712 }
3713 if (auto LI = dyn_cast<LoadInst>(EVI->getAggregateOperand())) {
3714 auto offset = preOffset;
3715
3716 auto &DL = LI->getParent()->getParent()->getParent()->getDataLayout();
3717 offset += (size_t)getAggregateElementOffset(
3718 DL, EVI->getOperand(0)->getType(), EVI->getIndices());
3719
3720 if (valSz == 0) {
3721 auto &DL = EVI->getParent()->getParent()->getParent()->getDataLayout();
3722 valSz = (DL.getTypeSizeInBits(EVI->getType()) + 7) / 8;
3723 }
3724 return simplifyLoad(LI, valSz, offset);
3725 }
3726 }
3727 return nullptr;
3728}
3729
3730Value *GetFunctionValFromValue(Value *fn) {
3731 while (!isa<Function>(fn)) {
3732 if (auto ci = dyn_cast<CastInst>(fn)) {
3733 fn = ci->getOperand(0);
3734 continue;
3735 }
3736 if (auto ci = dyn_cast<ConstantExpr>(fn)) {
3737 if (ci->isCast()) {
3738 fn = ci->getOperand(0);
3739 continue;
3740 }
3741 }
3742 if (auto ci = dyn_cast<BlockAddress>(fn)) {
3743 fn = ci->getFunction();
3744 continue;
3745 }
3746 if (auto *GA = dyn_cast<GlobalAlias>(fn)) {
3747 fn = GA->getAliasee();
3748 continue;
3749 }
3750 if (auto *Call = dyn_cast<CallInst>(fn)) {
3751 if (auto F = Call->getCalledFunction()) {
3752 SmallPtrSet<Value *, 1> ret;
3753 for (auto &BB : *F) {
3754 if (auto RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
3755 ret.insert(RI->getReturnValue());
3756 }
3757 }
3758 if (ret.size() == 1) {
3759 auto val = *ret.begin();
3760 val = GetFunctionValFromValue(val);
3761 if (isa<Constant>(val)) {
3762 fn = val;
3763 continue;
3764 }
3765 if (auto arg = dyn_cast<Argument>(val)) {
3766 fn = Call->getArgOperand(arg->getArgNo());
3767 continue;
3768 }
3769 }
3770 }
3771 }
3772 if (auto *Call = dyn_cast<InvokeInst>(fn)) {
3773 if (auto F = Call->getCalledFunction()) {
3774 SmallPtrSet<Value *, 1> ret;
3775 for (auto &BB : *F) {
3776 if (auto RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
3777 ret.insert(RI->getReturnValue());
3778 }
3779 }
3780 if (ret.size() == 1) {
3781 auto val = *ret.begin();
3782 while (isa<LoadInst>(val)) {
3783 auto v2 = simplifyLoad(val);
3784 if (v2) {
3785 val = v2;
3786 continue;
3787 }
3788 break;
3789 }
3790 if (isa<Constant>(val)) {
3791 fn = val;
3792 continue;
3793 }
3794 if (auto arg = dyn_cast<Argument>(val)) {
3795 fn = Call->getArgOperand(arg->getArgNo());
3796 continue;
3797 }
3798 }
3799 }
3800 }
3801 if (auto S = simplifyLoad(fn)) {
3802 fn = S;
3803 continue;
3804 }
3805 break;
3806 }
3807
3808 return fn;
3809}
3810
3811Function *GetFunctionFromValue(Value *fn) {
3812 return dyn_cast<Function>(GetFunctionValFromValue(fn));
3813}
3814
3815Function *getFirstFunctionDefinition(Module &M) {
3816 for (auto &F : M) {
3817 if (!F.isDeclaration()) {
3818 return &F;
3819 }
3820 }
3821 return nullptr;
3822}
3823
3824/// Decompose `in` as `prefix + floatType + stem + suffix` using the supplied
3825/// tables, setting the out-parameters to the entries that matched.
3826///
3827/// No name the tables in `extractBLAS` can spell decomposes in more than one
3828/// way, so peeling the fixed parts off either end of `in` and looking up what
3829/// remains is equivalent to comparing `in` against every name the tables can
3830/// generate -- but it allocates nothing. That matters: `extractBLAS` runs for
3831/// every call instruction visited by type analysis and by
3832/// `is_use_directly_needed_in_reverse`, and materialising the ~1200 candidate
3833/// `std::string`s per query dominated compile time on call-heavy modules that
3834/// contain no BLAS at all.
3835static bool matchBLASName(StringRef in, ArrayRef<const char *> prefixes,
3836 ArrayRef<const char *> floatTypes,
3837 ArrayRef<const char *> stems,
3838 ArrayRef<const char *> suffixes,
3839 const char *&prefixOut, const char *&floatTypeOut,
3840 const char *&stemOut, const char *&suffixOut) {
3841 for (const char *p : prefixes) {
3842 StringRef afterPrefix = in;
3843 if (!afterPrefix.consume_front(p))
3844 continue;
3845 for (const char *t : floatTypes) {
3846 StringRef afterType = afterPrefix;
3847 if (!afterType.consume_front(t))
3848 continue;
3849 for (const char *s : suffixes) {
3850 StringRef stem = afterType;
3851 if (!stem.consume_back(s))
3852 continue;
3853 for (const char *f : stems) {
3854 if (stem != f)
3855 continue;
3856 prefixOut = p;
3857 floatTypeOut = t;
3858 stemOut = f;
3859 suffixOut = s;
3860 return true;
3861 }
3862 }
3863 }
3864 }
3865 return false;
3866}
3867
3868#if LLVM_VERSION_MAJOR >= 16
3869std::optional<BlasInfo> extractBLAS(llvm::StringRef in)
3870#else
3871llvm::Optional<BlasInfo> extractBLAS(llvm::StringRef in)
3872#endif
3873{
3874 static const char *extractable[] = {
3875 "dot", "scal", "axpy", "gemv", "gemm", "spmv", "syrk", "nrm2",
3876 "trmm", "trmv", "symm", "potrf", "potrs", "copy", "spmv", "syr2k",
3877 "potrs", "getrf", "getrs", "trtrs", "getri", "symv", "lacpy", "trsv",
3878 };
3879 static const char *floatType[] = {"s", "d", "c", "z"};
3880 static const char *prefixes[] = {"" /*Fortran*/, "cblas_"};
3881 static const char *suffixes[] = {"", "_", "64_", "_64_"};
3882
3883 const char *p = nullptr, *t = nullptr, *f = nullptr, *s = nullptr;
3884 if (matchBLASName(in, prefixes, floatType, extractable, suffixes, p, t, f,
3885 s)) {
3886 bool is64 = llvm::StringRef(s).contains("64");
3887 return BlasInfo{
3888 t, p, s, f, is64,
3889 };
3890 }
3891
3892 // c interface to cublas
3893 static const char *cuCFloatType[] = {"S", "D", "C", "Z"};
3894 static const char *cuCPrefixes[] = {"cublas"};
3895 static const char *cuSuffixes[] = {"", "_v2", "_64", "_v2_64"};
3896 if (matchBLASName(in, cuCPrefixes, cuCFloatType, extractable, cuSuffixes, p,
3897 t, f, s)) {
3898 bool is64 = llvm::StringRef(s).contains("64");
3899 return BlasInfo{
3900 t, p, s, f, is64,
3901 };
3902 }
3903
3904 // Fortran interface to cublas
3905 static const char *cuFFloatType[] = {"s", "d", "c", "z"};
3906 static const char *cuFPrefixes[] = {"cublas_"};
3907 static const char *cuFSuffixes[] = {""};
3908 if (matchBLASName(in, cuFPrefixes, cuFFloatType, extractable, cuFSuffixes, p,
3909 t, f, s)) {
3910 return BlasInfo{
3911 t, p, "", f, false,
3912 };
3913 }
3914
3915 return {};
3916}
3917
3918llvm::Constant *getUndefinedValueForType(llvm::Module &M, llvm::Type *T,
3919 bool forceZero) {
3921 return cast<Constant>(
3922 unwrap(EnzymeUndefinedValueForType(wrap(&M), wrap(T), forceZero)));
3923 else if (EnzymeZeroCache || forceZero)
3924 return Constant::getNullValue(T);
3925 else
3926 return UndefValue::get(T);
3927}
3928
3929llvm::Value *SanitizeDerivatives(llvm::Value *val, llvm::Value *toset,
3930 llvm::IRBuilder<> &BuilderM,
3931 llvm::Value *mask) {
3932 if (EnzymeCheckDerivativeNaN && toset->getType()->isFPOrFPVectorTy()) {
3933 auto current_bb = BuilderM.GetInsertBlock();
3934 auto fn = current_bb->getParent();
3935 auto mod = fn->getParent();
3936 auto &Context = mod->getContext();
3937
3938 std::string type_str;
3939 llvm::raw_string_ostream type_ss(type_str);
3940 toset->getType()->print(type_ss);
3941 std::string fn_name = "__enzyme_sanitize_nan_" + type_str;
3942
3943 llvm::FunctionType *SanitizeFT = llvm::FunctionType::get(
3944 llvm::Type::getVoidTy(Context),
3945 {toset->getType(), getInt8PtrTy(Context)}, false);
3946
3947 auto SanitizeFCallee = mod->getOrInsertFunction(fn_name, SanitizeFT);
3948 llvm::Function *SanitizeF =
3949 llvm::cast<llvm::Function>(SanitizeFCallee.getCallee());
3950
3951 if (SanitizeF->empty()) {
3952 SanitizeF->setLinkage(Function::LinkageTypes::InternalLinkage);
3953 llvm::BasicBlock *entry =
3954 llvm::BasicBlock::Create(Context, "entry", SanitizeF);
3955 llvm::BasicBlock *good =
3956 llvm::BasicBlock::Create(Context, "good", SanitizeF);
3957 llvm::BasicBlock *bad =
3958 llvm::BasicBlock::Create(Context, "bad", SanitizeF);
3959
3960 llvm::IRBuilder<> B(entry);
3961 llvm::Value *inp = SanitizeF->getArg(0);
3962 llvm::Value *msg_ptr = SanitizeF->getArg(1);
3963
3964 llvm::Value *cmp = B.CreateFCmpUNO(inp, inp);
3965 if (auto VT = llvm::dyn_cast<llvm::VectorType>(inp->getType())) {
3966#if LLVM_VERSION_MAJOR >= 12
3967 unsigned len = VT->getElementCount().getKnownMinValue();
3968#else
3969 unsigned len = VT->getNumElements();
3970#endif
3971 llvm::Value *res = B.CreateExtractElement(cmp, (uint64_t)0);
3972 for (unsigned i = 1; i < len; ++i) {
3973 res = B.CreateOr(res, B.CreateExtractElement(cmp, (uint64_t)i));
3974 }
3975 cmp = res;
3976 }
3977 B.CreateCondBr(cmp, bad, good);
3978
3979 B.SetInsertPoint(good);
3980 B.CreateRetVoid();
3981
3982 B.SetInsertPoint(bad);
3983 if (CustomErrorHandler) {
3984 CustomErrorHandler("NaN Error", wrap(inp), ErrorType::NaNError, nullptr,
3985 wrap(msg_ptr), wrap(&B));
3986 } else {
3987 llvm::FunctionType *PutsFT = llvm::FunctionType::get(
3988 llvm::Type::getInt32Ty(Context), {getInt8PtrTy(Context)}, false);
3989 auto PutsF = mod->getOrInsertFunction("puts", PutsFT);
3990 B.CreateCall(PutsF, msg_ptr);
3991
3992 llvm::FunctionType *ExitFT =
3993 llvm::FunctionType::get(llvm::Type::getVoidTy(Context),
3994 {llvm::Type::getInt32Ty(Context)}, false);
3995 auto ExitF = mod->getOrInsertFunction("exit", ExitFT);
3996 B.CreateCall(
3997 ExitF, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Context), 1));
3998 }
3999 B.CreateUnreachable();
4000 }
4001
4002 std::string stringv = "Enzyme: Found nan while computing derivative of ";
4003 if (val) {
4004 std::string str;
4005 llvm::raw_string_ostream ss(str);
4006 if (auto inst = llvm::dyn_cast<llvm::Instruction>(val)) {
4007 ss << *inst << "\n";
4008 emit_backtrace(inst, ss);
4009 } else {
4010 ss << *val << "\n";
4011 }
4012 stringv += ss.str();
4013 } else {
4014 stringv += "\n";
4015 }
4016
4017 BuilderM.CreateCall(SanitizeFCallee, {toset, getString(*mod, stringv)});
4018 }
4019
4021 return unwrap(EnzymeSanitizeDerivatives(wrap(val), wrap(toset),
4022 wrap(&BuilderM), wrap(mask)));
4023 return toset;
4024}
4025
4026llvm::FastMathFlags getFast() {
4027 llvm::FastMathFlags f;
4028 if (EnzymeFastMath)
4029 f.set();
4030 return f;
4031}
4032
4033void addValueToCache(llvm::Value *arg, bool cache_arg, llvm::Type *ty,
4034 llvm::SmallVectorImpl<llvm::Value *> &cacheValues,
4035 llvm::IRBuilder<> &BuilderZ, const Twine &name) {
4036 if (!cache_arg)
4037 return;
4038 if (!arg->getType()->isPointerTy()) {
4039 assert(arg->getType() == ty);
4040 cacheValues.push_back(arg);
4041 return;
4042 }
4043#if LLVM_VERSION_MAJOR < 17
4044 auto PT = cast<PointerType>(arg->getType());
4045#if LLVM_VERSION_MAJOR <= 14
4046 if (PT->getElementType() != ty)
4047 arg = BuilderZ.CreatePointerCast(
4048 arg, getPointerType(ty, PT->getAddressSpace()), "pcld." + name);
4049#else
4050 auto PT2 = getPointerType(ty, PT->getAddressSpace());
4051 if (!PT->isOpaqueOrPointeeTypeMatches(PT2))
4052 arg = BuilderZ.CreatePointerCast(
4053 arg, getPointerType(ty, PT->getAddressSpace()), "pcld." + name);
4054#endif
4055#endif
4056 arg = BuilderZ.CreateLoad(ty, arg, "avld." + name);
4057 cacheValues.push_back(arg);
4058}
4059
4060// julia_decl null means not julia decl, otherwise it is the integer type needed
4061// to cast to
4062llvm::Value *to_blas_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef,
4063 bool cublas, IntegerType *julia_decl,
4064 IRBuilder<> &entryBuilder,
4065 llvm::Twine const &name) {
4066 if (!byRef)
4067 return V;
4068
4069 Value *allocV =
4070 entryBuilder.CreateAlloca(V->getType(), nullptr, "byref." + name);
4071 B.CreateStore(V, allocV);
4072
4073 if (julia_decl)
4074 allocV = B.CreatePointerCast(allocV, getInt8PtrTy(V->getContext()),
4075 "intcast." + name);
4076
4077 return allocV;
4078}
4079llvm::Value *to_blas_fp_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef,
4080 Type *fpTy, IRBuilder<> &entryBuilder,
4081 llvm::Twine const &name) {
4082 if (!byRef)
4083 return V;
4084
4085 Value *allocV =
4086 entryBuilder.CreateAlloca(V->getType(), nullptr, "byref." + name);
4087 B.CreateStore(V, allocV);
4088
4089 if (fpTy)
4090 allocV = B.CreatePointerCast(allocV, fpTy, "fpcast." + name);
4091
4092 return allocV;
4093}
4094
4095Value *is_lower(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas) {
4096 if (cublas) {
4097 Value *isNormal = nullptr;
4098 isNormal = B.CreateICmpEQ(
4099 uplo, ConstantInt::get(uplo->getType(),
4100 /*cublasFillMode_t::CUBLAS_FILL_MODE_LOWER*/ 0));
4101 return isNormal;
4102 }
4103 if (auto CI = dyn_cast<ConstantInt>(uplo)) {
4104 if (CI->getValue() == 'L' || CI->getValue() == 'l')
4105 return ConstantInt::getTrue(B.getContext());
4106 if (CI->getValue() == 'U' || CI->getValue() == 'u')
4107 return ConstantInt::getFalse(B.getContext());
4108 }
4109 if (byRef) {
4110 // can't inspect opaque ptr, so assume 8 (Julia)
4111 IntegerType *charTy = IntegerType::get(uplo->getContext(), 8);
4112 uplo = B.CreateLoad(charTy, uplo, "loaded.trans");
4113
4114 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'L'));
4115 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'l'));
4116 // fortran blas
4117 return B.CreateOr(isl, isL);
4118 } else {
4119 // we can inspect scalars
4120 auto capi = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 122));
4121 // TODO we really should just return capi, but for sake of consistency,
4122 // we will accept either here.
4123 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'L'));
4124 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'l'));
4125 return B.CreateOr(capi, B.CreateOr(isl, isL));
4126 }
4127}
4128
4129Value *is_nonunit(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas) {
4130 if (cublas) {
4131 Value *isNormal = nullptr;
4132 isNormal =
4133 B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(),
4134 /*CUBLAS_DIAG_NON_UNIT*/ 0));
4135 return isNormal;
4136 }
4137 if (auto CI = dyn_cast<ConstantInt>(uplo)) {
4138 if (CI->getValue() == 'N' || CI->getValue() == 'n')
4139 return ConstantInt::getTrue(B.getContext());
4140 if (CI->getValue() == 'U' || CI->getValue() == 'u')
4141 return ConstantInt::getFalse(B.getContext());
4142 }
4143 if (byRef) {
4144 // can't inspect opaque ptr, so assume 8 (Julia)
4145 IntegerType *charTy = IntegerType::get(uplo->getContext(), 8);
4146 uplo = B.CreateLoad(charTy, uplo, "loaded.nonunit");
4147
4148 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'N'));
4149 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'n'));
4150 // fortran blas
4151 return B.CreateOr(isl, isL);
4152 } else {
4153 // we can inspect scalars
4154 auto capi = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 131));
4155 // TODO we really should just return capi, but for sake of consistency,
4156 // we will accept either here.
4157 auto isL = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'N'));
4158 auto isl = B.CreateICmpEQ(uplo, ConstantInt::get(uplo->getType(), 'n'));
4159 return B.CreateOr(capi, B.CreateOr(isl, isL));
4160 }
4161}
4162
4163llvm::Value *is_normal(IRBuilder<> &B, llvm::Value *trans, bool byRef,
4164 bool cublas) {
4165 if (cublas) {
4166 Value *isNormal = nullptr;
4167 isNormal = B.CreateICmpEQ(
4168 trans, ConstantInt::get(trans->getType(),
4169 /*cublasOperation_t::CUBLAS_OP_N*/ 0));
4170 return isNormal;
4171 }
4172 // Explicitly support 'N' always, since we use in the rule infra
4173 if (auto CI = dyn_cast<ConstantInt>(trans)) {
4174 if (CI->getValue() == 'N' || CI->getValue() == 'n')
4175 return ConstantInt::getTrue(
4176 B.getContext()); //(Type::getInt1Ty(B.getContext()), true);
4177 }
4178 if (byRef) {
4179 // can't inspect opaque ptr, so assume 8 (Julia)
4180 IntegerType *charTy = IntegerType::get(trans->getContext(), 8);
4181 trans = B.CreateLoad(charTy, trans, "loaded.trans");
4182
4183 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'N'));
4184 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'n'));
4185 // fortran blas
4186 return B.CreateOr(isn, isN);
4187 } else {
4188 // TODO we really should just return capi, but for sake of consistency,
4189 // we will accept either here.
4190 // we can inspect scalars
4191 auto capi = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 111));
4192 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'N'));
4193 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'n'));
4194 // fortran blas
4195 return B.CreateOr(capi, B.CreateOr(isn, isN));
4196 }
4197}
4198
4199llvm::Value *is_left(IRBuilder<> &B, llvm::Value *side, bool byRef,
4200 bool cublas) {
4201 if (cublas) {
4202 Value *isNormal = nullptr;
4203 isNormal = B.CreateICmpEQ(
4204 side, ConstantInt::get(side->getType(),
4205 /*cublasSideMode_t::CUBLAS_SIDE_LEFT*/ 0));
4206 return isNormal;
4207 }
4208 // Explicitly support 'L'/'R' always, since we use in the rule infra
4209 if (auto CI = dyn_cast<ConstantInt>(side)) {
4210 if (CI->getValue() == 'L' || CI->getValue() == 'l')
4211 return ConstantInt::getTrue(B.getContext());
4212 if (CI->getValue() == 'R' || CI->getValue() == 'r')
4213 return ConstantInt::getFalse(B.getContext());
4214 }
4215 if (byRef) {
4216 // can't inspect opaque ptr, so assume 8 (Julia)
4217 IntegerType *charTy = IntegerType::get(side->getContext(), 8);
4218 side = B.CreateLoad(charTy, side, "loaded.side");
4219
4220 auto isL = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 'L'));
4221 auto isl = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 'l'));
4222 // fortran blas
4223 return B.CreateOr(isl, isL);
4224 } else {
4225 // TODO we really should just return capi, but for sake of consistency,
4226 // we will accept either here.
4227 // we can inspect scalars
4228 auto capi = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 141));
4229 auto isL = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 'L'));
4230 auto isl = B.CreateICmpEQ(side, ConstantInt::get(side->getType(), 'l'));
4231 // fortran blas
4232 return B.CreateOr(capi, B.CreateOr(isl, isL));
4233 }
4234}
4235
4236// Ok. Here we are.
4237// netlib declares trans args as something out of
4238// N,n,T,t,C,c, represented as 8 bit chars.
4239// However, if we ask openBlas c ABI,
4240// it is one of the following 32 bit integers values:
4241// enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113};
4242llvm::Value *transpose(std::string floatType, IRBuilder<> &B, llvm::Value *V,
4243 bool cublas) {
4244 llvm::Type *T = V->getType();
4245 if (cublas) {
4246 auto isT1 = B.CreateICmpEQ(V, ConstantInt::get(T, 1));
4247 auto isT0 = B.CreateICmpEQ(V, ConstantInt::get(T, 0));
4248 return B.CreateSelect(isT1, ConstantInt::get(V->getType(), 0),
4249 B.CreateSelect(isT0,
4250 ConstantInt::get(V->getType(), 1),
4251 ConstantInt::get(V->getType(), 42)));
4252 } else if (T->isIntegerTy(8)) {
4253 if (floatType == "z" || floatType == "c") {
4254 auto isn = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'n'));
4255 auto sel1 = B.CreateSelect(isn, ConstantInt::get(V->getType(), 'c'),
4256 ConstantInt::get(V->getType(), 0));
4257
4258 auto isN = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'N'));
4259 auto sel2 =
4260 B.CreateSelect(isN, ConstantInt::get(V->getType(), 'C'), sel1);
4261
4262 auto ist = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'c'));
4263 auto sel3 =
4264 B.CreateSelect(ist, ConstantInt::get(V->getType(), 'n'), sel2);
4265
4266 auto isT = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'C'));
4267 return B.CreateSelect(isT, ConstantInt::get(V->getType(), 'N'), sel3);
4268 } else {
4269 // the base case here of 'C' or 'c' becomes simply 'N'
4270 auto isn = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'n'));
4271 auto sel1 = B.CreateSelect(isn, ConstantInt::get(V->getType(), 't'),
4272 ConstantInt::get(V->getType(), 'N'));
4273
4274 auto isN = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'N'));
4275 auto sel2 =
4276 B.CreateSelect(isN, ConstantInt::get(V->getType(), 'T'), sel1);
4277
4278 auto ist = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 't'));
4279 auto sel3 =
4280 B.CreateSelect(ist, ConstantInt::get(V->getType(), 'n'), sel2);
4281
4282 auto isT = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 'T'));
4283 return B.CreateSelect(isT, ConstantInt::get(V->getType(), 'N'), sel3);
4284 }
4285
4286 } else if (T->isIntegerTy(32)) {
4287 auto is111 = B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 111));
4288 auto sel1 = B.CreateSelect(
4289 B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 112)),
4290 ConstantInt::get(V->getType(), 111), ConstantInt::get(V->getType(), 0));
4291 return B.CreateSelect(is111, ConstantInt::get(V->getType(), 112), sel1);
4292 } else {
4293 std::string s;
4294 llvm::raw_string_ostream ss(s);
4295 ss << "cannot handle unknown trans blas value\n" << V;
4296 if (CustomErrorHandler) {
4297 CustomErrorHandler(ss.str().c_str(), nullptr, ErrorType::NoDerivative,
4298 nullptr, nullptr, nullptr);
4299 } else {
4300 EmitFailure("unknown trans blas value", B.getCurrentDebugLocation(),
4301 B.GetInsertBlock()->getParent(), ss.str());
4302 }
4303 return V;
4304 }
4305}
4306
4307// Implement the following logic to get the width of a matrix
4308// if (cache_A) {
4309// ld_A = (arg_transa == 'N') ? arg_k : arg_m;
4310// } else {
4311// ld_A = arg_lda;
4312// }
4313llvm::Value *get_cached_mat_width(llvm::IRBuilder<> &B,
4314 llvm::ArrayRef<llvm::Value *> trans,
4315 llvm::Value *arg_ld, llvm::Value *dim1,
4316 llvm::Value *dim2, bool cacheMat, bool byRef,
4317 bool cublas) {
4318 if (!cacheMat)
4319 return arg_ld;
4320
4321 assert(trans.size() == 1);
4322
4323 llvm::Value *width =
4324 CreateSelect(B, is_normal(B, trans[0], byRef, cublas), dim2, dim1);
4325
4326 return width;
4327}
4328
4329llvm::Value *transpose(std::string floatType, llvm::IRBuilder<> &B,
4330 llvm::Value *V, bool byRef, bool cublas,
4331 llvm::IntegerType *julia_decl,
4332 llvm::IRBuilder<> &entryBuilder,
4333 const llvm::Twine &name) {
4334
4335 if (!byRef) {
4336 // Explicitly support 'N' always, since we use in the rule infra
4337 if (auto CI = dyn_cast<ConstantInt>(V)) {
4338 if (floatType == "c" || floatType == "z") {
4339 if (CI->getValue() == 'N')
4340 return ConstantInt::get(CI->getType(), 'C');
4341 if (CI->getValue() == 'c')
4342 return ConstantInt::get(CI->getType(), 'c');
4343 } else {
4344 if (CI->getValue() == 'N')
4345 return ConstantInt::get(CI->getType(), 'T');
4346 if (CI->getValue() == 'n')
4347 return ConstantInt::get(CI->getType(), 't');
4348 }
4349 }
4350
4351 // cblas
4352 if (!cublas)
4353 return B.CreateSelect(
4354 B.CreateICmpEQ(V, ConstantInt::get(V->getType(), 111)),
4355 ConstantInt::get(V->getType(), 112),
4356 ConstantInt::get(V->getType(), 111));
4357 }
4358
4359 if (byRef) {
4360 auto charType = IntegerType::get(V->getContext(), 8);
4361 V = B.CreateLoad(charType, V, "ld." + name);
4362 }
4363
4364 V = transpose(floatType, B, V, cublas);
4365
4366 return to_blas_callconv(B, V, byRef, cublas, julia_decl, entryBuilder,
4367 "transpose." + name);
4368}
4369
4370llvm::Value *load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType,
4371 llvm::Value *V, bool byRef) {
4372 if (!byRef)
4373 return V;
4374
4375 if (V->getType()->isIntegerTy())
4376 V = B.CreateIntToPtr(V, getUnqual(intType));
4377 else
4378 V = B.CreatePointerCast(
4379 V, getPointerType(intType,
4380 cast<PointerType>(V->getType())->getAddressSpace()));
4381 return B.CreateLoad(intType, V);
4382}
4383
4384SmallVector<llvm::Value *, 1> get_blas_row(llvm::IRBuilder<> &B,
4385 ArrayRef<llvm::Value *> transA,
4386 bool byRef, bool cublas) {
4387 assert(transA.size() == 1);
4388 auto trans = transA[0];
4389 if (byRef) {
4390 auto charType = IntegerType::get(trans->getContext(), 8);
4391 trans = B.CreateLoad(charType, trans, "ld.row.trans");
4392 }
4393
4394 Value *cond = nullptr;
4395 if (!cublas) {
4396
4397 if (!byRef) {
4398 cond = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 111));
4399 } else {
4400 auto isn = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'n'));
4401 auto isN = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 'N'));
4402 cond = B.CreateOr(isN, isn);
4403 }
4404 } else {
4405 // CUBLAS_OP_N = 0, CUBLAS_OP_T = 1, CUBLAS_OP_C = 2
4406 // TODO: verify
4407 cond = B.CreateICmpEQ(trans, ConstantInt::get(trans->getType(), 0));
4408 }
4409 return {cond};
4410}
4411SmallVector<llvm::Value *, 1> get_blas_row(llvm::IRBuilder<> &B,
4412 ArrayRef<llvm::Value *> transA,
4413 ArrayRef<llvm::Value *> row,
4414 ArrayRef<llvm::Value *> col,
4415 bool byRef, bool cublas) {
4416 auto conds = get_blas_row(B, transA, byRef, cublas);
4417 assert(row.size() == col.size());
4418 SmallVector<Value *, 1> toreturn;
4419 for (size_t i = 0; i < row.size(); i++) {
4420 auto lhs = row[i];
4421 auto rhs = col[i];
4422 if (lhs->getType() != rhs->getType())
4423 rhs = B.CreatePointerCast(rhs, lhs->getType());
4424 toreturn.push_back(B.CreateSelect(conds[0], lhs, rhs));
4425 }
4426 return toreturn;
4427}
4428
4429// return how many Special pointers are in T (count > 0),
4430// and if there is anything else in T (all == false)
4432 if (isa<PointerType>(T)) {
4433 if (isSpecialPtr(T)) {
4434 count++;
4435 if (T->getPointerAddressSpace() != AddressSpace::Tracked)
4436 derived = true;
4437 }
4438 } else if (isa<StructType>(T) || isa<ArrayType>(T) || isa<VectorType>(T)) {
4439 for (Type *ElT : T->subtypes()) {
4440 auto sub = CountTrackedPointers(ElT);
4441 count += sub.count;
4442 all &= sub.all;
4443 derived |= sub.derived;
4444 }
4445 if (isa<ArrayType>(T))
4446 count *= cast<ArrayType>(T)->getNumElements();
4447 else if (isa<VectorType>(T)) {
4448#if LLVM_VERSION_MAJOR >= 12
4449 count *= cast<VectorType>(T)->getElementCount().getKnownMinValue();
4450#else
4451 count *= cast<VectorType>(T)->getNumElements();
4452#endif
4453 }
4454 }
4455 if (count == 0)
4456 all = false;
4457}
4458
4459#if LLVM_VERSION_MAJOR >= 20
4460bool collectOffset(GEPOperator *gep, const DataLayout &DL, unsigned BitWidth,
4461 SmallMapVector<Value *, APInt, 4> &VariableOffsets,
4462 APInt &ConstantOffset)
4463#else
4464bool collectOffset(GEPOperator *gep, const DataLayout &DL, unsigned BitWidth,
4465 MapVector<Value *, APInt> &VariableOffsets,
4466 APInt &ConstantOffset)
4467#endif
4468{
4469#if LLVM_VERSION_MAJOR >= 13
4470 return gep->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset);
4471#else
4472 assert(BitWidth == DL.getIndexSizeInBits(gep->getPointerAddressSpace()) &&
4473 "The offset bit width does not match DL specification.");
4474
4475 auto CollectConstantOffset = [&](APInt Index, uint64_t Size) {
4476 Index = Index.sextOrTrunc(BitWidth);
4477 APInt IndexedSize = APInt(BitWidth, Size);
4478 ConstantOffset += Index * IndexedSize;
4479 };
4480
4481 for (gep_type_iterator GTI = gep_type_begin(gep), GTE = gep_type_end(gep);
4482 GTI != GTE; ++GTI) {
4483 // Scalable vectors are multiplied by a runtime constant.
4484 bool ScalableType = isa<ScalableVectorType>(GTI.getIndexedType());
4485
4486 Value *V = GTI.getOperand();
4487 StructType *STy = GTI.getStructTypeOrNull();
4488 // Handle ConstantInt if possible.
4489 if (auto ConstOffset = dyn_cast<ConstantInt>(V)) {
4490 if (ConstOffset->isZero())
4491 continue;
4492 // If the type is scalable and the constant is not zero (vscale * n * 0 =
4493 // 0) bailout.
4494 // TODO: If the runtime value is accessible at any point before DWARF
4495 // emission, then we could potentially keep a forward reference to it
4496 // in the debug value to be filled in later.
4497 if (ScalableType)
4498 return false;
4499 // Handle a struct index, which adds its field offset to the pointer.
4500 if (STy) {
4501 unsigned ElementIdx = ConstOffset->getZExtValue();
4502 const StructLayout *SL = DL.getStructLayout(STy);
4503 // Element offset is in bytes.
4504 CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)),
4505 1);
4506 continue;
4507 }
4508 CollectConstantOffset(ConstOffset->getValue(),
4509 DL.getTypeAllocSize(GTI.getIndexedType()));
4510 continue;
4511 }
4512
4513 if (STy || ScalableType)
4514 return false;
4515 APInt IndexedSize =
4516 APInt(BitWidth, DL.getTypeAllocSize(GTI.getIndexedType()));
4517 // Insert an initial offset of 0 for V iff none exists already, then
4518 // increment the offset by IndexedSize.
4519 if (IndexedSize != 0) {
4520 VariableOffsets.insert({V, APInt(BitWidth, 0)});
4521 VariableOffsets[V] += IndexedSize;
4522 }
4523 }
4524 return true;
4525#endif
4526}
4527
4528llvm::CallInst *createIntrinsicCall(llvm::IRBuilderBase &B,
4529 llvm::Intrinsic::ID ID, llvm::Type *RetTy,
4530 llvm::ArrayRef<llvm::Value *> Args,
4531 llvm::Instruction *FMFSource,
4532 const llvm::Twine &Name) {
4533#if LLVM_VERSION_MAJOR >= 23
4534 llvm::CallInst *nres = dyn_cast<llvm::CallInst>(
4535 B.CreateIntrinsic(RetTy, ID, Args, FMFSource, Name));
4536#elif LLVM_VERSION_MAJOR >= 16
4537 llvm::CallInst *nres = B.CreateIntrinsic(RetTy, ID, Args, FMFSource, Name);
4538#else
4539 SmallVector<Intrinsic::IITDescriptor, 1> Table;
4540 Intrinsic::getIntrinsicInfoTableEntries(ID, Table);
4541 ArrayRef<Intrinsic::IITDescriptor> TableRef(Table);
4542
4543 SmallVector<Type *, 2> ArgTys;
4544 ArgTys.reserve(Args.size());
4545 for (auto &I : Args)
4546 ArgTys.push_back(I->getType());
4547 FunctionType *FTy = FunctionType::get(RetTy, ArgTys, false);
4548 SmallVector<Type *, 2> OverloadTys;
4549 Intrinsic::MatchIntrinsicTypesResult Res =
4550 matchIntrinsicSignature(FTy, TableRef, OverloadTys);
4551 (void)Res;
4552 assert(Res == Intrinsic::MatchIntrinsicTypes_Match && TableRef.empty() &&
4553 "Wrong types for intrinsic!");
4554 Function *Fn = Intrinsic::getDeclaration(B.GetInsertPoint()->getModule(), ID,
4555 OverloadTys);
4556 CallInst *nres = B.CreateCall(Fn, Args, {}, Name);
4557 if (FMFSource)
4558 nres->copyFastMathFlags(FMFSource);
4559#endif
4560 return nres;
4561}
4562
4563/* Bithack to compute 1 ulp as follows:
4564double ulp(double res) {
4565 double nres = res;
4566 (*(uint64_t*)&nres) = 0x1 ^ *(uint64_t*)&nres;
4567 return abs(nres - res);
4568}
4569*/
4570llvm::Value *get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res) {
4571 auto ty = res->getType();
4572 unsigned tsize = builder.GetInsertBlock()
4573 ->getParent()
4574 ->getParent()
4575 ->getDataLayout()
4576 .getTypeSizeInBits(ty);
4577
4578 auto ity = IntegerType::get(ty->getContext(), tsize);
4579
4580 auto as_int = builder.CreateBitCast(res, ity);
4581 auto masked = builder.CreateXor(as_int, ConstantInt::get(ity, 1));
4582 auto neighbor = builder.CreateBitCast(masked, ty);
4583
4584 auto diff = builder.CreateFSub(res, neighbor);
4585
4586 auto absres = builder.CreateIntrinsic(Intrinsic::fabs,
4587 ArrayRef<Type *>(diff->getType()),
4588 ArrayRef<Value *>(diff));
4589
4590 return absres;
4591}
4592
4593llvm::Value *EmitNoDerivativeError(const std::string &message,
4594 llvm::Instruction &inst,
4595 GradientUtils *gutils,
4596 llvm::IRBuilder<> &Builder2,
4597 llvm::Value *condition) {
4598 if (CustomErrorHandler) {
4599 return unwrap(CustomErrorHandler(message.c_str(), wrap(&inst),
4601 wrap(condition), wrap(&Builder2)));
4602 } else if (EnzymeRuntimeError) {
4603 auto &M = *inst.getParent()->getParent()->getParent();
4604 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4605 {getInt8PtrTy(M.getContext())}, false);
4606 std::string str;
4607 raw_string_ostream ss(str);
4608 ss << message << "\n";
4609 emit_backtrace(&inst, ss);
4610 auto msg = getString(M, ss.str());
4611 ;
4612 auto PutsF = M.getOrInsertFunction("puts", FT);
4613 Builder2.CreateCall(PutsF, msg);
4614
4615 FunctionType *FT2 =
4616 FunctionType::get(Type::getVoidTy(M.getContext()),
4617 {Type::getInt32Ty(M.getContext())}, false);
4618
4619 auto ExitF = M.getOrInsertFunction("exit", FT2);
4620 Builder2.CreateCall(ExitF,
4621 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4622 return nullptr;
4623 } else {
4624 if (StringRef(message).contains("cannot handle above cast")) {
4625 gutils->TR.dump();
4626 }
4627 EmitFailure("NoDerivative", inst.getDebugLoc(), &inst, message);
4628 return nullptr;
4629 }
4630}
4631
4632bool EmitNoDerivativeError(const std::string &message, Value *todiff,
4633 RequestContext &context) {
4634 Value *toshow = todiff;
4635 if (context.req) {
4636 toshow = context.req;
4637 }
4638 if (CustomErrorHandler) {
4639 CustomErrorHandler(message.c_str(), wrap(toshow), ErrorType::NoDerivative,
4640 nullptr, wrap(todiff), wrap(context.ip));
4641 return true;
4642 } else if (context.ip && EnzymeRuntimeError) {
4643 auto &M = *context.ip->GetInsertBlock()->getParent()->getParent();
4644 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4645 {getInt8PtrTy(M.getContext())}, false);
4646 std::string str;
4647 raw_string_ostream ss(str);
4648 ss << message << "\n";
4649 if (auto inst = dyn_cast<Instruction>(todiff))
4650 emit_backtrace(inst, ss);
4651 auto msg = getString(M, ss.str());
4652 auto PutsF = M.getOrInsertFunction("puts", FT);
4653 context.ip->CreateCall(PutsF, msg);
4654
4655 FunctionType *FT2 =
4656 FunctionType::get(Type::getVoidTy(M.getContext()),
4657 {Type::getInt32Ty(M.getContext())}, false);
4658
4659 auto ExitF = M.getOrInsertFunction("exit", FT2);
4660 context.ip->CreateCall(
4661 ExitF, ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4662 return true;
4663 } else if (context.req) {
4664 EmitFailure("NoDerivative", context.req->getDebugLoc(), context.req,
4665 message);
4666 return true;
4667 } else if (auto arg = dyn_cast<Instruction>(todiff)) {
4668 auto loc = arg->getDebugLoc();
4669 EmitFailure("NoDerivative", loc, arg, message);
4670 return true;
4671 }
4672 return false;
4673}
4674
4675void EmitNoTypeError(const std::string &message, llvm::Instruction &inst,
4676 GradientUtils *gutils, llvm::IRBuilder<> &Builder2) {
4677 if (CustomErrorHandler) {
4678 CustomErrorHandler(message.c_str(), wrap(&inst), ErrorType::NoType,
4679 gutils->TR.analyzer, nullptr, wrap(&Builder2));
4680 } else if (EnzymeRuntimeError) {
4681 auto &M = *inst.getParent()->getParent()->getParent();
4682 FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()),
4683 {getInt8PtrTy(M.getContext())}, false);
4684 std::string str;
4685 raw_string_ostream ss(str);
4686 ss << message << "\n";
4687 emit_backtrace(&inst, ss);
4688 auto msg = getString(M, ss.str());
4689 auto PutsF = M.getOrInsertFunction("puts", FT);
4690 Builder2.CreateCall(PutsF, msg);
4691
4692 FunctionType *FT2 =
4693 FunctionType::get(Type::getVoidTy(M.getContext()),
4694 {Type::getInt32Ty(M.getContext())}, false);
4695
4696 auto ExitF = M.getOrInsertFunction("exit", FT2);
4697 Builder2.CreateCall(ExitF,
4698 ConstantInt::get(Type::getInt32Ty(M.getContext()), 1));
4699 } else {
4700 // Without a custom handler, append the function and its type analysis.
4701 // (A custom handler is given the analyzer and the instruction and can
4702 // produce these itself, if and as much as it wants.)
4703 std::string str;
4704 raw_string_ostream ss(str);
4705 ss << message << "\n";
4706 ss << "fn: " << *inst.getParent()->getParent() << "\n";
4707 if (gutils)
4708 gutils->TR.dump(ss);
4709 EmitFailure("CannotDeduceType", inst.getDebugLoc(), &inst, ss.str());
4710 }
4711}
4712
4713llvm::MDNode *sliceTrueType(const llvm::MDNode *md, size_t start,
4714 size_t length) {
4715 // Each (type, offset) entry describes the bytes from its offset up to the
4716 // next entry's offset.
4717 size_t end = start + length;
4718 if (end < start)
4719 end = SIZE_MAX;
4720 llvm::SmallVector<llvm::Metadata *, 8> out;
4721 auto I64 = llvm::Type::getInt64Ty(md->getContext());
4722 for (size_t i = 0; i < md->getNumOperands(); i += 2) {
4723 size_t offset =
4724 llvm::cast<llvm::ConstantInt>(
4725 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 1))
4726 ->getValue())
4727 ->getZExtValue();
4728 if (offset >= end)
4729 break;
4730 size_t next = SIZE_MAX;
4731 if (i + 3 < md->getNumOperands())
4732 next = llvm::cast<llvm::ConstantInt>(
4733 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 3))
4734 ->getValue())
4735 ->getZExtValue();
4736 if (next <= start)
4737 continue;
4738 size_t newoff = offset <= start ? 0 : offset - start;
4739 out.push_back(md->getOperand(i));
4740 out.push_back(
4741 llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(I64, newoff)));
4742 }
4743 if (out.empty())
4744 return nullptr;
4745 return llvm::MDNode::get(md->getContext(), out);
4746}
4747
4748std::vector<std::tuple<llvm::Type *, size_t, size_t>>
4749parseTrueType(const llvm::MDNode *md, DerivativeMode Mode, bool const_src) {
4750 std::vector<std::pair<ConcreteType, size_t>> parsed;
4751 for (size_t i = 0; i < md->getNumOperands(); i += 2) {
4752 ConcreteType base(
4753 llvm::cast<llvm::MDString>(md->getOperand(i))->getString(),
4754 md->getContext());
4755 auto size = llvm::cast<llvm::ConstantInt>(
4756 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i + 1))
4757 ->getValue())
4758 ->getSExtValue();
4759 parsed.emplace_back(base, size);
4760 }
4761
4762 std::vector<std::tuple<llvm::Type *, size_t, size_t>> toIterate;
4763 size_t idx = 0;
4764 while (idx < parsed.size()) {
4765
4766 auto dt = parsed[idx].first;
4767 size_t start = parsed[idx].second;
4768 size_t end = 0x0fffffff;
4769 for (idx = idx + 1; idx < parsed.size(); ++idx) {
4770 bool Legal = true;
4771 auto tmp = dt;
4772 auto next = parsed[idx].first;
4773 tmp.checkedOrIn(next, /*PointerIntSame*/ true, Legal);
4774 // Prevent fusion of {Anything, Float} since anything is an int rule
4775 // but float requires zeroing.
4776 if ((dt == BaseType::Anything &&
4777 (next != BaseType::Anything && next.isKnown())) ||
4778 (next == BaseType::Anything &&
4779 (dt != BaseType::Anything && dt.isKnown())))
4780 Legal = false;
4781 if (!Legal) {
4782 if (Mode == DerivativeMode::ForwardMode ||
4784 // if both are floats (of any type), forward mode is the same.
4785 // + [potentially zero if const, otherwise copy]
4786 // if both are int/pointer (of any type), also the same
4787 // + copy
4788 // if known non-constant, also the same
4789 // + copy
4790 if ((parsed[idx].first.isFloat() == nullptr) ==
4791 (parsed[idx - 1].first.isFloat() == nullptr)) {
4792 Legal = true;
4793 }
4794 if (const_src) {
4795 Legal = true;
4796 }
4797 }
4798 if (!Legal) {
4799 end = parsed[idx].second;
4800 break;
4801 }
4802 } else
4803 dt = tmp;
4804 }
4805 assert(dt.isKnown());
4806 toIterate.emplace_back(dt.isFloat(), start, end - start);
4807 }
4808 return toIterate;
4809}
4810
4811void dumpModule(llvm::Module *mod) { llvm::errs() << *mod << "\n"; }
4812
4813void dumpValue(llvm::Value *val) { llvm::errs() << *val << "\n"; }
4814
4815void dumpBlock(llvm::BasicBlock *blk) { llvm::errs() << *blk << "\n"; }
4816
4817void dumpType(llvm::Type *ty) { llvm::errs() << *ty << "\n"; }
4818
4820
4821bool isNVLoad(const llvm::Value *V) {
4822 auto II = dyn_cast<IntrinsicInst>(V);
4823 if (!II)
4824 return false;
4825 switch (II->getIntrinsicID()) {
4826 case Intrinsic::nvvm_ldu_global_i:
4827 case Intrinsic::nvvm_ldu_global_p:
4828 case Intrinsic::nvvm_ldu_global_f:
4829#if LLVM_VERSION_MAJOR < 20
4830 case Intrinsic::nvvm_ldg_global_i:
4831 case Intrinsic::nvvm_ldg_global_p:
4832 case Intrinsic::nvvm_ldg_global_f:
4833#endif
4834 return true;
4835 default:
4836 return false;
4837 }
4838 return false;
4839}
4840
4841bool notCapturedBefore(llvm::Value *V, Instruction *inst,
4842 size_t checkLoadCaptures, Instruction *startinst,
4843 llvm::TargetLibraryInfo *TLI) {
4844 // The point after which uses count; only needed to bound the search by inst.
4845 Instruction *VI = startinst;
4846 if (!VI)
4847 VI = dyn_cast<Instruction>(V);
4848 if (VI)
4849 VI = VI->getNextNode();
4850 else if (inst)
4851 VI = &*inst->getParent()->getParent()->getEntryBlock().begin();
4852 SmallPtrSet<BasicBlock *, 1> regionBetween;
4853 if (inst) {
4854 SmallVector<BasicBlock *, 1> todo;
4855 todo.push_back(VI->getParent());
4856 while (todo.size()) {
4857 auto cur = todo.pop_back_val();
4858 if (regionBetween.count(cur))
4859 continue;
4860 regionBetween.insert(cur);
4861 if (cur == inst->getParent())
4862 continue;
4863 for (auto BB : successors(cur))
4864 todo.push_back(BB);
4865 }
4866 }
4867 SmallVector<std::tuple<Instruction *, size_t, Value *>, 1> todo;
4868 for (auto U : V->users()) {
4869 if (auto I = dyn_cast<Instruction>(U)) {
4870 todo.emplace_back(cast<Instruction>(I), checkLoadCaptures, V);
4871 }
4872 }
4873 std::set<std::tuple<Value *, size_t, Value *>> seen;
4874 while (todo.size()) {
4875 auto pair = todo.pop_back_val();
4876 if (seen.count(pair))
4877 continue;
4878 seen.insert(pair);
4879 auto UI = std::get<0>(pair);
4880 auto level = std::get<1>(pair);
4881 auto prev = std::get<2>(pair);
4882 if (inst) {
4883 if (!regionBetween.count(UI->getParent()))
4884 continue;
4885 if (UI->getParent() == VI->getParent()) {
4886 if (UI->comesBefore(VI))
4887 continue;
4888 }
4889 if (UI->getParent() == inst->getParent())
4890 if (inst->comesBefore(UI))
4891 continue;
4892 }
4893
4894 if (isPointerArithmeticInst(UI, /*includephi*/ true,
4895 /*includebin*/ true)) {
4896 for (auto U2 : UI->users()) {
4897 auto UI2 = cast<Instruction>(U2);
4898 todo.emplace_back(UI2, level, UI);
4899 }
4900 continue;
4901 }
4902
4903 if (isa<MemSetInst>(UI))
4904 continue;
4905
4906 if (isa<MemTransferInst>(UI)) {
4907 if (level == 0)
4908 continue;
4909 if (UI->getOperand(1) != prev)
4910 continue;
4911 }
4912
4913 if (auto CI = dyn_cast<CallBase>(UI)) {
4914 // Freeing the memory does not capture it.
4915 if (TLI && level == 0 && isDeallocationCall(CI, *TLI))
4916 continue;
4917#if LLVM_VERSION_MAJOR >= 14
4918 for (size_t i = 0, size = CI->arg_size(); i < size; i++)
4919#else
4920 for (size_t i = 0, size = CI->getNumArgOperands(); i < size; i++)
4921#endif
4922 {
4923 if (prev == CI->getArgOperand(i)) {
4924 if (isNoCapture(CI, i) && level == 0)
4925 continue;
4926 return false;
4927 }
4928 }
4929 // This call does not capture; keep checking the remaining users.
4930 continue;
4931 }
4932
4933 if (isa<CmpInst>(UI)) {
4934 continue;
4935 }
4936 if (isa<LoadInst>(UI)) {
4937 if (level) {
4938 for (auto U2 : UI->users()) {
4939 auto UI2 = cast<Instruction>(U2);
4940 todo.emplace_back(UI2, level - 1, UI);
4941 }
4942 }
4943 continue;
4944 }
4945 // storing into it.
4946 if (auto SI = dyn_cast<StoreInst>(UI)) {
4947 if (SI->getValueOperand() != prev) {
4948 continue;
4949 }
4950 }
4951 return false;
4952 }
4953 return true;
4954}
4955
4956bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI) {
4957 return notCapturedBefore(V, nullptr, 0, nullptr, TLI);
4958}
4959
4960// Return true if guaranteed not to alias
4961// Return false if guaranteed to alias [with possible offset depending on flag].
4962// Return {} if no information is given.
4963#if LLVM_VERSION_MAJOR >= 16
4964std::optional<bool>
4965#else
4966llvm::Optional<bool>
4967#endif
4968arePointersGuaranteedNoAlias(TargetLibraryInfo &TLI, llvm::AAResults &AA,
4969 llvm::DominatorTree &DT, llvm::LoopInfo &LI,
4970 llvm::Value *op0, llvm::Value *op1,
4971 bool offsetAllowed) {
4972 auto lhs = getBaseObject(op0, offsetAllowed);
4973 auto rhs = getBaseObject(op1, offsetAllowed);
4974
4975 if (lhs == rhs) {
4976 return false;
4977 }
4978 if (auto i1 = dyn_cast<Instruction>(op1))
4979 if (isa<ConstantPointerNull>(op0) &&
4980 hasMetadata(i1, LLVMContext::MD_nonnull)) {
4981 return true;
4982 }
4983 if (auto i0 = dyn_cast<Instruction>(op0))
4984 if (isa<ConstantPointerNull>(op1) &&
4985 hasMetadata(i0, LLVMContext::MD_nonnull)) {
4986 return true;
4987 }
4988
4989 if (!lhs->getType()->isPointerTy() && !rhs->getType()->isPointerTy())
4990 return {};
4991
4992 bool noalias_lhs = isNoAlias(lhs);
4993 bool noalias_rhs = isNoAlias(rhs);
4994
4995 bool noalias[2] = {noalias_lhs, noalias_rhs};
4996
4997 for (int i = 0; i < 2; i++) {
4998 int start_i = i;
4999 Value *start = (i == 0) ? lhs : rhs;
5000 int end_i = 1 - i;
5001 Value *end = (i == 0) ? rhs : lhs;
5002
5003 if (noalias[start_i]) {
5004 if (noalias[end_i]) {
5005 return true;
5006 }
5007 if (isa<Argument>(end)) {
5008 return true;
5009 }
5010
5011 if (auto starti = dyn_cast<Instruction>(start)) {
5012 // If end dominates start, then since start is noalias at its creation,
5013 // it is definitionally not aliasing end
5014 if (DT.dominates(end, starti)) {
5015 return true;
5016 }
5017 }
5018
5019 if (auto endi = dyn_cast<Instruction>(end)) {
5020 if (isAllocationCall(start, TLI)) {
5021 if (auto ld = dyn_cast<LoadInst>(endi)) {
5022 if (getBaseObject(ld->getOperand(0), true) == start)
5023 continue;
5024 }
5025 }
5026 if (notCapturedBefore(start, endi, 0)) {
5027 return true;
5028 }
5029 }
5030 }
5031
5032 if (auto ld = dyn_cast<LoadInst>(start)) {
5033 auto base = getBaseObject(ld->getOperand(0), /*offsetAllowed*/ true);
5034 auto end_base = getBaseObject(end, /*offsetAllowed*/ true);
5035 if (isAllocationCall(base, TLI) || isa<AllocaInst>(base)) {
5036 auto alloc_call = cast<Instruction>(base);
5037 // Even if the alloc was written into:
5038 // if either:
5039 // end didn't alias any other value at time of construction
5040 // AND
5041 // end was not captured prior to the load,
5042 // it cannot possibly alias.
5043 //
5044 // In this case, end is a fresh (aka non
5045 // aliasing) pointer, which means no other pointers in scope could
5046 // point to, none of which were captured.
5047 //
5048 // It is not sufficient here to merely prove end dominates alloc_call
5049 // and is not captured, since there could be an aliasing pointer to end
5050 // which is captured.
5051 if (end_base != alloc_call && noalias[end_i] &&
5052 notCapturedBefore(end_base, ld, 0, alloc_call)) {
5053 return true;
5054 }
5055
5056 // However if nothing was written to the alloc prior to the load, we
5057 // know that there is no way to dataflow end into start, so we now
5058 // merely must prove there is no way to dataflow start into end.
5059
5060 bool alloc_written = false;
5061 allInstructionsBetween(LI, alloc_call, ld, [&](Instruction *I) -> bool {
5062 if (!I->mayWriteToMemory())
5063 return /*earlyBreak*/ false;
5064
5065 if (writesToMemoryReadBy(nullptr, AA, TLI,
5066 /*maybeReader*/ ld,
5067 /*maybeWriter*/ I)) {
5068 alloc_written = true;
5069 return /*earlyBreak*/ true;
5070 }
5071 return /*earlyBreak*/ false;
5072 });
5073
5074 if (!alloc_written && end_base != alloc_call) {
5075 // If end is marked noalias at the time of construction it
5076 // definitionally cannot alias another potential load out of alloc. If
5077 // the base of end occurs prior to alloc_call (and is distinct from
5078 // alloc_call), there is no way for alloc_call to dataflow into end.
5079 if (noalias[end_i] || DT.dominates(end_base, alloc_call)) {
5080 return true;
5081 }
5082
5083 // If the allocation was not written into prior to ld, any pointer
5084 // value loaded from it must have been created by one of the loads
5085 // reading from alloc_call. If every load out of alloc_call is
5086 // distinct from end, and was neither captured before end nor created
5087 // after end, alloc_call's loaded values cannot dataflow into end.
5088 SmallVector<Value *, 8> worklist;
5089 SmallPtrSet<Value *, 8> visited;
5090 SmallVector<LoadInst *, 8> alloc_loads;
5091
5092 worklist.push_back(alloc_call);
5093 visited.insert(alloc_call);
5094
5095 while (!worklist.empty()) {
5096 Value *V = worklist.pop_back_val();
5097 for (User *U : V->users()) {
5098 if (!visited.insert(U).second)
5099 continue;
5100 if (isPointerArithmeticInst(U, /*includephi*/ true,
5101 /*includebin*/ true)) {
5102 worklist.push_back(U);
5103 } else if (auto LI = dyn_cast<LoadInst>(U)) {
5104 alloc_loads.push_back(LI);
5105 }
5106 }
5107 }
5108
5109 bool all_loads_no_alias = true;
5110 for (LoadInst *alloc_load : alloc_loads) {
5111 if (alloc_load == end) {
5112 all_loads_no_alias = false;
5113 break;
5114 }
5115 if (auto end_inst = dyn_cast<Instruction>(end)) {
5116 if (DT.dominates(end_inst, alloc_load)) {
5117 continue;
5118 }
5119 }
5120 if (!notCapturedBefore(alloc_load, dyn_cast<Instruction>(end), 0)) {
5121 all_loads_no_alias = false;
5122 break;
5123 }
5124 }
5125
5126 if (all_loads_no_alias) {
5127 return true;
5128 }
5129 }
5130 }
5131 }
5132 }
5133
5134 return {};
5135}
5136
5137static Value *constantInBoundsGEPHelper(llvm::IRBuilder<> &B, llvm::Type *type,
5138 llvm::Value *value,
5139 ArrayRef<unsigned> path) {
5140 SmallVector<Value *, 2> vals;
5141 vals.push_back(ConstantInt::get(B.getInt64Ty(), 0));
5142 for (auto v : path) {
5143 vals.push_back(ConstantInt::get(B.getInt32Ty(), v));
5144 }
5145 return B.CreateInBoundsGEP(type, value, vals);
5146}
5147
5148llvm::Value *moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype,
5149 llvm::Value *sret, llvm::Type *root_ty,
5150 llvm::Value *rootRet, size_t rootOffset,
5151 SRetRootMovement direction) {
5152 std::deque<std::pair<llvm::Type *, std::vector<unsigned>>> todo = {
5153 {jltype, {}}};
5154 SmallVector<Value *> extracted;
5155 Value *val = sret;
5156 auto rootOffset0 = rootOffset;
5157 while (!todo.empty()) {
5158 auto cur = std::move(todo[0]);
5159 todo.pop_front();
5160 auto path = std::move(cur.second);
5161 auto ty = cur.first;
5162
5163 if (auto PT = dyn_cast<PointerType>(ty)) {
5164 if (!isSpecialPtr(PT))
5165 continue;
5166
5167 Value *loc = nullptr;
5168 switch (direction) {
5173 loc = constantInBoundsGEPHelper(B, root_ty, rootRet, rootOffset);
5174 break;
5175 default:
5176 llvm_unreachable("Unhandled");
5177 }
5178 switch (direction) {
5180 Value *outloc = constantInBoundsGEPHelper(B, jltype, sret, path);
5181 outloc = B.CreateLoad(ty, outloc);
5182 B.CreateStore(outloc, loc);
5183 break;
5184 }
5186 Value *outloc = GradientUtils::extractMeta(B, sret, path);
5187 outloc = B.CreatePointerCast(
5188 outloc,
5189 getPointerType(StructType::get(outloc->getContext(), {}), Tracked));
5190 B.CreateStore(outloc, loc);
5191 break;
5192 }
5194 loc = B.CreateLoad(ty, loc);
5195 val = B.CreateInsertValue(val, loc, path);
5196 break;
5197 }
5200 *B.GetInsertBlock()->getParent()->getParent(), ty, false);
5201 val = B.CreateInsertValue(val, loc, path);
5202 break;
5203 }
5205 Value *outloc = constantInBoundsGEPHelper(B, jltype, sret, path);
5206 loc = B.CreateLoad(ty, loc);
5207 extracted.push_back(loc);
5208 B.CreateStore(loc, outloc);
5209 break;
5210 }
5211 default:
5212 llvm_unreachable("Unhandled");
5213 break;
5214 }
5215
5216 rootOffset += 1;
5217 continue;
5218 }
5219
5220 if (auto AT = dyn_cast<ArrayType>(ty)) {
5221 for (size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5222 std::vector<unsigned> path2(path);
5223 path2.push_back(E - 1 - i);
5224 todo.emplace_front(AT->getElementType(), path2);
5225 }
5226 continue;
5227 }
5228
5229 if (auto VT = dyn_cast<VectorType>(ty)) {
5230 for (size_t i = 0, E = VT->getElementCount().getKnownMinValue(); i < E;
5231 i++) {
5232 std::vector<unsigned> path2(path);
5233 path2.push_back(E - 1 - i);
5234 todo.emplace_front(VT->getElementType(), path2);
5235 }
5236 continue;
5237 }
5238
5239 if (auto ST = dyn_cast<StructType>(ty)) {
5240 for (size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5241 std::vector<unsigned> path2(path);
5242 path2.push_back(E - 1 - i);
5243 todo.emplace_front(ST->getTypeAtIndex(E - 1 - i), path2);
5244 }
5245 continue;
5246 }
5247 }
5248
5250 auto obj = getBaseObject(sret);
5251 auto PT = cast<PointerType>(obj->getType());
5252 assert(PT->getAddressSpace() == 0 || PT->getAddressSpace() == 10);
5253 if (PT->getAddressSpace() == 10 && extracted.size()) {
5254 extracted.insert(extracted.begin(), obj);
5255 auto JLT = getPointerType(StructType::get(PT->getContext(), {}), 10);
5256 auto FT = FunctionType::get(JLT, {}, true);
5257 auto wb =
5258 B.GetInsertBlock()->getParent()->getParent()->getOrInsertFunction(
5259 "julia.write_barrier", FT);
5260 assert(obj->getType() == JLT);
5261 B.CreateCall(wb, extracted);
5262 }
5263 }
5264
5265 CountTrackedPointers tracked(jltype);
5266 assert(rootOffset - rootOffset0 == tracked.count);
5267
5268 return val;
5269}
5270
5271void copyNonJLValueInto(llvm::IRBuilder<> &B, llvm::Type *curType,
5272 llvm::Type *dstType, llvm::Value *dst,
5273 llvm::ArrayRef<unsigned> dstPrefix0,
5274 llvm::Type *srcType, llvm::Value *src,
5275 llvm::ArrayRef<unsigned> srcPrefix0, bool shouldZero) {
5276 std::deque<
5277 std::tuple<llvm::Type *, std::vector<unsigned>, std::vector<unsigned>>>
5278 todo = {{curType,
5279 std::vector<unsigned>(dstPrefix0.begin(), dstPrefix0.end()),
5280 std::vector<unsigned>(srcPrefix0.begin(), srcPrefix0.end())}};
5281
5282 auto &M = *B.GetInsertBlock()->getParent()->getParent();
5283
5284 size_t numRootsSeen = 0;
5285
5286 while (!todo.empty()) {
5287 auto cur = std::move(todo[0]);
5288 auto &&[ty, dstPrefix, srcPrefix] = cur;
5289 todo.pop_front();
5290
5291 if (auto PT = dyn_cast<PointerType>(ty)) {
5292 if (PT->getAddressSpace() == 10) {
5293 numRootsSeen++;
5294 if (shouldZero) {
5295 Value *out = dst;
5296 if (dstPrefix.size() > 0)
5297 out = constantInBoundsGEPHelper(B, dstType, out, dstPrefix);
5298 B.CreateStore(getUndefinedValueForType(M, ty), out);
5299 }
5300 }
5301 // We don't actually need pointers either here
5302 continue;
5303 }
5304
5305 if (auto AT = dyn_cast<ArrayType>(ty)) {
5306 for (size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5307 std::vector<unsigned> nextDst(dstPrefix);
5308 std::vector<unsigned> nextSrc(srcPrefix);
5309 nextDst.push_back(E - 1 - i);
5310 nextSrc.push_back(E - 1 - i);
5311 todo.emplace_front(AT->getElementType(), std::move(nextDst),
5312 std::move(nextSrc));
5313 }
5314 continue;
5315 }
5316
5317 if (auto ST = dyn_cast<StructType>(ty)) {
5318 for (size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5319 std::vector<unsigned> nextDst(dstPrefix);
5320 std::vector<unsigned> nextSrc(srcPrefix);
5321 nextDst.push_back(E - 1 - i);
5322 nextSrc.push_back(E - 1 - i);
5323 todo.emplace_front(ST->getElementType(E - 1 - i), std::move(nextDst),
5324 std::move(nextSrc));
5325 }
5326 continue;
5327 }
5328
5329 Value *out = dst;
5330 if (dstPrefix.size() > 0)
5331 out = constantInBoundsGEPHelper(B, dstType, out, dstPrefix);
5332
5333 Value *in = src;
5334 if (srcPrefix.size() > 0)
5335 in = constantInBoundsGEPHelper(B, srcType, in, srcPrefix);
5336
5337 auto ld = B.CreateLoad(ty, in);
5338 B.CreateStore(ld, out);
5339 }
5340
5341 CountTrackedPointers tracked(curType);
5342 assert(numRootsSeen == tracked.count);
5343 (void)tracked;
5344 (void)numRootsSeen;
5345}
5346
5347llvm::SmallVector<llvm::Value *, 1> getJuliaObjects(llvm::Value *v,
5348 llvm::IRBuilder<> &B) {
5349 std::deque<Value *> todo = {v};
5350 SmallVector<Value *, 1> done;
5351 while (todo.size()) {
5352 auto cur = todo.front();
5353 todo.pop_front();
5354 auto T = cur->getType();
5355 if (!anyJuliaObjects(T)) {
5356 continue;
5357 }
5358 if (isSpecialPtr(T)) {
5359 done.push_back(cur);
5360 continue;
5361 }
5362 if (auto ST = dyn_cast<StructType>(T)) {
5363 for (size_t i = 0, E = ST->getNumElements(); i < E; i++) {
5364 auto T2 = ST->getElementType(E - 1 - i);
5365 if (anyJuliaObjects(T2)) {
5366 auto V2 = B.CreateExtractValue(cur, E - 1 - i);
5367 todo.push_front(V2);
5368 }
5369 }
5370 continue;
5371 }
5372 if (auto AT = dyn_cast<ArrayType>(T)) {
5373 for (size_t i = 0, E = AT->getNumElements(); i < E; i++) {
5374 todo.push_front(B.CreateExtractValue(cur, E - 1 - i));
5375 }
5376 continue;
5377 }
5378 if (auto VT = dyn_cast<VectorType>(T)) {
5379 assert(!VT->getElementCount().isScalable());
5380 size_t numElems = VT->getElementCount().getKnownMinValue();
5381 for (size_t i = 0; i < numElems; i++) {
5382 todo.push_front(B.CreateExtractElement(cur, numElems - 1 - i));
5383 }
5384 continue;
5385 }
5386 llvm_unreachable("unknown source of julia type");
5387 }
5388 return done;
5389}
static bool contains(ArrayRef< int > ar, int v)
static bool isDeallocationFunction(const llvm::StringRef name, const llvm::TargetLibraryInfo &TLI)
Return whether a given function is a known C/C++ memory deallocation function For updating below one ...
static bool isDeallocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static bool isAllocationFunction(const llvm::StringRef name, const llvm::TargetLibraryInfo &TLI)
Return whether a given function is a known C/C++ memory allocation function For updating below one sh...
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static Operation * getFunctionFromCall(CallOpInterface iface)
static std::string str(AugmentedStruct c)
Definition EnzymeLogic.h:62
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...
Definition TBAA.h:439
static bool isMemFreeLibMFunction(llvm::StringRef str, llvm::Intrinsic::ID *ID=nullptr)
llvm::Value * get_cached_mat_width(llvm::IRBuilder<> &B, llvm::ArrayRef< llvm::Value * > trans, llvm::Value *arg_ld, llvm::Value *dim1, llvm::Value *dim2, bool cacheMat, bool byRef, bool cublas)
Definition Utils.cpp:4313
void addValueToCache(llvm::Value *arg, bool cache_arg, llvm::Type *ty, llvm::SmallVectorImpl< llvm::Value * > &cacheValues, llvm::IRBuilder<> &BuilderZ, const Twine &name)
Definition Utils.cpp:4033
llvm::Value * load_if_ref(llvm::IRBuilder<> &B, llvm::Type *intType, llvm::Value *V, bool byRef)
Definition Utils.cpp:4370
CallInst * CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree)
Definition Utils.cpp:871
llvm::Value * CreateReAllocation(llvm::IRBuilder<> &B, llvm::Value *prev, llvm::Type *T, llvm::Value *OuterCount, llvm::Value *InnerCount, const llvm::Twine &Name, llvm::CallInst **caller, bool ZeroMem)
Definition Utils.cpp:685
AllocaInst * getBaseAndOffset(Value *ptr, size_t &offset)
Definition Utils.cpp:3445
bool isFlangRuntimeNoFree(llvm::StringRef name)
LLVM flang runtime functions that neither free memory of the program nor return or store an allocatio...
Definition Utils.cpp:115
Value * lookup_with_layout(IRBuilder<> &B, Type *fpType, Value *layout, Value *const base, Value *lda, Value *row, Value *col)
Definition Utils.cpp:1333
llvm::cl::opt< bool > EnzymeZeroCache
llvm::Value * to_blas_fp_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef, Type *fpTy, IRBuilder<> &entryBuilder, llvm::Twine const &name)
Definition Utils.cpp:4079
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
Definition Utils.cpp:508
llvm::SmallVector< llvm::Instruction *, 2 > PostCacheStore(llvm::StoreInst *SI, llvm::IRBuilder<> &B)
Definition Utils.cpp:517
Function * getFirstFunctionDefinition(Module &M)
Definition Utils.cpp:3815
llvm::cl::opt< bool > EnzymeFastMath("enzyme-fast-math", cl::init(true), cl::Hidden, cl::desc("Use fast math on derivative compuation"))
llvm::Function * getOrInsertDifferentialMPI_Wait(llvm::Module &M, ArrayRef< llvm::Type * > T, Type *reqType, StringRef caller)
Definition Utils.cpp:2494
llvm::PointerType * getDefaultAnonymousTapeType(llvm::LLVMContext &C)
Definition Utils.cpp:531
static bool matchBLASName(StringRef in, ArrayRef< const char * > prefixes, ArrayRef< const char * > floatTypes, ArrayRef< const char * > stems, ArrayRef< const char * > suffixes, const char *&prefixOut, const char *&floatTypeOut, const char *&stemOut, const char *&suffixOut)
Decompose in as prefix + floatType + stem + suffix using the supplied tables, setting the out-paramet...
Definition Utils.cpp:3835
llvm::Function * getOrInsertDifferentialWaitallSave(llvm::Module &M, ArrayRef< llvm::Type * > T, PointerType *reqType)
Definition Utils.cpp:2426
Value * simplifyLoad(Value *V, size_t valSz, size_t preOffset)
Definition Utils.cpp:3664
bool attributeKnownFunctions(llvm::Function &F)
Definition Utils.cpp:139
llvm::CallInst * createIntrinsicCall(llvm::IRBuilderBase &B, llvm::Intrinsic::ID ID, llvm::Type *RetTy, llvm::ArrayRef< llvm::Value * > Args, llvm::Instruction *FMFSource, const llvm::Twine &Name)
Definition Utils.cpp:4528
llvm::Value * getOrInsertOpFloatSum(llvm::Module &M, llvm::Function *templateFn, llvm::Type *OpPtr, llvm::Type *OpType, ConcreteType CT, llvm::Type *intType, IRBuilder<> &B2)
Definition Utils.cpp:2601
void mayExecuteAfter(llvm::SmallVectorImpl< llvm::Instruction * > &results, llvm::Instruction *inst, const llvm::SetVector< Instruction * > &stores, const llvm::Loop *region)
Definition Utils.cpp:2740
llvm::Value * get1ULP(llvm::IRBuilder<> &builder, llvm::Value *res)
Definition Utils.cpp:4570
void callMemcpyStridedLapack(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::ArrayRef< llvm::Value * > args, llvm::ArrayRef< llvm::OperandBundleDef > bundles)
Create function for type that performs memcpy using lapack copy.
Definition Utils.cpp:1546
LLVMValueRef(* EnzymeUndefinedValueForType)(LLVMModuleRef, LLVMTypeRef, uint8_t)
Definition Utils.cpp:77
bool notCapturedBefore(llvm::Value *V, Instruction *inst, size_t checkLoadCaptures, Instruction *startinst, llvm::TargetLibraryInfo *TLI)
Definition Utils.cpp:4841
llvm::Value * nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V)
Create function to computer nearest power of two.
Definition Utils.cpp:2415
Function * getOrInsertMemcpyMat(Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT, unsigned dstalign, unsigned srcalign)
Definition Utils.cpp:2003
llvm::Value * transpose(std::string floatType, IRBuilder<> &B, llvm::Value *V, bool cublas)
Definition Utils.cpp:4242
void EmitNoTypeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2)
Definition Utils.cpp:4675
Value * is_lower(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas)
Definition Utils.cpp:4095
LLVMValueRef(* CustomDeallocator)(LLVMBuilderRef, LLVMValueRef)
Definition Utils.cpp:71
llvm::cl::opt< bool > EnzymeCheckDerivativeNaN("enzyme-check-nan", cl::init(false), cl::Hidden, cl::desc("Add NaN checks to all derivative intermediate values"))
llvm::Optional< BlasInfo > extractBLAS(llvm::StringRef in)
Definition Utils.cpp:3871
Value * CreateAllocation(IRBuilder<> &Builder, llvm::Type *T, Value *Count, const Twine &Name, CallInst **caller, Instruction **ZeroMem, bool isDefault)
Definition Utils.cpp:714
FunctionCallee getOrInsertPerCallingConv(Module &M, Function *templateFn, StringRef callee, FunctionType *FT)
Definition Utils.cpp:2294
LLVMValueRef(* CustomAllocator)(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef, LLVMValueRef, uint8_t, LLVMValueRef *)
Definition Utils.cpp:65
void dumpBlock(llvm::BasicBlock *blk)
Definition Utils.cpp:4815
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Definition Utils.cpp:62
Function * getOrInsertExponentialAllocator(Module &M, Function *newFunc, bool ZeroInit, llvm::Type *RT)
Definition Utils.cpp:537
Value * GetFunctionValFromValue(Value *fn)
Definition Utils.cpp:3730
bool overwritesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter, llvm::Loop *scope)
Definition Utils.cpp:3040
bool isNVLoad(const llvm::Value *V)
Definition Utils.cpp:4821
llvm::Value * is_left(IRBuilder<> &B, llvm::Value *side, bool byRef, bool cublas)
Definition Utils.cpp:4199
llvm::Value * EmitNoDerivativeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2, llvm::Value *condition)
Definition Utils.cpp:4593
Constant * getString(Module &M, StringRef Str)
Definition Utils.cpp:954
llvm::cl::opt< bool > EnzymeMemmoveWarning("enzyme-memmove-warning", cl::init(true), cl::Hidden, cl::desc("Warn if using memmove implementation as a fallback for memmove"))
void dumpTypeResults(TypeResults &TR)
Definition Utils.cpp:4819
bool writesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter)
Return whether maybeReader can read from memory written to by maybeWriter.
Definition Utils.cpp:3155
Function * getOrInsertDifferentialFloatMemcpyMat(Module &Mod, Type *elementType, PointerType *PT, IntegerType *IT, IntegerType *CT, unsigned dstalign, unsigned srcalign, bool zeroSrc)
Definition Utils.cpp:2123
SmallVector< std::pair< Value *, size_t >, 1 > getAllLoadedValuesFrom(AllocaInst *ptr0, size_t offset, size_t valSz, bool &legal)
Definition Utils.cpp:3533
void dumpType(llvm::Type *ty)
Definition Utils.cpp:4817
void callMemcpyStridedBlas(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, llvm::ArrayRef< llvm::Value * > args, llvm::Type *copy_retty, llvm::ArrayRef< llvm::OperandBundleDef > bundles)
Create function for type that performs memcpy with a stride using blas copy.
Definition Utils.cpp:1526
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,...
Definition Utils.cpp:4713
void(* CustomRuntimeInactiveError)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef)
Definition Utils.cpp:72
LLVMValueRef *(* EnzymePostCacheStore)(LLVMValueRef, LLVMBuilderRef, uint64_t *size)
Definition Utils.cpp:74
llvm::Value * moveSRetToFromRoots(llvm::IRBuilder<> &B, llvm::Type *jltype, llvm::Value *sret, llvm::Type *root_ty, llvm::Value *rootRet, size_t rootOffset, SRetRootMovement direction)
Definition Utils.cpp:5148
void copy_lower_to_upper(llvm::IRBuilder<> &B, llvm::Type *fpType, BlasInfo blas, bool byRef, llvm::Value *layout, llvm::Value *islower, llvm::Value *A, llvm::Value *lda, llvm::Value *N)
Definition Utils.cpp:1382
void ErrorIfRuntimeInactive(llvm::IRBuilder<> &B, llvm::Value *primal, llvm::Value *shadow, const char *Message, llvm::DebugLoc &&loc, llvm::Instruction *orig)
Definition Utils.cpp:1031
SmallVector< llvm::Value *, 1 > get_blas_row(llvm::IRBuilder<> &B, ArrayRef< llvm::Value * > transA, bool byRef, bool cublas)
Definition Utils.cpp:4384
bool isAtomic(Value *origptr, bool AtomicAdd, Function *newFunc)
Definition Utils.cpp:1136
Value * is_nonunit(IRBuilder<> &B, Value *uplo, bool byRef, bool cublas)
Definition Utils.cpp:4129
Function * GetFunctionFromValue(Value *fn)
Definition Utils.cpp:3811
llvm::SmallVector< llvm::Value *, 1 > getJuliaObjects(llvm::Value *v, llvm::IRBuilder<> &B)
Definition Utils.cpp:5347
void callSPMVDiagUpdate(IRBuilder<> &B, Module &M, BlasInfo blas, IntegerType *IT, Type *BlasCT, Type *BlasFPT, Type *BlasPT, Type *BlasIT, Type *fpTy, ArrayRef< Value * > args, ArrayRef< OperandBundleDef > bundles, bool byRef, bool julia_decl)
Definition Utils.cpp:1564
bool overwritesToMemoryReadByLoop(llvm::ScalarEvolution &SE, llvm::LoopInfo &LI, llvm::DominatorTree &DT, llvm::Instruction *maybeReader, const llvm::SCEV *LoadStart, const llvm::SCEV *LoadEnd, llvm::Instruction *maybeWriter, const llvm::SCEV *StoreStart, const llvm::SCEV *StoreEnd, llvm::Loop *scope)
Definition Utils.cpp:2849
void dumpValue(llvm::Value *val)
Definition Utils.cpp:4813
LLVMTypeRef(* EnzymeDefaultTapeType)(LLVMContextRef)
Definition Utils.cpp:76
Function * getOrInsertDifferentialFloatMemmove(Module &M, Type *T, unsigned dstalign, unsigned srcalign, unsigned dstaddr, unsigned srcaddr, unsigned bitwidth, bool runtimeActivity, bool atomic)
Definition Utils.cpp:2282
void emit_backtrace(llvm::Instruction *inst, llvm::raw_ostream &ss)
Definition Utils.cpp:964
llvm::Value * to_blas_callconv(IRBuilder<> &B, llvm::Value *V, bool byRef, bool cublas, IntegerType *julia_decl, IRBuilder<> &entryBuilder, llvm::Twine const &name)
Definition Utils.cpp:4062
LLVMValueRef(* EnzymeSanitizeDerivatives)(LLVMValueRef, LLVMValueRef toset, LLVMBuilderRef, LLVMValueRef)
Definition Utils.cpp:80
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...
Definition Utils.cpp:1160
llvm::cl::opt< bool > EnzymeRuntimeError("enzyme-runtime-error", cl::init(false), cl::Hidden, cl::desc("Emit Runtime errors instead of compile time ones"))
llvm::CallInst * getorInsertInnerProd(llvm::IRBuilder<> &B, llvm::Module &M, BlasInfo blas, IntegerType *IT, Type *BlasPT, Type *BlasIT, Type *fpTy, llvm::ArrayRef< llvm::Value * > args, const llvm::ArrayRef< llvm::OperandBundleDef > bundles, bool byRef, bool cublas, bool julia_decl)
Definition Utils.cpp:1752
const SCEV * evaluateAtIterationWithoutExt(const SCEVAddRecExpr *AR, const SCEV *It, ScalarEvolution &SE)
Definition Utils.cpp:2801
SmallVector< std::tuple< Instruction *, Value *, size_t >, 1 > findAllUsersOf(Value *AI)
Definition Utils.cpp:3488
bool notCaptured(llvm::Value *V, llvm::TargetLibraryInfo *TLI)
Check if value if b captured.
Definition Utils.cpp:4956
Function * getOrInsertMemcpyStrided(Module &M, Type *elementType, PointerType *T, Type *IT, unsigned dstalign, unsigned srcalign)
Definition Utils.cpp:1901
static Value * constantInBoundsGEPHelper(llvm::IRBuilder<> &B, llvm::Type *type, llvm::Value *value, ArrayRef< unsigned > path)
Definition Utils.cpp:5137
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
Definition Utils.cpp:4026
void copyNonJLValueInto(llvm::IRBuilder<> &B, llvm::Type *curType, llvm::Type *dstType, llvm::Value *dst, llvm::ArrayRef< unsigned > dstPrefix0, llvm::Type *srcType, llvm::Value *src, llvm::ArrayRef< unsigned > srcPrefix0, bool shouldZero)
Definition Utils.cpp:5271
llvm::Optional< bool > arePointersGuaranteedNoAlias(TargetLibraryInfo &TLI, llvm::AAResults &AA, llvm::DominatorTree &DT, llvm::LoopInfo &LI, llvm::Value *op0, llvm::Value *op1, bool offsetAllowed)
Definition Utils.cpp:4968
static std::string tofltstr(Type *T)
Convert a floating type to a string.
Definition Utils.cpp:925
void dumpModule(llvm::Module *mod)
Definition Utils.cpp:4811
void(* CustomZero)(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef, uint8_t)
Definition Utils.cpp:69
llvm::Constant * getUndefinedValueForType(llvm::Module &M, llvm::Type *T, bool forceZero)
Definition Utils.cpp:3918
Function * getOrInsertCheckedFree(Module &M, CallInst *call, Type *Ty, unsigned width)
Definition Utils.cpp:2304
std::vector< std::tuple< llvm::Type *, size_t, size_t > > parseTrueType(const llvm::MDNode *md, DerivativeMode Mode, bool const_src)
Definition Utils.cpp:4749
llvm::Value * is_normal(IRBuilder<> &B, llvm::Value *trans, bool byRef, bool cublas)
Definition Utils.cpp:4163
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask)
Definition Utils.cpp:3929
bool collectOffset(GEPOperator *gep, const DataLayout &DL, unsigned BitWidth, MapVector< Value *, APInt > &VariableOffsets, APInt &ConstantOffset)
Definition Utils.cpp:4464
static llvm::StringRef getFuncName(llvm::Function *called)
Definition Utils.h:1461
static llvm::StringRef canonicalizeMPIName(llvm::StringRef Name)
Canonicalize the name of an MPI routine across calling conventions: both the C convention ("MPI_Recv"...
Definition Utils.h:2780
static std::string getRenamedPerCallingConv(llvm::StringRef caller, llvm::StringRef callee)
Definition Utils.h:2812
llvm::cl::opt< bool > EnzymeBlasCopy
static llvm::Loop * getAncestor(llvm::Loop *R1, llvm::Loop *R2)
Definition Utils.h:1197
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
static bool anyJuliaObjects(llvm::Type *T)
Definition Utils.h:2953
@ Args
Return is a struct of all args.
static bool isNoAlias(const llvm::CallBase *call)
Definition Utils.h:2127
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
Definition Utils.h:847
static llvm::PointerType * getUnqual(llvm::Type *T)
Definition Utils.h:1329
static bool isCertainPrint(const llvm::StringRef name)
Definition Utils.h:863
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
Definition Utils.h:1324
static uint64_t getAggregateElementOffset(const llvm::DataLayout &DL, llvm::Type *T, llvm::ArrayRef< unsigned > Idxs)
The byte offset of the aggregate element addressed by Idxs within T, that is, what gep T,...
Definition Utils.h:1354
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
Definition Utils.h:2541
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
Definition Utils.h:2109
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
Definition Utils.h:2609
static bool isDebugFunction(llvm::Function *called)
Definition Utils.h:824
static bool isPointerArithmeticInst(const llvm::Value *V, bool includephi=true, bool includebin=true)
Definition Utils.h:1660
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1711
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
Definition Utils.h:483
static bool isReadOnlyOrThrow(const llvm::Function *F)
Definition Utils.h:2004
static bool isSpecialPtr(llvm::Type *Ty)
Definition Utils.h:2664
@ Tracked
Definition Utils.h:2651
static void allInstructionsBetween(llvm::LoopInfo &LI, llvm::Instruction *inst1, llvm::Instruction *inst2, llvm::function_ref< bool(llvm::Instruction *)> f)
Call the function f for all instructions that happen between inst1 and inst2 If the function returns ...
Definition Utils.h:1243
ErrorType
Definition Utils.h:189
static std::tuple< llvm::StringRef, llvm::StringRef, llvm::StringRef > tripleSplitDollar(llvm::StringRef caller)
Definition Utils.h:2803
SRetRootMovement
Definition Utils.h:2934
static llvm::Value * CreateSelect(llvm::IRBuilder<> &Builder2, llvm::Value *cmp, llvm::Value *tval, llvm::Value *fval, const llvm::Twine &Name="")
Definition Utils.h:2283
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
Definition Utils.h:1470
llvm::cl::opt< bool > EnzymeLapackCopy
llvm::cl::opt< bool > EnzymeNonPower2Cache
DerivativeMode
Definition Utils.h:524
Concrete SubType of a given value.
bool isKnown() const
Whether this ConcreteType has information (is not unknown)
std::string str() const
Convert the ConcreteType to a string.
llvm::Type * isFloat() const
Return the floating point type, if this is a float.
EnzymeFailure(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
Definition Utils.cpp:914
EnzymeWarning(const llvm::Twine &Msg, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion)
Definition Utils.cpp:904
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.
TypeResults TR
A holder class representing the results of running TypeAnalysis on a given function.
void dump(llvm::raw_ostream &ss=llvm::errs()) const
Prints all known information.
TypeAnalyzer * analyzer
const TypeTree & query(llvm::Value *val) const
The TypeTree of a particular Value.
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.
Definition TypeTree.h:595
llvm::Type * fpType(llvm::LLVMContext &ctx, bool to_scalar=false) const
Definition Utils.cpp:1110
bool is64
Definition Utils.h:883
std::string suffix
Definition Utils.h:881
std::string prefix
Definition Utils.h:880
std::string floatType
Definition Utils.h:879
llvm::IntegerType * intType(llvm::LLVMContext &ctx) const
Definition Utils.cpp:1129
CountTrackedPointers(llvm::Type *T)
Definition Utils.cpp:4431
llvm::Instruction * req
llvm::IRBuilder * ip