Enzyme main
Loading...
Searching...
No Matches
DiffeGradientUtils.cpp
Go to the documentation of this file.
1//===- DiffeGradientUtils.cpp - Helper class and utilities for AD ---------===//
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 declares two helper classes GradientUtils and subclass
22// DiffeGradientUtils. These classes contain utilities for managing the cache,
23// recomputing statements, and in the case of DiffeGradientUtils, managing
24// adjoint values and shadow pointers.
25//
26//===----------------------------------------------------------------------===//
27
28#include <string>
29
30#include "DiffeGradientUtils.h"
31
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/ADT/SmallVector.h"
35
36#include "llvm/IR/BasicBlock.h"
37#include "llvm/IR/DebugInfoMetadata.h"
38#include "llvm/IR/Dominators.h"
39#include "llvm/IR/IRBuilder.h"
40#include "llvm/IR/Instructions.h"
41#include "llvm/IR/Type.h"
42#include "llvm/IR/Value.h"
43
44#include "llvm/Transforms/Utils/BasicBlockUtils.h"
45
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/ErrorHandling.h"
48
49#include "LibraryFuncs.h"
50#include "Utils.h"
51
52using namespace llvm;
53
54namespace {
55bool elementwiseReadForContext(const Instruction *orig, const Value *origptr) {
56 if (orig) {
57 if (const Function *F = orig->getFunction()) {
58 if (F->hasFnAttribute("enzyme_elementwise_read")) {
59 return true;
60 }
61 }
62 }
63 const Value *base = getBaseObject(origptr);
64 if (auto *arg = dyn_cast<Argument>(base)) {
65 if (const Function *F = arg->getParent()) {
66 return F->getAttributes().hasParamAttr(arg->getArgNo(),
67 "enzyme_elementwise_read");
68 }
69 }
70 return false;
71}
72} // namespace
73
74DiffeGradientUtils::DiffeGradientUtils(
75 EnzymeLogic &Logic, Function *newFunc_, Function *oldFunc_,
76 TargetLibraryInfo &TLI, TypeAnalysis &TA, TypeResults TR,
77 ValueToValueMapTy &invertedPointers_,
78 const SmallPtrSetImpl<Value *> &constantvalues_,
79 const SmallPtrSetImpl<Value *> &returnvals_, DIFFE_TYPE ActiveReturn,
80 bool shadowReturnUsed, ArrayRef<DIFFE_TYPE> constant_values,
81 llvm::ValueMap<const llvm::Value *, AssertingReplacingVH> &origToNew_,
82 DerivativeMode mode, bool runtimeActivity, bool strongZero, unsigned width,
83 bool omp)
84 : GradientUtils(Logic, newFunc_, oldFunc_, TLI, TA, TR, invertedPointers_,
85 constantvalues_, returnvals_, ActiveReturn,
86 shadowReturnUsed, constant_values, origToNew_, mode,
87 runtimeActivity, strongZero, width, omp) {
88 if (oldFunc_->empty())
89 return;
90 assert(reverseBlocks.size() == 0);
91 if (mode == DerivativeMode::ForwardMode ||
94 return;
95 }
96 for (BasicBlock *BB : originalBlocks) {
97 if (BB == inversionAllocs)
98 continue;
99 BasicBlock *RBB =
100 BasicBlock::Create(BB->getContext(), "invert" + BB->getName(), newFunc);
101 reverseBlocks[BB].push_back(RBB);
102 reverseBlockToPrimal[RBB] = BB;
103 }
104 assert(reverseBlocks.size() != 0);
105}
106
108 EnzymeLogic &Logic, DerivativeMode mode, bool runtimeActivity,
109 bool strongZero, unsigned width, Function *todiff, TargetLibraryInfo &TLI,
110 TypeAnalysis &TA, FnTypeInfo &oldTypeInfo, DIFFE_TYPE retType,
111 bool shadowReturn, bool diffeReturnArg, ArrayRef<DIFFE_TYPE> constant_args,
112 bool returnTape, bool returnPrimal, Type *additionalArg, bool omp) {
113 Function *oldFunc = todiff;
119 ValueToValueMapTy invertedPointers;
120 SmallPtrSet<Instruction *, 4> constants;
121 SmallPtrSet<Instruction *, 20> nonconstant;
122 SmallPtrSet<Value *, 2> returnvals;
123 llvm::ValueMap<const llvm::Value *, AssertingReplacingVH> originalToNew;
124
125 SmallPtrSet<Value *, 4> constant_values;
126 SmallPtrSet<Value *, 4> nonconstant_values;
127
128 std::string prefix;
129
130 switch (mode) {
134 prefix = "fwddiffe";
135 break;
138 prefix = "diffe";
139 break;
141 llvm_unreachable("invalid DerivativeMode: ReverseModePrimal\n");
142 }
143
144 if (width > 1)
145 prefix += std::to_string(width);
146
148 mode, width, oldFunc, invertedPointers, constant_args, constant_values,
149 nonconstant_values, returnvals, returnTape, returnPrimal,
150 (mode == DerivativeMode::ReverseModeGradient) ? false : shadowReturn,
151 prefix + oldFunc->getName(), &originalToNew,
152 /*diffeReturnArg*/ diffeReturnArg, additionalArg);
153
154 // Convert overwritten args from the input function to the preprocessed
155 // function
156
157 FnTypeInfo typeInfo(oldFunc);
158 {
159 auto toarg = todiff->arg_begin();
160 auto olarg = oldFunc->arg_begin();
161 for (; toarg != todiff->arg_end(); ++toarg, ++olarg) {
162
163 {
164 auto fd = oldTypeInfo.Arguments.find(toarg);
165 assert(fd != oldTypeInfo.Arguments.end());
166 typeInfo.Arguments.insert(
167 std::pair<Argument *, TypeTree>(olarg, fd->second));
168 }
169
170 {
171 auto cfd = oldTypeInfo.KnownValues.find(toarg);
172 assert(cfd != oldTypeInfo.KnownValues.end());
173 typeInfo.KnownValues.insert(
174 std::pair<Argument *, std::set<int64_t>>(olarg, cfd->second));
175 }
176 }
177 typeInfo.Return = oldTypeInfo.Return;
178 }
179
180 TypeResults TR = TA.analyzeFunction(typeInfo);
181 if (!oldFunc->empty())
182 assert(TR.getFunction() == oldFunc);
183
184 auto res = new DiffeGradientUtils(
185 Logic, newFunc, oldFunc, TLI, TA, TR, invertedPointers, constant_values,
186 nonconstant_values, retType, shadowReturn, constant_args, originalToNew,
188
189 return res;
190}
191
192AllocaInst *DiffeGradientUtils::getDifferential(Value *val) {
196 assert(val);
197#ifndef NDEBUG
198 if (auto arg = dyn_cast<Argument>(val))
199 assert(arg->getParent() == oldFunc);
200 if (auto inst = dyn_cast<Instruction>(val))
201 assert(inst->getParent()->getParent() == oldFunc);
202#endif
203 assert(inversionAllocs);
204
205 Type *type = getShadowType(val->getType());
206 if (differentials.find(val) == differentials.end()) {
207 IRBuilder<> entryBuilder(inversionAllocs);
208 entryBuilder.setFastMathFlags(getFast());
209 differentials[val] =
210 entryBuilder.CreateAlloca(type, nullptr, val->getName() + "'de");
211 auto Alignment =
212 oldFunc->getParent()->getDataLayout().getPrefTypeAlign(type);
213 differentials[val]->setAlignment(Alignment);
214 ZeroMemory(entryBuilder, type, differentials[val],
215 /*isTape*/ false);
216 }
217#if LLVM_VERSION_MAJOR < 17
218 if (val->getContext().supportsTypedPointers()) {
219 assert(differentials[val]->getType()->getPointerElementType() == type);
220 }
221#endif
222 return differentials[val];
223}
224
225Value *DiffeGradientUtils::diffe(Value *val, IRBuilder<> &BuilderM) {
226#ifndef NDEBUG
227 if (auto arg = dyn_cast<Argument>(val))
228 assert(arg->getParent() == oldFunc);
229 if (auto inst = dyn_cast<Instruction>(val))
230 assert(inst->getParent()->getParent() == oldFunc);
231#endif
232
233 if (isConstantValue(val)) {
234 llvm::errs() << *newFunc << "\n";
235 llvm::errs() << *val << "\n";
236 assert(0 && "getting diffe of constant value");
237 }
241 return invertPointerM(val, BuilderM);
242 if (val->getType()->isPointerTy()) {
243 llvm::errs() << *newFunc << "\n";
244 llvm::errs() << *val << "\n";
245 }
246 assert(!val->getType()->isPointerTy());
247 assert(!val->getType()->isVoidTy());
248 Type *ty = getShadowType(val->getType());
249 return BuilderM.CreateLoad(ty, getDifferential(val));
250}
251
252SmallVector<SelectInst *, 4> DiffeGradientUtils::addToDiffe(
253 Value *val, Value *dif, IRBuilder<> &BuilderM, Type *addingType,
254 unsigned start, unsigned size, llvm::ArrayRef<llvm::Value *> idxs,
255 llvm::Value *mask, size_t ignoreFirstSlicesOfDif) {
256 assert(addingType);
257 auto &DL = oldFunc->getParent()->getDataLayout();
258 Type *VT = val->getType();
259 for (auto cv : idxs) {
260 auto i = dyn_cast<ConstantInt>(cv)->getSExtValue();
261 if (auto ST = dyn_cast<StructType>(VT)) {
262 VT = ST->getElementType(i);
263 continue;
264 }
265 if (auto AT = dyn_cast<ArrayType>(VT)) {
266 assert((size_t)i < AT->getNumElements());
267 VT = AT->getElementType();
268 continue;
269 }
270 assert(0 && "illegal indexing type");
271 }
272 auto storeSize = (DL.getTypeSizeInBits(VT) + 7) / 8;
273
274 assert(start < storeSize);
275 assert(start + size <= storeSize);
276
277 // If VT is a struct type the addToDiffe algorithm will lose type information
278 // so we do the recurrence here, with full type information.
279 if (start == 0 && size == storeSize && !isa<StructType>(VT)) {
280 if (getWidth() == 1) {
281 SmallVector<unsigned, 1> eidxs;
282 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
283 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
284 }
285 return addToDiffe(val, extractMeta(BuilderM, dif, eidxs), BuilderM,
286 addingType, idxs, mask);
287 } else {
288 SmallVector<SelectInst *, 4> res;
289 for (unsigned j = 0; j < getWidth(); j++) {
290 SmallVector<Value *, 1> lidxs;
291 SmallVector<unsigned, 1> eidxs = {(unsigned)j};
292 lidxs.push_back(
293 ConstantInt::get(Type::getInt32Ty(val->getContext()), j));
294 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
295 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
296 }
297 for (auto idx : idxs) {
298 lidxs.push_back(idx);
299 }
300 for (auto v : addToDiffe(val, extractMeta(BuilderM, dif, eidxs),
301 BuilderM, addingType, lidxs, mask))
302 res.push_back(v);
303 }
304 return res;
305 }
306 }
307 if (auto ST = dyn_cast<StructType>(VT)) {
308 auto SL = DL.getStructLayout(ST);
309 auto left_idx = SL->getElementContainingOffset(start);
310 auto right_idx = ST->getNumElements();
311 if (storeSize != start + size) {
312 right_idx = SL->getElementContainingOffset(start + size);
313 // If this doesn't cleanly end the window, make sure we do a partial
314 // accumulate for the remaining part in right_idx.
315 if (SL->getElementOffset(right_idx) != start + size)
316 right_idx++;
317 }
318 SmallVector<SelectInst *, 4> res;
319 for (auto i = left_idx; i < right_idx; i++) {
320 auto subType = ST->getElementType(i);
321 SmallVector<Value *, 1> lidxs(idxs.begin(), idxs.end());
322 lidxs.push_back(ConstantInt::get(Type::getInt32Ty(val->getContext()), i));
323 auto sub_start =
324 (i == left_idx) ? (start - (unsigned)SL->getElementOffset(i)) : 0;
325 auto subTypeSize = (DL.getTypeSizeInBits(subType) + 7) / 8;
326 auto sub_end = (i == right_idx - 1)
327 ? min(start + size - (unsigned)SL->getElementOffset(i),
328 (unsigned)subTypeSize)
329 : subTypeSize;
330 for (auto v :
331 addToDiffe(val, dif, BuilderM, addingType, sub_start,
332 sub_end - sub_start, lidxs, mask, ignoreFirstSlicesOfDif))
333 res.push_back(v);
334 }
335 return res;
336 }
337
338 if (auto AT = dyn_cast<ArrayType>(VT)) {
339 auto subType = AT->getElementType();
340 auto subTypeSize = (DL.getTypeSizeInBits(subType) + 7) / 8;
341 auto left_idx = start / subTypeSize;
342 auto right_idx = AT->getNumElements();
343 if (storeSize != start + size) {
344 right_idx = (start + size) / subTypeSize;
345 // If this doesn't cleanly end the window, make sure we do a partial
346 // accumulate for the remaining part in right_idx.
347 if (right_idx * subTypeSize != start + size)
348 right_idx++;
349 }
350 SmallVector<SelectInst *, 4> res;
351 for (auto i = left_idx; i < right_idx; i++) {
352 SmallVector<Value *, 1> lidxs(idxs.begin(), idxs.end());
353 lidxs.push_back(ConstantInt::get(Type::getInt32Ty(val->getContext()), i));
354 auto sub_start = (i == left_idx) ? (start - (i * subTypeSize)) : 0;
355 auto sub_end = (i == right_idx - 1)
356 ? min(start + size - (unsigned)(i * subTypeSize),
357 (unsigned)subTypeSize)
358 : subTypeSize;
359 for (auto v :
360 addToDiffe(val, dif, BuilderM, addingType, sub_start,
361 sub_end - sub_start, lidxs, mask, ignoreFirstSlicesOfDif))
362 res.push_back(v);
363 }
364 return res;
365 }
366
367 if (auto VecT = dyn_cast<VectorType>(VT)) {
368 if (!VecT->getElementCount().isScalable()) {
369 Type *elemTy = VecT->getElementType();
370 auto elemBytes = (DL.getTypeSizeInBits(elemTy) + 7) / 8;
371
372 // Only handle element-aligned windows
373 if (elemBytes != 0 && start % elemBytes == 0 && size % elemBytes == 0) {
374 unsigned left_idx = start / elemBytes;
375 unsigned right_idx = (start + size) / elemBytes; // exclusive
376
377 unsigned numElts = VecT->getElementCount().getFixedValue();
378 if (left_idx > numElts)
379 left_idx = numElts;
380 if (right_idx > numElts)
381 right_idx = numElts;
382
383 auto maskVec = [&](Value *dsub) -> Value * {
384 if (left_idx == 0 && right_idx == numElts)
385 return dsub;
386 Value *masked = Constant::getNullValue(VT);
387 for (unsigned i = left_idx; i < right_idx; i++) {
388 Value *vidx =
389 ConstantInt::get(Type::getInt32Ty(val->getContext()), i);
390 Value *el = BuilderM.CreateExtractElement(dsub, vidx);
391 masked = BuilderM.CreateInsertElement(masked, el, vidx);
392 }
393 return masked;
394 };
395
396 if (getWidth() == 1) {
397 SmallVector<unsigned, 1> eidxs;
398 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif))
399 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
400
401 Value *subdif = extractMeta(BuilderM, dif, eidxs);
402 return addToDiffe(val, maskVec(subdif), BuilderM, addingType, idxs,
403 mask);
404 } else {
405 SmallVector<SelectInst *, 4> res;
406 for (unsigned j = 0; j < getWidth(); j++) {
407 SmallVector<Value *, 1> lidxs;
408 SmallVector<unsigned, 1> eidxs = {(unsigned)j};
409
410 lidxs.push_back(
411 ConstantInt::get(Type::getInt32Ty(val->getContext()), j));
412 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif))
413 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
414 for (auto idx : idxs)
415 lidxs.push_back(idx);
416
417 Value *subdif = extractMeta(BuilderM, dif, eidxs);
418 for (auto v : addToDiffe(val, maskVec(subdif), BuilderM, addingType,
419 lidxs, mask))
420 res.push_back(v);
421 }
422 return res;
423 }
424 }
425 }
426 }
427
428 auto addingSize = addingType ? (DL.getTypeSizeInBits(addingType) + 7) / 8 : 0;
429 if (size < storeSize && addingType && !mask && addingSize != 0 &&
430 size % addingSize == 0 && start % addingSize == 0) {
431 if (getWidth() == 1) {
432 SmallVector<unsigned, 1> eidxs;
433 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
434 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
435 }
436 Value *subdif = extractMeta(BuilderM, dif, eidxs);
437 IRBuilder<> A(inversionAllocs);
438 auto Al = A.CreateAlloca(subdif->getType());
439 BuilderM.CreateStore(subdif, Al);
440 if (start > 0) {
441 Value *ptr_i8 = BuilderM.CreatePointerCast(
442 Al, getUnqual(Type::getInt8Ty(val->getContext())));
443 BuilderM.CreateMemSet(ptr_i8, BuilderM.getInt8(0), start,
444 MaybeAlign(1));
445 }
446 if (storeSize > start + size) {
447 Value *ptr_i8 = BuilderM.CreatePointerCast(
448 Al, getUnqual(Type::getInt8Ty(val->getContext())));
449 Value *gep = BuilderM.CreateInBoundsGEP(
450 Type::getInt8Ty(val->getContext()), ptr_i8,
451 ConstantInt::get(Type::getInt64Ty(val->getContext()),
452 start + size));
453 BuilderM.CreateMemSet(gep, BuilderM.getInt8(0),
454 storeSize - start - size, MaybeAlign(1));
455 }
456 Value *new_dif = BuilderM.CreateLoad(subdif->getType(), Al);
457 return addToDiffe(val, new_dif, BuilderM, addingType, 0, storeSize, idxs,
458 mask, idxs.size());
459 } else {
460 SmallVector<unsigned, 1> eidxs;
461 for (auto idx : idxs.slice(ignoreFirstSlicesOfDif)) {
462 eidxs.push_back((unsigned)cast<ConstantInt>(idx)->getZExtValue());
463 }
464 Value *subdif = extractMeta(BuilderM, dif, eidxs);
465 IRBuilder<> A(inversionAllocs);
466 auto Al = A.CreateAlloca(subdif->getType());
467 BuilderM.CreateStore(subdif, Al);
468 if (start > 0) {
469 for (unsigned j = 0; j < getWidth(); j++) {
470 Value *Idxs[] = {
471 ConstantInt::get(Type ::getInt64Ty(val->getContext()), 0),
472 ConstantInt::get(Type::getInt32Ty(val->getContext()), j)};
473 Value *Alj = BuilderM.CreateInBoundsGEP(subdif->getType(), Al, Idxs);
474 Value *ptr_i8 = BuilderM.CreatePointerCast(
475 Alj, getUnqual(Type::getInt8Ty(val->getContext())));
476 BuilderM.CreateMemSet(ptr_i8, BuilderM.getInt8(0), start,
477 MaybeAlign(1));
478 }
479 }
480 if (storeSize > start + size) {
481 for (unsigned j = 0; j < getWidth(); j++) {
482 Value *Idxs[] = {
483 ConstantInt::get(Type ::getInt64Ty(val->getContext()), 0),
484 ConstantInt::get(Type::getInt32Ty(val->getContext()), j)};
485 Value *Alj = BuilderM.CreateInBoundsGEP(subdif->getType(), Al, Idxs);
486 Value *ptr_i8 = BuilderM.CreatePointerCast(
487 Alj, getUnqual(Type::getInt8Ty(val->getContext())));
488 Value *gep = BuilderM.CreateInBoundsGEP(
489 Type::getInt8Ty(val->getContext()), ptr_i8,
490 ConstantInt::get(Type::getInt64Ty(val->getContext()),
491 start + size));
492 BuilderM.CreateMemSet(gep, BuilderM.getInt8(0),
493 storeSize - start - size, MaybeAlign(1));
494 }
495 }
496 Value *new_dif = BuilderM.CreateLoad(subdif->getType(), Al);
497 return addToDiffe(val, new_dif, BuilderM, addingType, 0, storeSize, idxs,
498 mask, idxs.size());
499 }
500 }
501
502 std::string s;
503 llvm::raw_string_ostream ss(s);
504 ss << "Unhandled accumulate with partial sizes:\n";
505 ss << " val: " << *val << "\n";
506 ss << " VT: " << *VT << " idxs:{";
507 for (auto idx : idxs)
508 ss << *idx << ",";
509 ss << "} start=" << start << " size=" << size << " storeSize=" << storeSize
510 << " val=" << *val << "\n";
511 if (addingType)
512 ss << " addingType: " << *addingType << "\n";
513 else
514 ss << " addingType: null\n";
515 if (CustomErrorHandler) {
516 CustomErrorHandler(ss.str().c_str(), wrap(val), ErrorType::NoAccumulate,
517 nullptr, nullptr, wrap(&BuilderM));
518 } else {
519 DebugLoc loc;
520 if (auto inst = dyn_cast<Instruction>(val))
521 EmitFailure("UnhandledAccumulate", inst->getDebugLoc(), inst, ss.str());
522 else {
523 llvm::errs() << ss.str() << "\n";
524 llvm_unreachable("UnhandledAccumulate");
525 }
526 }
527 return {};
528}
529
530static bool isZero(llvm::Constant *cst) {
531#if LLVM_VERSION_MAJOR >= 22
532 return cst->isNullValue() || cst->isNegativeZeroValue();
533#else
534 return cst->isZeroValue();
535#endif
536}
537SmallVector<SelectInst *, 4>
538DiffeGradientUtils::addToDiffe(Value *val, Value *dif, IRBuilder<> &BuilderM,
539 Type *addingType, ArrayRef<Value *> idxs,
540 Value *mask) {
543
544#ifndef NDEBUG
545 if (auto arg = dyn_cast<Argument>(val))
546 assert(arg->getParent() == oldFunc);
547 if (auto inst = dyn_cast<Instruction>(val))
548 assert(inst->getParent()->getParent() == oldFunc);
549#endif
550
551 SmallVector<SelectInst *, 4> addedSelects;
552
553 auto faddForNeg = [&](Value *old, Value *inc, bool san) {
554 if (auto bi = dyn_cast<BinaryOperator>(inc)) {
555 if (auto ci = dyn_cast<ConstantFP>(bi->getOperand(0))) {
556 if (bi->getOpcode() == BinaryOperator::FSub && ci->isZero()) {
557 Value *res = BuilderM.CreateFSub(old, bi->getOperand(1));
558 if (san)
559 res = SanitizeDerivatives(val, res, BuilderM, mask);
560 return res;
561 }
562 }
563 }
564 Value *res = BuilderM.CreateFAdd(old, inc);
565 if (san)
566 res = SanitizeDerivatives(val, res, BuilderM, mask);
567 return res;
568 };
569
570 auto faddForSelect = [&](Value *old, Value *dif) -> Value * {
571 //! optimize fadd of select to select of fadd
572 if (SelectInst *select = dyn_cast<SelectInst>(dif)) {
573 if (Constant *ci = dyn_cast<Constant>(select->getTrueValue())) {
574 if (isZero(ci)) {
575 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
576 select->getCondition(), old,
577 faddForNeg(old, select->getFalseValue(), false)));
578 addedSelects.push_back(res);
579 return SanitizeDerivatives(val, res, BuilderM, mask);
580 }
581 }
582 if (Constant *ci = dyn_cast<Constant>(select->getFalseValue())) {
583 if (isZero(ci)) {
584 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
585 select->getCondition(),
586 faddForNeg(old, select->getTrueValue(), false), old));
587 addedSelects.push_back(res);
588 return SanitizeDerivatives(val, res, BuilderM, mask);
589 }
590 }
591 }
592
593 //! optimize fadd of bitcast select to select of bitcast fadd
594 if (BitCastInst *bc = dyn_cast<BitCastInst>(dif)) {
595 if (SelectInst *select = dyn_cast<SelectInst>(bc->getOperand(0))) {
596 if (Constant *ci = dyn_cast<Constant>(select->getTrueValue())) {
597 if (isZero(ci)) {
598 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
599 select->getCondition(), old,
600 faddForNeg(old,
601 BuilderM.CreateCast(bc->getOpcode(),
602 select->getFalseValue(),
603 bc->getDestTy()),
604 false)));
605 addedSelects.push_back(res);
606 return SanitizeDerivatives(val, res, BuilderM, mask);
607 }
608 }
609 if (Constant *ci = dyn_cast<Constant>(select->getFalseValue())) {
610 if (isZero(ci)) {
611 SelectInst *res = cast<SelectInst>(BuilderM.CreateSelect(
612 select->getCondition(),
613 faddForNeg(old,
614 BuilderM.CreateCast(bc->getOpcode(),
615 select->getTrueValue(),
616 bc->getDestTy()),
617 false),
618 old));
619 addedSelects.push_back(res);
620 return SanitizeDerivatives(val, res, BuilderM, mask);
621 }
622 }
623 }
624 }
625
626 // fallback
627 return faddForNeg(old, dif, true);
628 };
629
630 if (val->getType()->isPointerTy()) {
631 llvm::errs() << *newFunc << "\n";
632 llvm::errs() << *val << "\n";
633 }
634 if (isConstantValue(val)) {
635 llvm::errs() << *newFunc << "\n";
636 llvm::errs() << *val << "\n";
637 }
638 assert(!val->getType()->isPointerTy());
639 assert(!isConstantValue(val));
640
641 Value *ptr = getDifferential(val);
642
643 Value *old;
644 if (idxs.size() != 0) {
645 SmallVector<Value *, 4> sv = {
646 ConstantInt::get(Type::getInt32Ty(val->getContext()), 0)};
647 for (auto i : idxs)
648 sv.push_back(i);
649 ptr = BuilderM.CreateGEP(getShadowType(val->getType()), ptr, sv);
650 cast<GetElementPtrInst>(ptr)->setIsInBounds(true);
651 old = BuilderM.CreateLoad(
652 GetElementPtrInst::getIndexedType(getShadowType(val->getType()), sv),
653 ptr);
654 } else {
655 old = BuilderM.CreateLoad(getShadowType(val->getType()), ptr);
656 }
657 if (dif->getType() != old->getType()) {
658 if (auto inst = dyn_cast<Instruction>(val)) {
659 EmitFailure("IllegalAddingType", inst->getDebugLoc(), inst, "val ", *val,
660 " dif ", *dif, " old ", *old);
661 return addedSelects;
662 }
663 llvm::errs() << " IllegalAddingType val: " << *val << " dif: " << *dif
664 << " old: " << *old << "\n";
665 llvm_unreachable("IllegalAddingType");
666 }
667
668 assert(dif->getType() == old->getType());
669 Value *res = nullptr;
670 if (old->getType()->isIntOrIntVectorTy() || old->getType()->isPointerTy()) {
671 if (!addingType) {
672 if (looseTypeAnalysis) {
673 if (old->getType()->isIntegerTy(64))
674 addingType = Type::getDoubleTy(old->getContext());
675 else if (old->getType()->isIntegerTy(32))
676 addingType = Type::getFloatTy(old->getContext());
677 }
678 }
679 if (!addingType) {
680 std::string s;
681 llvm::raw_string_ostream ss(s);
682 ss << "Cannot deduce adding type of: " << *val << "\n";
683 ss << " + idxs {";
684 for (auto idx : idxs)
685 ss << *idx << ",";
686 ss << "}\n";
687 if (auto inst = dyn_cast<Instruction>(val)) {
688 EmitNoTypeError(ss.str(), *inst, this, BuilderM);
689 return addedSelects;
690 } else if (CustomErrorHandler) {
691 CustomErrorHandler(ss.str().c_str(), wrap(val), ErrorType::NoType,
692 TR.analyzer, nullptr, wrap(&BuilderM));
693 return addedSelects;
694 } else {
695 TR.dump(ss);
696 llvm::errs() << ss.str() << "\n";
697 llvm_unreachable("Cannot deduce adding type");
698 return addedSelects;
699 }
700 }
701 assert(addingType);
702 assert(addingType->isFPOrFPVectorTy());
703
704 auto oldBitSize =
705 oldFunc->getParent()->getDataLayout().getTypeSizeInBits(old->getType());
706 auto newBitSize =
707 oldFunc->getParent()->getDataLayout().getTypeSizeInBits(addingType);
708
709 if (oldBitSize == newBitSize) {
710 } else if (oldBitSize > newBitSize && oldBitSize % newBitSize == 0) {
711 if (!addingType->isVectorTy())
712 addingType =
713 VectorType::get(addingType, oldBitSize / newBitSize, false);
714 } else {
715 std::string s;
716 llvm::raw_string_ostream ss(s);
717 ss << "Illegal intermediate when adding to: " << *val
718 << " with addingType: " << *addingType << "\n"
719 << " old: " << *old << " dif: " << *dif << "\n"
720 << " oldBitSize: " << oldBitSize << " newBitSize: " << newBitSize
721 << "\n";
722 if (CustomErrorHandler) {
723 CustomErrorHandler(ss.str().c_str(), wrap(val), ErrorType::NoType,
724 TR.analyzer, nullptr, wrap(&BuilderM));
725 return addedSelects;
726 } else {
727 DebugLoc loc;
728 if (auto inst = dyn_cast<Instruction>(val))
729 EmitFailure("CannotDeduceType", inst->getDebugLoc(), inst, ss.str());
730 else {
731 llvm::errs() << ss.str() << "\n";
732 llvm_unreachable("Cannot deduce adding type");
733 }
734 return addedSelects;
735 }
736 }
737
738 Value *bcold = old;
739 Value *bcdif = dif;
740 Type *intTy = nullptr;
741 if (old->getType()->isPointerTy()) {
742 auto &DL = oldFunc->getParent()->getDataLayout();
743 intTy = Type::getIntNTy(old->getContext(), DL.getPointerSizeInBits());
744 bcold = BuilderM.CreatePtrToInt(bcold, intTy);
745 bcdif = BuilderM.CreatePtrToInt(bcdif, intTy);
746 } else {
747 intTy = old->getType();
748 }
749
750 bcold = BuilderM.CreateBitCast(bcold, addingType);
751 bcdif = BuilderM.CreateBitCast(bcdif, addingType);
752
753 res = faddForSelect(bcold, bcdif);
754 if (SelectInst *select = dyn_cast<SelectInst>(res)) {
755 assert(addedSelects.back() == select);
756 addedSelects.erase(addedSelects.end() - 1);
757
758 Value *tval = BuilderM.CreateBitCast(select->getTrueValue(), intTy);
759 Value *fval = BuilderM.CreateBitCast(select->getFalseValue(), intTy);
760 if (old->getType()->isPointerTy()) {
761 tval = BuilderM.CreateIntToPtr(tval, old->getType());
762 fval = BuilderM.CreateIntToPtr(fval, old->getType());
763 }
764 res = BuilderM.CreateSelect(select->getCondition(), tval, fval);
765 assert(select->getNumUses() == 0);
766 } else {
767 res = BuilderM.CreateBitCast(res, intTy);
768 if (old->getType()->isPointerTy())
769 res = BuilderM.CreateIntToPtr(res, old->getType());
770 }
771 if (!mask) {
772 BuilderM.CreateStore(res, ptr);
773 // store->setAlignment(align);
774 } else {
775 Type *tys[] = {res->getType(), ptr->getType()};
776 auto F = getIntrinsicDeclaration(oldFunc->getParent(),
777 Intrinsic::masked_store, tys);
778 auto align = cast<AllocaInst>(ptr)->getAlign().value();
779 assert(align);
780 Value *alignv =
781 ConstantInt::get(Type::getInt32Ty(mask->getContext()), align);
782 Value *args[] = {res, ptr, alignv, mask};
783 BuilderM.CreateCall(F, args);
784 }
785 return addedSelects;
786 } else if (old->getType()->isFPOrFPVectorTy()) {
787 // TODO consider adding type
788 res = faddForSelect(old, dif);
789
790 if (!mask) {
791 BuilderM.CreateStore(res, ptr);
792 // store->setAlignment(align);
793 } else {
794 Type *tys[] = {res->getType(), ptr->getType()};
795 auto F = getIntrinsicDeclaration(oldFunc->getParent(),
796 Intrinsic::masked_store, tys);
797 auto align = cast<AllocaInst>(ptr)->getAlign().value();
798 assert(align);
799 Value *alignv =
800 ConstantInt::get(Type::getInt32Ty(mask->getContext()), align);
801 Value *args[] = {res, ptr, alignv, mask};
802 BuilderM.CreateCall(F, args);
803 }
804 return addedSelects;
805 } else if (auto st = dyn_cast<StructType>(old->getType())) {
806 assert(!mask);
807 if (mask)
808 llvm_unreachable("cannot handle recursive addToDiffe with mask");
809 for (unsigned i = 0; i < st->getNumElements(); ++i) {
810 // TODO pass in full type tree here and recurse into tree.
811 if (st->getElementType(i)->isPointerTy())
812 continue;
813 if (st->getElementType(i)->isIntegerTy(8) ||
814 st->getElementType(i)->isIntegerTy(1))
815 continue;
816 Value *v = ConstantInt::get(Type::getInt32Ty(st->getContext()), i);
817 SmallVector<Value *, 2> idx2(idxs.begin(), idxs.end());
818 idx2.push_back(v);
819 // FIXME: reconsider if passing a nullptr is correct here.
820 auto selects = addToDiffe(val, extractMeta(BuilderM, dif, i), BuilderM,
821 nullptr, idx2);
822 for (auto select : selects) {
823 addedSelects.push_back(select);
824 }
825 }
826 return addedSelects;
827 } else if (auto at = dyn_cast<ArrayType>(old->getType())) {
828 assert(!mask);
829 if (mask)
830 llvm_unreachable("cannot handle recursive addToDiffe with mask");
831 if (at->getElementType()->isPointerTy())
832 return addedSelects;
833 for (unsigned i = 0; i < at->getNumElements(); ++i) {
834 // TODO pass in full type tree here and recurse into tree.
835 Value *v = ConstantInt::get(Type::getInt32Ty(at->getContext()), i);
836 SmallVector<Value *, 2> idx2(idxs.begin(), idxs.end());
837 idx2.push_back(v);
838 auto selects = addToDiffe(val, extractMeta(BuilderM, dif, i), BuilderM,
839 addingType, idx2);
840 for (auto select : selects) {
841 addedSelects.push_back(select);
842 }
843 }
844 return addedSelects;
845 } else {
846 llvm::errs() << " idx: {";
847 for (auto i : idxs)
848 llvm::errs() << *i << ", ";
849 llvm::errs() << "}\n";
850 if (addingType)
851 llvm::errs() << " addingType: " << *addingType << "\n";
852 else
853 llvm::errs() << " addingType: null\n";
854 llvm::errs() << " oldType:" << *old->getType() << " old:" << *old << "\n";
855 llvm_unreachable("unknown type to add to diffe");
856 exit(1);
857 }
858}
859
860void DiffeGradientUtils::setDiffe(Value *val, Value *toset,
861 IRBuilder<> &BuilderM) {
862#ifndef NDEBUG
863 if (auto arg = dyn_cast<Argument>(val))
864 assert(arg->getParent() == oldFunc);
865 if (auto inst = dyn_cast<Instruction>(val))
866 assert(inst->getParent()->getParent() == oldFunc);
867 if (isConstantValue(val)) {
868 llvm::errs() << *newFunc << "\n";
869 llvm::errs() << *val << "\n";
870 }
871 assert(!isConstantValue(val));
872#endif
873 toset = SanitizeDerivatives(val, toset, BuilderM);
877 assert(getShadowType(val->getType()) == toset->getType());
878 auto found = invertedPointers.find(val);
879 assert(found != invertedPointers.end());
880 auto placeholder0 = &*found->second;
881 auto placeholder = cast<PHINode>(placeholder0);
882 invertedPointers.erase(found);
883 replaceAWithB(placeholder, toset);
884 placeholder->replaceAllUsesWith(toset);
885 erase(placeholder);
886 invertedPointers.insert(
887 std::make_pair((const Value *)val, InvertedPointerVH(this, toset)));
888 return;
889 }
890 Value *tostore = getDifferential(val);
891#if LLVM_VERSION_MAJOR < 17
892 if (toset->getContext().supportsTypedPointers()) {
893 if (toset->getType() != tostore->getType()->getPointerElementType()) {
894 llvm::errs() << "toset:" << *toset << "\n";
895 llvm::errs() << "tostore:" << *tostore << "\n";
896 }
897 assert(toset->getType() == tostore->getType()->getPointerElementType());
898 }
899#endif
900 BuilderM.CreateStore(toset, tostore);
901}
902
903CallInst *DiffeGradientUtils::freeCache(BasicBlock *forwardPreheader,
904 const SubLimitType &sublimits, int i,
905 AllocaInst *alloc, llvm::Type *T,
906 ConstantInt *byteSizeOfType,
907 Value *storeInto, MDNode *InvariantMD) {
908 if (!FreeMemory)
909 return nullptr;
910 assert(reverseBlocks.find(forwardPreheader) != reverseBlocks.end());
911 assert(reverseBlocks[forwardPreheader].size());
912 IRBuilder<> tbuild(reverseBlocks[forwardPreheader].back());
913 tbuild.setFastMathFlags(getFast());
914
915 // ensure we are before the terminator if it exists
916 if (hasTerminator(tbuild.GetInsertBlock())) {
917 tbuild.SetInsertPoint(tbuild.GetInsertBlock()->getTerminator());
918 }
919
920 ValueToValueMapTy antimap;
921 for (int j = sublimits.size() - 1; j >= i; j--) {
922 auto &innercontainedloops = sublimits[j].second;
923 for (auto riter = innercontainedloops.rbegin(),
924 rend = innercontainedloops.rend();
925 riter != rend; ++riter) {
926 const auto &idx = riter->first;
927 if (idx.var) {
928 antimap[idx.var] =
929 tbuild.CreateLoad(idx.var->getType(), idx.antivaralloc);
930 }
931 }
932 }
933
934 Value *metaforfree = unwrapM(storeInto, tbuild, antimap,
936
937#if LLVM_VERSION_MAJOR < 17
938 if (metaforfree->getContext().supportsTypedPointers()) {
939 assert(T == metaforfree->getType()->getPointerElementType());
940 }
941#endif
942
943 LoadInst *forfree = cast<LoadInst>(tbuild.CreateLoad(T, metaforfree));
944 forfree->setMetadata(LLVMContext::MD_invariant_group, InvariantMD);
945 forfree->setMetadata(LLVMContext::MD_dereferenceable,
946 MDNode::get(forfree->getContext(),
947 ArrayRef<Metadata *>(ConstantAsMetadata::get(
948 byteSizeOfType))));
949 forfree->setName("forfree");
950 unsigned align = getCacheAlignment(
951 (unsigned)newFunc->getParent()->getDataLayout().getPointerSize());
952 forfree->setAlignment(Align(align));
953
954 CallInst *ci = CreateDealloc(tbuild, forfree);
955 if (ci) {
956 if (newFunc->getSubprogram())
957 ci->setDebugLoc(DILocation::get(newFunc->getContext(), 0, 0,
958 newFunc->getSubprogram(), 0));
959 scopeFrees[alloc].insert(ci);
960 }
961 return ci;
962}
963
964void DiffeGradientUtils::addToInvertedPtrDiffe(Instruction *orig,
965 Value *origVal, Type *addingType,
966 unsigned start, unsigned size,
967 Value *origptr, Value *dif,
968 IRBuilder<> &BuilderM,
969 MaybeAlign align, Value *mask) {
970 auto &DL = oldFunc->getParent()->getDataLayout();
971
972 auto addingSize = (DL.getTypeSizeInBits(addingType) + 1) / 8;
973 if (addingSize != size) {
974 assert(size > addingSize);
975 addingType =
976 VectorType::get(addingType, size / addingSize, /*isScalable*/ false);
977 size = (size / addingSize) * addingSize;
978 }
979
980 Value *ptr;
981
982 switch (mode) {
986 ptr = invertPointerM(origptr, BuilderM);
987 break;
989 assert(false && "Invalid derivative mode (ReverseModePrimal)");
990 break;
993 ptr = lookupM(invertPointerM(origptr, BuilderM), BuilderM);
994 break;
995 }
996
997 bool needsCast = false;
998#if LLVM_VERSION_MAJOR < 17
999 if (isa<PointerType>(origptr->getType()) &&
1000 origptr->getContext().supportsTypedPointers()) {
1001 needsCast = origptr->getType()->getPointerElementType() != addingType;
1002 }
1003#endif
1004
1005 assert(ptr);
1006 if (start != 0 || needsCast || !isa<PointerType>(origptr->getType())) {
1007 auto rule = [&](Value *ptr) {
1008 if (!isa<PointerType>(origptr->getType())) {
1009 ptr = BuilderM.CreateIntToPtr(ptr, getUnqual(addingType));
1010 }
1011 if (start != 0) {
1012 auto i8 = Type::getInt8Ty(ptr->getContext());
1013 ptr = BuilderM.CreatePointerCast(
1014 ptr, getPointerType(
1015 i8, cast<PointerType>(ptr->getType())->getAddressSpace()));
1016 auto off = ConstantInt::get(Type::getInt64Ty(ptr->getContext()), start);
1017 ptr = BuilderM.CreateInBoundsGEP(i8, ptr, off);
1018 }
1019 if (needsCast) {
1020 ptr = BuilderM.CreatePointerCast(
1021 ptr, getPointerType(
1022 addingType,
1023 cast<PointerType>(ptr->getType())->getAddressSpace()));
1024 }
1025 return ptr;
1026 };
1027 ptr = applyChainRule(
1029 addingType,
1030 isa<PointerType>(origptr->getType())
1031 ? cast<PointerType>(origptr->getType())->getAddressSpace()
1032 : 0),
1033 BuilderM, rule, ptr);
1034 }
1035
1036 if (getWidth() == 1)
1037 needsCast = dif->getType() != addingType;
1038 else if (auto AT = cast<ArrayType>(dif->getType()))
1039 needsCast = AT->getElementType() != addingType;
1040 else
1041 needsCast =
1042 cast<VectorType>(dif->getType())->getElementType() != addingType;
1043
1044 if (start != 0 || needsCast) {
1045 auto rule = [&](Value *dif) {
1046 if (start != 0) {
1047 IRBuilder<> A(inversionAllocs);
1048 auto i8 = Type::getInt8Ty(ptr->getContext());
1049 auto prevSize = (DL.getTypeSizeInBits(dif->getType()) + 1) / 8;
1050 Type *tys[] = {ArrayType::get(i8, start), addingType,
1051 ArrayType::get(i8, prevSize - start - size)};
1052 auto ST = StructType::get(i8->getContext(), tys, /*isPacked*/ true);
1053 auto Al = A.CreateAlloca(ST, nullptr, "gep.alloca");
1054 auto SI = BuilderM.CreateStore(
1055 dif, BuilderM.CreatePointerCast(Al, getUnqual(dif->getType())));
1056 if (align) {
1057 Al->setAlignment(*align);
1058 SI->setAlignment(*align);
1059 }
1060 Value *idxs[] = {
1061 ConstantInt::get(Type::getInt64Ty(ptr->getContext()), 0),
1062 ConstantInt::get(Type::getInt32Ty(ptr->getContext()), 1)};
1063
1064 auto difp = BuilderM.CreateInBoundsGEP(ST, Al, idxs, "gep.ptr");
1065 auto LI = BuilderM.CreateLoad(addingType, difp, "gep.load");
1066 LI->setAlignment(commonAlignment(Al->getAlign(), start));
1067 dif = LI;
1068 }
1069 if (dif->getType() != addingType) {
1070 auto difSize = (DL.getTypeSizeInBits(dif->getType()) + 1) / 8;
1071 if (difSize < size) {
1072 llvm::errs() << " ds: " << difSize << " as: " << size << "\n";
1073 llvm::errs() << " dif: " << *dif << " adding: " << *addingType
1074 << "\n";
1075 }
1076 assert(difSize >= size);
1077 if (CastInst::castIsValid(Instruction::CastOps::BitCast, dif,
1078 addingType))
1079 dif = BuilderM.CreateBitCast(dif, addingType);
1080 else {
1081 IRBuilder<> A(inversionAllocs);
1082 auto Al = A.CreateAlloca(dif->getType(), nullptr, "cast.alloca");
1083 BuilderM.CreateStore(dif, Al);
1084 auto LI = BuilderM.CreateLoad(
1085 addingType, BuilderM.CreatePointerCast(Al, getUnqual(addingType)),
1086 "cast.load");
1087 LI->setAlignment(Al->getAlign());
1088 dif = LI;
1089 }
1090 }
1091 return dif;
1092 };
1093 dif = applyChainRule(addingType, BuilderM, rule, dif);
1094 }
1095
1096 auto TmpOrig = getBaseObject(origptr);
1097
1098 // atomics
1099 bool Atomic = isAtomic(origptr);
1100 auto TT = llvm::Triple(newFunc->getParent()->getTargetTriple());
1101 auto Arch = TT.getArch();
1102 // No need to do atomic on local memory for CUDA since it can't be raced
1103 // upon
1104 if (isa<AllocaInst>(TmpOrig) && isGPUArch(TT)) {
1105 Atomic = false;
1106 }
1107 // Moreover no need to do atomic on local shadows regardless since they are
1108 // not captured/escaping and created in this function. This assumes that
1109 // all additional parallelism in this function is outlined.
1110 if (backwardsOnlyShadows.find(TmpOrig) != backwardsOnlyShadows.end())
1111 Atomic = false;
1112 if (Atomic && elementwiseReadForContext(orig, origptr))
1113 Atomic = false;
1114
1115 if (Atomic) {
1116 // For amd_target constant AS is 4 and if the primal is in it we need to
1117 // cast the derivative value to AS 1
1118 if (Arch == Triple::amd_target &&
1119 cast<PointerType>(origptr->getType())->getAddressSpace() == 4) {
1120 auto rule = [&](Value *ptr) {
1121 return BuilderM.CreateAddrSpaceCast(ptr, getPointerType(addingType, 1));
1122 };
1123 ptr = applyChainRule(getPointerType(addingType, 1), BuilderM, rule, ptr);
1124 }
1125
1126 if (mask) {
1127 std::string s;
1128 llvm::raw_string_ostream ss(s);
1129 ss << "Unimplemented masked atomic fadd for ptr:" << *ptr
1130 << " dif:" << *dif << " mask: " << *mask << " orig: " << *orig << "\n";
1131 if (CustomErrorHandler) {
1132 CustomErrorHandler(ss.str().c_str(), wrap(orig),
1133 ErrorType::NoDerivative, this, nullptr,
1134 wrap(&BuilderM));
1135 return;
1136 } else {
1137 EmitFailure("NoDerivative", orig->getDebugLoc(), orig, ss.str());
1138 return;
1139 }
1140 }
1141
1142 /*
1143 while (auto ASC = dyn_cast<AddrSpaceCastInst>(ptr)) {
1144 ptr = ASC->getOperand(0);
1145 }
1146 while (auto ASC = dyn_cast<ConstantExpr>(ptr)) {
1147 if (!ASC->isCast()) break;
1148 if (ASC->getOpcode() != Instruction::AddrSpaceCast) break;
1149 ptr = ASC->getOperand(0);
1150 }
1151 */
1152 AtomicRMWInst::BinOp op = AtomicRMWInst::FAdd;
1153 if (auto vt = dyn_cast<VectorType>(addingType)) {
1154 assert(!vt->getElementCount().isScalable());
1155 size_t numElems = vt->getElementCount().getKnownMinValue();
1156 auto rule = [&](Value *dif, Value *ptr) {
1157 for (size_t i = 0; i < numElems; ++i) {
1158 auto vdif = BuilderM.CreateExtractElement(dif, i);
1159 vdif = SanitizeDerivatives(orig, vdif, BuilderM);
1160 Value *Idxs[] = {
1161 ConstantInt::get(Type::getInt64Ty(vt->getContext()), 0),
1162 ConstantInt::get(Type::getInt32Ty(vt->getContext()), i)};
1163 auto vptr = BuilderM.CreateGEP(addingType, ptr, Idxs);
1164 MaybeAlign alignv = align;
1165 if (alignv) {
1166 if (start != 0) {
1167 // todo make better alignment calculation
1168 assert((*alignv).value() != 0);
1169 if (start % (*alignv).value() != 0) {
1170 alignv = Align(1);
1171 }
1172 }
1173 }
1174 BuilderM.CreateAtomicRMW(op, vptr, vdif, alignv,
1175 AtomicOrdering::Monotonic,
1176 SyncScope::System);
1177 }
1178 };
1179 applyChainRule(BuilderM, rule, dif, ptr);
1180 } else {
1181 auto rule = [&](Value *dif, Value *ptr) {
1182 dif = SanitizeDerivatives(orig, dif, BuilderM);
1183 MaybeAlign alignv = align;
1184 if (alignv) {
1185 if (start != 0) {
1186 // todo make better alignment calculation
1187 assert((*alignv).value() != 0);
1188 if (start % (*alignv).value() != 0) {
1189 alignv = Align(1);
1190 }
1191 }
1192 }
1193 BuilderM.CreateAtomicRMW(op, ptr, dif, alignv,
1194 AtomicOrdering::Monotonic, SyncScope::System);
1195 };
1196 applyChainRule(BuilderM, rule, dif, ptr);
1197 }
1198 return;
1199 }
1200
1201 if (!mask) {
1202
1203 size_t idx = 0;
1204 auto rule = [&](Value *ptr, Value *dif) {
1205 auto LI = BuilderM.CreateLoad(addingType, ptr);
1206
1207 Value *res = BuilderM.CreateFAdd(LI, dif);
1208 res = SanitizeDerivatives(orig, res, BuilderM);
1209 StoreInst *st = BuilderM.CreateStore(res, ptr);
1210
1211 SmallVector<Metadata *, 1> scopeMD = {
1212 getDerivativeAliasScope(origptr, idx)};
1213 if (auto origValI = dyn_cast_or_null<Instruction>(origVal))
1214 if (auto MD = origValI->getMetadata(LLVMContext::MD_alias_scope)) {
1215 auto MDN = cast<MDNode>(MD);
1216 for (auto &o : MDN->operands())
1217 scopeMD.push_back(o);
1218 }
1219 auto scope = MDNode::get(LI->getContext(), scopeMD);
1220 LI->setMetadata(LLVMContext::MD_alias_scope, scope);
1221 st->setMetadata(LLVMContext::MD_alias_scope, scope);
1222
1223 SmallVector<Metadata *, 1> MDs;
1224 for (ssize_t j = -1; j < getWidth(); j++) {
1225 if (j != (ssize_t)idx)
1226 MDs.push_back(getDerivativeAliasScope(origptr, j));
1227 }
1228 if (auto origValI = dyn_cast_or_null<Instruction>(origVal))
1229 if (auto MD = origValI->getMetadata(LLVMContext::MD_noalias)) {
1230 auto MDN = cast<MDNode>(MD);
1231 for (auto &o : MDN->operands())
1232 MDs.push_back(o);
1233 }
1234 idx++;
1235 auto noscope = MDNode::get(ptr->getContext(), MDs);
1236 LI->setMetadata(LLVMContext::MD_noalias, noscope);
1237 st->setMetadata(LLVMContext::MD_noalias, noscope);
1238
1239 if (origVal && isa<Instruction>(origVal) && start == 0 &&
1240 size == (DL.getTypeSizeInBits(origVal->getType()) + 7) / 8) {
1241 auto origValI = cast<Instruction>(origVal);
1242 LI->copyMetadata(*origValI, MD_ToCopy);
1243 unsigned int StoreData[] = {LLVMContext::MD_tbaa,
1244 LLVMContext::MD_tbaa_struct};
1245 for (auto MD : StoreData)
1246 st->setMetadata(MD, origValI->getMetadata(MD));
1247 }
1248
1249 LI->setDebugLoc(getNewFromOriginal(orig->getDebugLoc()));
1250 st->setDebugLoc(getNewFromOriginal(orig->getDebugLoc()));
1251
1252 if (align) {
1253 auto alignv = align ? (*align).value() : 0;
1254 if (alignv != 0) {
1255 if (start != 0) {
1256 // todo make better alignment calculation
1257 if (start % alignv != 0) {
1258 alignv = 1;
1259 }
1260 }
1261
1262 LI->setAlignment(Align(alignv));
1263 st->setAlignment(Align(alignv));
1264 }
1265 }
1266 };
1267 applyChainRule(BuilderM, rule, ptr, dif);
1268 } else {
1269 Type *tys[] = {addingType, origptr->getType()};
1270 auto LF = getIntrinsicDeclaration(oldFunc->getParent(),
1271 Intrinsic::masked_load, tys);
1272 auto SF = getIntrinsicDeclaration(oldFunc->getParent(),
1273 Intrinsic::masked_store, tys);
1274 unsigned aligni = align ? align->value() : 0;
1275
1276 if (aligni != 0)
1277 if (start != 0) {
1278 // todo make better alignment calculation
1279 if (start % aligni != 0) {
1280 aligni = 1;
1281 }
1282 }
1283 Value *alignv =
1284 ConstantInt::get(Type::getInt32Ty(mask->getContext()), aligni);
1285 auto rule = [&](Value *ptr, Value *dif) {
1286 Value *largs[] = {ptr, alignv, mask,
1287 Constant::getNullValue(dif->getType())};
1288 Value *LI = BuilderM.CreateCall(LF, largs);
1289 Value *res = BuilderM.CreateFAdd(LI, dif);
1290 res = SanitizeDerivatives(orig, res, BuilderM, mask);
1291 Value *sargs[] = {res, ptr, alignv, mask};
1292 BuilderM.CreateCall(SF, sargs);
1293 };
1294 applyChainRule(BuilderM, rule, ptr, dif);
1295 }
1296}
1297
1299 llvm::Instruction *orig, llvm::Value *origVal, TypeTree vd,
1300 unsigned LoadSize, llvm::Value *origptr, llvm::Value *prediff,
1301 llvm::IRBuilder<> &Builder2, MaybeAlign alignment, llvm::Value *premask)
1302
1303{
1304
1305 unsigned start = 0;
1306 unsigned size = LoadSize;
1307
1308 assert(prediff);
1309
1310 BasicBlock *merge = nullptr;
1311
1312 while (1) {
1313 unsigned nextStart = size;
1314
1315 auto dt = vd[{-1}];
1316 for (size_t i = start; i < size; ++i) {
1317 bool Legal = true;
1318 dt.checkedOrIn(vd[{(int)i}], /*PointerIntSame*/ true, Legal);
1319 if (!Legal) {
1320 nextStart = i;
1321 break;
1322 }
1323 }
1324 if (!dt.isKnown()) {
1325 TR.dump();
1326 llvm::errs() << " vd:" << vd.str() << " start:" << start
1327 << " size: " << size << " dt:" << dt.str() << "\n";
1328 }
1329 assert(dt.isKnown());
1330
1331 if (Type *isfloat = dt.isFloat()) {
1332
1333 if (origVal) {
1334 if (start == 0 && nextStart == LoadSize) {
1335 setDiffe(origVal,
1336 Constant::getNullValue(getShadowType(origVal->getType())),
1337 Builder2);
1338 } else {
1339 Value *tostore = getDifferential(origVal);
1340
1341 auto i8 = Type::getInt8Ty(tostore->getContext());
1342 if (start != 0) {
1343 tostore = Builder2.CreatePointerCast(
1344 tostore,
1346 i8,
1347 cast<PointerType>(tostore->getType())->getAddressSpace()));
1348 auto off = ConstantInt::get(Type::getInt64Ty(tostore->getContext()),
1349 start);
1350 tostore = Builder2.CreateInBoundsGEP(i8, tostore, off);
1351 }
1352 auto AT = ArrayType::get(i8, nextStart - start);
1353 tostore = Builder2.CreatePointerCast(
1354 tostore, getPointerType(AT, cast<PointerType>(tostore->getType())
1355 ->getAddressSpace()));
1356 Builder2.CreateStore(Constant::getNullValue(AT), tostore);
1357 }
1358 }
1359
1360 if (!isConstantValue(origptr)) {
1361 auto basePtr = getBaseObject(origptr);
1362 assert(!isConstantValue(basePtr));
1363 // If runtime activity, first see if we can prove that the shadow/primal
1364 // are distinct statically as they are allocas/mallocs, if not compare
1365 // the pointers and conditionally execute.
1366 if ((!isa<AllocaInst>(basePtr) && !isAllocationCall(basePtr, TLI)) &&
1367 runtimeActivity && !merge) {
1368 Value *primal_val = lookupM(getNewFromOriginal(origptr), Builder2);
1369 Value *shadow_val =
1370 lookupM(invertPointerM(origptr, Builder2), Builder2);
1371 if (getWidth() != 1) {
1372 shadow_val = extractMeta(Builder2, shadow_val, 0);
1373 }
1374 Value *shadow = Builder2.CreateICmpNE(primal_val, shadow_val);
1375
1376 BasicBlock *current = Builder2.GetInsertBlock();
1377 BasicBlock *conditional =
1378 addReverseBlock(current, current->getName() + "_active");
1379 merge = addReverseBlock(conditional, current->getName() + "_amerge");
1380 Builder2.CreateCondBr(shadow, conditional, merge);
1381 Builder2.SetInsertPoint(conditional);
1382 }
1383 // Masked partial type is unhanled.
1384 if (premask)
1385 assert(start == 0 && nextStart == LoadSize);
1386 addToInvertedPtrDiffe(orig, origVal, isfloat, start, nextStart - start,
1387 origptr, prediff, Builder2, alignment, premask);
1388 }
1389 }
1390
1391 if (nextStart == size)
1392 break;
1393 start = nextStart;
1394 }
1395 if (merge) {
1396 Builder2.CreateBr(merge);
1397 Builder2.SetInsertPoint(merge);
1398 }
1399}
@ AttemptFullUnwrapWithLookup
static bool isZero(llvm::Constant *cst)
DIFFE_TYPE
Potential differentiable argument classifications.
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
llvm::cl::opt< bool > looseTypeAnalysis
SmallVector< unsigned int, 9 > MD_ToCopy
CallInst * CreateDealloc(llvm::IRBuilder<> &Builder, llvm::Value *ToFree)
Definition Utils.cpp:871
void ZeroMemory(llvm::IRBuilder<> &Builder, llvm::Type *T, llvm::Value *obj, bool isTape)
Definition Utils.cpp:508
void EmitNoTypeError(const std::string &message, llvm::Instruction &inst, GradientUtils *gutils, llvm::IRBuilder<> &Builder2)
Definition Utils.cpp:4675
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Definition Utils.cpp:62
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
Definition Utils.cpp:4026
llvm::Value * SanitizeDerivatives(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM, llvm::Value *mask)
Definition Utils.cpp:3929
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
static bool isGPUArch(const llvm::Triple &TT)
Definition Utils.h:162
static llvm::PointerType * getUnqual(llvm::Type *T)
Definition Utils.h:1329
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
Definition Utils.h:2541
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1711
static T min(T a, T b)
Pick the maximum value.
Definition Utils.h:412
static bool hasTerminator(llvm::BasicBlock *BB)
Definition Utils.h:2981
DerivativeMode
Definition Utils.h:524
llvm::Function *const newFunc
The function whose instructions we are caching.
std::map< llvm::AllocaInst *, std::set< llvm::AssertingVH< llvm::CallInst > > > scopeFrees
A map of allocations to a set of instructions which free memory as part of the cache.
llvm::TargetLibraryInfo & TLI
Various analysis results of newFunc.
llvm::LoopInfo LI
llvm::BasicBlock * inversionAllocs
llvm::SmallVector< std::pair< llvm::Value *, llvm::SmallVector< std::pair< LoopContext, llvm::Value * >, 4 > >, 0 > SubLimitType
Given a LimitContext ctx, representing a location inside a loop nest, break each of the loops up into...
unsigned getCacheAlignment(unsigned bsize) const
llvm::ValueMap< const llvm::Value *, llvm::TrackingVH< llvm::AllocaInst > > differentials
void addToInvertedPtrDiffe(llvm::Instruction *orig, llvm::Value *origVal, llvm::Type *addingType, unsigned start, unsigned size, llvm::Value *origptr, llvm::Value *dif, llvm::IRBuilder<> &BuilderM, llvm::MaybeAlign align=llvm::MaybeAlign(), llvm::Value *mask=nullptr)
align is the alignment that should be specified for load/store to pointer
llvm::Value * diffe(llvm::Value *val, llvm::IRBuilder<> &BuilderM)
llvm::AllocaInst * getDifferential(llvm::Value *val)
bool FreeMemory
Whether to free memory in reverse pass or split forward.
static DiffeGradientUtils * CreateFromClone(EnzymeLogic &Logic, DerivativeMode mode, bool runtimeActivity, bool strongZero, unsigned width, llvm::Function *todiff, llvm::TargetLibraryInfo &TLI, TypeAnalysis &TA, FnTypeInfo &oldTypeInfo, DIFFE_TYPE retType, bool shadowReturnArg, bool diffeReturnArg, llvm::ArrayRef< DIFFE_TYPE > constant_args, bool returnTape, bool returnPrimal, llvm::Type *additionalArg, bool omp)
llvm::CallInst * freeCache(llvm::BasicBlock *forwardPreheader, const SubLimitType &sublimits, int i, llvm::AllocaInst *alloc, llvm::Type *myType, llvm::ConstantInt *byteSizeOfType, llvm::Value *storeInto, llvm::MDNode *InvariantMD) override
If an allocation is requested to be freed, this subclass will be called to chose how and where to fre...
void setDiffe(llvm::Value *val, llvm::Value *toset, llvm::IRBuilder<> &BuilderM)
llvm::SmallVector< llvm::SelectInst *, 4 > addToDiffe(llvm::Value *val, llvm::Value *dif, llvm::IRBuilder<> &BuilderM, llvm::Type *addingType, llvm::ArrayRef< llvm::Value * > idxs={}, llvm::Value *mask=nullptr)
Returns created select instructions, if any.
PreProcessCache PPC
DerivativeMode mode
llvm::ValueMap< const llvm::Value *, InvertedPointerVH > invertedPointers
llvm::DebugLoc getNewFromOriginal(const llvm::DebugLoc L) const
static llvm::Value * extractMeta(llvm::IRBuilder<> &Builder, llvm::Value *Agg, unsigned off, const llvm::Twine &name="")
Helper routine to extract a nested element from a struct/array. This is.
llvm::Value * applyChainRule(llvm::Type *diffType, llvm::IRBuilder<> &Builder, Func rule, Args... args)
Unwraps a vector derivative from its internal representation and applies a function f to each element...
TypeAnalysis & TA
TypeResults TR
llvm::BasicBlock * addReverseBlock(llvm::BasicBlock *currentBlock, llvm::Twine const &name, bool forkCache=true, bool push=true)
unsigned getWidth()
std::map< llvm::BasicBlock *, llvm::SmallVector< llvm::BasicBlock *, 4 > > reverseBlocks
Map of primal block to corresponding block(s) in reverse.
llvm::Function * oldFunc
void replaceAWithB(llvm::Value *A, llvm::Value *B, bool storeInCache=false) override
Replace this instruction both in LLVM modules and any local data-structures.
EnzymeLogic & Logic
llvm::Value * unwrapM(llvm::Value *const val, llvm::IRBuilder<> &BuilderM, const llvm::ValueToValueMapTy &available, UnwrapMode unwrapMode, llvm::BasicBlock *scope=nullptr, bool permitCache=true) override final
if full unwrap, don't just unwrap this instruction, but also its operands, etc
llvm::Value * lookupM(llvm::Value *val, llvm::IRBuilder<> &BuilderM, const llvm::ValueToValueMapTy &incoming_availalble=llvm::ValueToValueMapTy(), bool tryLegalRecomputeCheck=true, llvm::BasicBlock *scope=nullptr) override
High-level utility to get the value an instruction at a new location specified by BuilderM.
bool isAtomic(llvm::Value *origptr) const
llvm::MDNode * getDerivativeAliasScope(const llvm::Value *origptr, ssize_t newptr)
llvm::Value * invertPointerM(llvm::Value *val, llvm::IRBuilder<> &BuilderM)
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
llvm::ValueMap< llvm::Value *, ShadowRematerializer > backwardsOnlyShadows
Only loaded from and stored to (not captured), mapped to the stores (and memset).
bool isConstantValue(llvm::Value *val) const
void erase(llvm::Instruction *I) override
Erase this instruction both from LLVM modules and any local data-structures.
llvm::Function * CloneFunctionWithReturns(DerivativeMode mode, unsigned width, llvm::Function *&F, llvm::ValueToValueMapTy &ptrInputs, llvm::ArrayRef< DIFFE_TYPE > constant_args, llvm::SmallPtrSetImpl< llvm::Value * > &constants, llvm::SmallPtrSetImpl< llvm::Value * > &nonconstant, llvm::SmallPtrSetImpl< llvm::Value * > &returnvals, bool returnTape, bool returnPrimal, bool returnShadow, const llvm::Twine &name, llvm::ValueMap< const llvm::Value *, AssertingReplacingVH > *VMapO, bool diffeReturnArg, llvm::Type *additionalArg=nullptr)
Full interprocedural TypeAnalysis.
TypeResults analyzeFunction(const FnTypeInfo &fn)
Analyze a particular function, returning the results.
A holder class representing the results of running TypeAnalysis on a given function.
void dump(llvm::raw_ostream &ss=llvm::errs()) const
Prints all known information.
TypeAnalyzer * analyzer
llvm::Function * getFunction() const
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
Definition TypeTree.h:72
std::string str() const
Returns a string representation of this TypeTree.
Definition TypeTree.h:1455
Struct containing all contextual type information for a particular function call.
std::map< llvm::Argument *, TypeTree > Arguments
Types of arguments.
TypeTree Return
Type of return.
std::map< llvm::Argument *, std::set< int64_t > > KnownValues
The specific constant(s) known to represented by an argument, if constant.