20#include "mlir/Dialect/Affine/IR/AffineOps.h"
21#include "mlir/Dialect/MemRef/IR/MemRef.h"
22#include "mlir/IR/IntegerSet.h"
23#include "llvm/ADT/ScopeExit.h"
31createAffineForWithShadows(Operation *op, OpBuilder &builder,
33 ValueRange remappedOperands, TypeRange rettys) {
34 affine::AffineForOpAdaptor adaptor(remappedOperands,
35 cast<affine::AffineForOp>(original));
36 auto repFor = affine::AffineForOp::create(
37 builder, original->getLoc(), adaptor.getLowerBoundOperands(),
38 adaptor.getLowerBoundMap(), adaptor.getUpperBoundOperands(),
39 adaptor.getUpperBoundMap(), adaptor.getStep().getZExtValue(),
47 remappedOperands.drop_front(adaptor.getLowerBoundOperands().size() +
48 adaptor.getUpperBoundOperands().size()));
52affine::AffineIfOp createAffineIfWithShadows(Operation *op, OpBuilder &builder,
54 affine::AffineIfOp original,
55 ValueRange remappedOperands,
57 affine::AffineIfOpAdaptor adaptor(remappedOperands, original);
58 return affine::AffineIfOp::create(
59 builder, original->getLoc(), rettys, original.getIntegerSet(),
60 adaptor.getOperands(), !original.getElseRegion().empty());
63struct AffineForOpInterfaceReverse
64 :
public ReverseAutoDiffOpInterface::ExternalModel<
65 AffineForOpInterfaceReverse, affine::AffineForOp> {
66 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
68 SmallVector<Value> caches)
const {
69 auto forOp = cast<affine::AffineForOp>(op);
71 affine::AffineBound lb = forOp.getLowerBound();
72 affine::AffineBound ub = forOp.getUpperBound();
74 if (lb.getMap().getNumResults() != 1 || ub.getMap().getNumResults() != 1) {
75 op->emitError() <<
"cannot differentiate loop with minmax bounds yet";
79 SmallVector<bool> operandsActive;
80 for (
auto [operand, result] : llvm::zip_equal(
81 op->getOperands().slice(forOp.getNumControlOperands(),
82 forOp->getNumOperands() -
83 forOp.getNumControlOperands()),
89 SmallVector<Value> revLBOperands, revUBOperands, incomingGradients;
91 for (
int i = 0, e = lb.getNumOperands(); i < e; ++i) {
92 revLBOperands.push_back(gutils->
popCache(caches[i], builder));
95 for (
int i = lb.getNumOperands(), e = forOp.getNumControlOperands(); i < e;
97 revUBOperands.push_back(gutils->
popCache(caches[i], builder));
100 for (
auto &&[active, res] :
101 llvm::zip_equal(operandsActive, op->getResults())) {
103 incomingGradients.push_back(gutils->
diffe(res, builder));
109 auto revFor = affine::AffineForOp::create(
110 builder, op->getLoc(), revLBOperands, lb.getMap(), revUBOperands,
111 ub.getMap(), forOp.getStepAsInt(), incomingGradients);
114 for (
auto &&[oldReg, newReg] :
115 llvm::zip(op->getRegions(), revFor->getRegions())) {
116 for (
auto &&[oBB, revBB] : llvm::zip(oldReg, newReg)) {
117 OpBuilder bodyBuilder(&revBB, revBB.end());
121 affine::AffineYieldOp::create(bodyBuilder, revFor->getLoc());
123 bodyBuilder.setInsertionPoint(revBB.getTerminator());
128 for (
auto operand : oBB.getArguments().slice(1)) {
134 for (
auto &it : oBB.getOperations()) {
135 for (
auto res : it.getResults()) {
137 auto iface = dyn_cast<AutoDiffTypeInterface>(res.getType());
138 if (iface && !iface.isMutable())
144 auto term = oBB.getTerminator();
147 for (
auto &&[active, operand] :
148 llvm::zip_equal(operandsActive, term->getOperands())) {
153 gutils->
setDiffe(operand, revBB.getArgument(argIdx), bodyBuilder);
158 auto first = oBB.rbegin();
161 auto last = oBB.rend();
163 for (
auto it = first; it != last; ++it) {
164 Operation *op = &*it;
169 SmallVector<Value> newResults;
170 newResults.reserve(incomingGradients.size());
172 for (
auto &&[active, arg] :
173 llvm::zip_equal(operandsActive, oBB.getArguments().slice(1))) {
175 newResults.push_back(gutils->
diffe(arg, bodyBuilder));
182 revBB.getTerminator()->setOperands(newResults);
187 for (
auto &&[active, arg] :
188 llvm::zip_equal(operandsActive, forOp.getInits())) {
191 gutils->
addToDiffe(arg, revFor.getResult(resIdx), builder);
197 return success(valid);
200 SmallVector<Value> cacheValues(Operation *op,
202 auto forOp = cast<affine::AffineForOp>(op);
204 SmallVector<Value> caches;
206 for (
auto operand : forOp.getControlOperands()) {
214 void createShadowValues(Operation *op, OpBuilder &builder,
218struct AffineParallelOpInterfaceReverse
219 :
public ReverseAutoDiffOpInterface::ExternalModel<
220 AffineParallelOpInterfaceReverse, affine::AffineParallelOp> {
221 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
223 SmallVector<Value> caches)
const {
224 auto parOp = cast<affine::AffineParallelOp>(op);
225 if (!parOp.getReductions().empty()) {
226 return parOp.emitError() <<
"parallel reductions not yet implemented";
228 if (parOp.hasMinMaxBounds()) {
229 return parOp.emitError() <<
"minmax bounds not yet supported";
232 SmallVector<Value> bounds = llvm::map_to_vector(
233 caches, [&](Value cache) {
return gutils->
popCache(cache, builder); });
234 auto revPar = affine::AffineParallelOp::create(
235 builder, op->getLoc(), parOp.getResultTypes(), parOp.getReductions(),
236 parOp.getLowerBoundsMap(), parOp.getLowerBoundsGroups(),
237 parOp.getUpperBoundsMap(), parOp.getUpperBoundsGroups(),
238 parOp.getSteps(), bounds);
241 OpBuilder::InsertionGuard guard(builder);
242 SmallVector<Type> ivTypes(parOp.getIVs().size(), builder.getIndexType());
243 SmallVector<Location> ivLocs(parOp.getIVs().size(), parOp.getLoc());
244 builder.createBlock(&revPar.getBodyRegion(), revPar.getBodyRegion().begin(),
246 affine::AffineYieldOp::create(builder, parOp.getLoc());
253 Block *oBB = parOp.getBody();
254 Block *rBB = revPar.getBody();
256 OpBuilder bodyBuilder = revPar.getBodyBuilder();
258 bodyBuilder.setInsertionPointToStart(revPar.getBody());
261 bodyBuilder.setInsertionPoint(rBB->getTerminator());
263 auto first = oBB->rbegin();
266 auto last = oBB->rend();
268 for (
auto it = first; it != last; ++it) {
269 Operation *op = &*it;
270 valid &= gutils->
Logic.
visitChild(op, bodyBuilder, gutils).succeeded();
275 return success(valid);
278 SmallVector<Value> cacheValues(Operation *op,
280 auto parOp = cast<affine::AffineParallelOp>(op);
282 SmallVector<Value> caches;
284 for (
auto operand : parOp.getMapOperands()) {
291 void createShadowValues(Operation *op, OpBuilder &builder,
295struct AffineParallelOpEnzymeOpsRemover
297 affine::AffineParallelOp> {
298 static SmallVector<IntOrValue, 1>
299 getDimensionBounds(OpBuilder &builder, affine::AffineParallelOp parOp) {
300 SmallVector<IntOrValue, 1> bounds;
301 auto ranges = parOp.getConstantRanges();
303 for (
auto &&[r, step] : llvm::zip(*ranges, parOp.getSteps())) {
304 bounds.push_back(r / step);
307 for (
auto &&[dim, step] : llvm::enumerate(parOp.getSteps())) {
308 auto lb = AffineApplyOp::create(builder, parOp.getLoc(),
309 parOp.getLowerBoundMap(dim),
310 parOp.getLowerBoundsOperands());
311 auto ub = AffineApplyOp::create(builder, parOp.getLoc(),
312 parOp.getUpperBoundMap(dim),
313 parOp.getUpperBoundsOperands());
314 Value diff = arith::SubIOp::create(builder, parOp.getLoc(), ub, lb);
317 arith::ConstantIndexOp::create(builder, parOp.getLoc(), step);
318 diff = arith::DivUIOp::create(builder, parOp.getLoc(), diff, stepVal);
320 bounds.push_back(diff);
326 static SmallVector<Value> computeReversedIndices(
327 PatternRewriter &rewriter, affine::AffineParallelOp parOp,
328 ArrayRef<Value> otherInductionVariable, ArrayRef<IntOrValue> bounds) {
329 return SmallVector<Value>(otherInductionVariable);
332 static SmallVector<Value>
333 getCanonicalLoopIVs(OpBuilder &builder, affine::AffineParallelOp parOp) {
334 SmallVector<Value> ivs(parOp.getIVs());
335 for (
auto &&[dim, step] : llvm::enumerate(parOp.getSteps())) {
337 auto lbMap = parOp.getLowerBoundMap(dim);
338 if (!(lbMap.isSingleConstant() && lbMap.getSingleConstantResult() == 0)) {
339 auto lb = AffineApplyOp::create(builder, parOp.getLoc(), lbMap,
340 parOp.getLowerBoundsOperands());
341 iv = arith::SubIOp::create(builder, parOp.getLoc(), iv, lb);
346 arith::ConstantIndexOp::create(builder, parOp.getLoc(), step);
347 iv = arith::DivUIOp::create(builder, parOp.getLoc(), iv, stepVal);
355 static IRMapping createArgumentMap(PatternRewriter &rewriter,
356 affine::AffineParallelOp parOp,
357 ArrayRef<Value> indPar,
358 affine::AffineParallelOp otherParOp,
359 ArrayRef<Value> indOther) {
361 for (
auto &&[f, o] : llvm::zip_equal(indPar, indOther))
364 for (
auto &&[fiv, oiv] :
365 llvm::zip_equal(parOp.getIVs(), otherParOp.getIVs())) {
366 if (!map.contains(fiv)) {
367 assert(parOp.getLowerBoundsMap() == otherParOp.getLowerBoundsMap());
369 llvm::zip_equal(parOp.getLowerBoundsOperands(),
370 otherParOp.getLowerBoundsOperands())) {
373 assert(Equivalent(f, o));
375 for (
auto [fstep, ostep] :
376 llvm::zip_equal(parOp.getSteps(), otherParOp.getSteps())) {
379 assert(fstep == ostep);
387 static affine::AffineParallelOp
388 replaceWithNewOperands(PatternRewriter &rewriter,
389 affine::AffineParallelOp otherParOp,
390 ArrayRef<Value> operands) {
391 SmallVector<mlir::Attribute> reductionKinds(
392 otherParOp.getReductions().begin(), otherParOp.getReductions().end());
394 for (
unsigned i = otherParOp->getNumOperands(); i < operands.size(); i++) {
395 reductionKinds.push_back(arith::AtomicRMWKindAttr::get(
396 otherParOp.getContext(), arith::AtomicRMWKind::addf));
399 ValueRange operands_(operands);
400 auto newOtherParOp = affine::AffineParallelOp::create(
401 rewriter, otherParOp.getLoc(), operands_.getTypes(),
402 ArrayAttr::get(otherParOp.getContext(), reductionKinds),
403 otherParOp.getLowerBoundsMap(), otherParOp.getLowerBoundsGroups(),
404 otherParOp.getUpperBoundsMap(), otherParOp.getUpperBoundsGroups(),
405 otherParOp.getSteps(), otherParOp.getMapOperands());
407 newOtherParOp.getRegion().takeBody(otherParOp.getRegion());
408 rewriter.replaceOp(otherParOp, newOtherParOp->getResults().slice(
409 0, otherParOp->getNumResults()));
410 return newOtherParOp;
413 static ValueRange getInits(affine::AffineParallelOp parOp) {
414 return parOp.getInits();
417 static bool mustPostAdd(affine::AffineParallelOp forOp) {
return true; }
419 static Value initialValueInBlock(OpBuilder &builder, Block *body,
421 OpBuilder::InsertionGuard guard(builder);
422 builder.setInsertionPointToStart(body);
423 return cast<AutoDiffTypeInterface>(
424 cast<enzyme::GradientType>(grad.getType()).getBasetype())
425 .createNullValue(builder, grad.getLoc());
429struct AffineLoadOpInterfaceReverse
430 :
public ReverseAutoDiffOpInterface::ExternalModel<
431 AffineLoadOpInterfaceReverse, affine::AffineLoadOp> {
432 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
434 SmallVector<Value> caches)
const {
435 auto loadOp = cast<affine::AffineLoadOp>(op);
436 Value memref = loadOp.getMemref();
438 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
441 Value gradient = gutils->
diffe(loadOp, builder);
442 Value memrefGradient = gutils->
popCache(caches.front(), builder);
444 SmallVector<Value> retrievedArguments;
445 for (Value cache : ValueRange(caches).drop_front(1)) {
446 Value retrievedValue = gutils->
popCache(cache, builder);
447 retrievedArguments.push_back(retrievedValue);
451 bool hasIndex = loadOp.getAffineMap().getNumDims() > 0;
454 SmallVector<Value> indices;
456 loadOp.getAffineMap(), retrievedArguments,
459 Value loadedGradient = memref::LoadOp::create(
460 builder, loadOp.getLoc(), memrefGradient, indices);
461 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
462 loadedGradient, gradient);
463 memref::StoreOp::create(builder, loadOp.getLoc(), addedGradient,
464 memrefGradient, indices);
466 Value loadedGradient = affine::AffineLoadOp::create(
467 builder, loadOp.getLoc(), memrefGradient, loadOp.getAffineMap(),
468 ArrayRef<Value>(retrievedArguments));
469 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
470 loadedGradient, gradient);
471 affine::AffineStoreOp::create(
472 builder, loadOp.getLoc(), addedGradient, memrefGradient,
473 loadOp.getAffineMap(), ArrayRef<Value>(retrievedArguments));
476 bool hasIndex = loadOp.getAffineMap().getNumDims() > 0;
479 SmallVector<Value> indices;
481 loadOp.getAffineMap(), retrievedArguments,
483 memref::AtomicRMWOp::create(builder, loadOp.getLoc(),
484 arith::AtomicRMWKind::addf, gradient,
485 memrefGradient, indices);
487 enzyme::AffineAtomicRMWOp::create(
488 builder, loadOp.getLoc(), gradient.getType(),
489 arith::AtomicRMWKind::addf, gradient, memrefGradient,
490 retrievedArguments, loadOp.getAffineMap());
498 SmallVector<Value> cacheValues(Operation *op,
500 auto loadOp = cast<affine::AffineLoadOp>(op);
501 Value memref = loadOp.getMemref();
502 ValueRange indices = loadOp.getIndices();
503 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
507 SmallVector<Value> caches;
510 for (Value v : indices) {
517 return SmallVector<Value>();
520 void createShadowValues(Operation *op, OpBuilder &builder,
529struct AffineStoreOpInterfaceReverse
530 :
public ReverseAutoDiffOpInterface::ExternalModel<
531 AffineStoreOpInterfaceReverse, affine::AffineStoreOp> {
532 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
534 SmallVector<Value> caches)
const {
535 auto storeOp = cast<affine::AffineStoreOp>(op);
536 Value val = storeOp.getValue();
537 Value memref = storeOp.getMemref();
540 auto iface = cast<AutoDiffTypeInterface>(val.getType());
545 Value memrefGradient = gutils->
popCache(caches.front(), builder);
547 SmallVector<Value> retrievedArguments;
548 for (Value cache : ValueRange(caches).drop_front(1)) {
549 Value retrievedValue = gutils->
popCache(cache, builder);
550 retrievedArguments.push_back(retrievedValue);
553 bool hasIndex = storeOp.getAffineMap().getNumDims() > 0;
555 if (!iface.isMutable()) {
557 Value loadedGradient;
559 SmallVector<Value> indices;
561 storeOp.getAffineMap(), retrievedArguments,
563 loadedGradient = memref::LoadOp::create(builder, storeOp.getLoc(),
564 memrefGradient, indices);
566 loadedGradient = affine::AffineLoadOp::create(
567 builder, storeOp.getLoc(), memrefGradient,
568 storeOp.getAffineMap(), ArrayRef<Value>(retrievedArguments));
570 gutils->
addToDiffe(val, loadedGradient, builder);
574 cast<AutoDiffTypeInterface>(gutils->
getShadowType(val.getType()))
575 .createNullValue(builder, op->getLoc());
579 SmallVector<Value> indices;
581 storeOp.getAffineMap(), retrievedArguments,
583 memref::StoreOp::create(builder, storeOp.getLoc(), zero,
584 memrefGradient, indices);
586 affine::AffineStoreOp::create(builder, storeOp.getLoc(), zero,
587 memrefGradient, storeOp.getAffineMap(),
588 ArrayRef<Value>(retrievedArguments));
595 SmallVector<Value> cacheValues(Operation *op,
597 auto storeOp = cast<affine::AffineStoreOp>(op);
598 Value memref = storeOp.getMemref();
599 ValueRange indices = storeOp.getIndices();
600 Value val = storeOp.getValue();
601 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType())) {
604 SmallVector<Value> caches;
607 for (Value v : indices) {
614 return SmallVector<Value>();
617 void createShadowValues(Operation *op, OpBuilder &builder,
626struct AffineForOpADDataFlow
627 :
public ADDataFlowOpInterface::ExternalModel<AffineForOpADDataFlow,
628 affine::AffineForOp> {
629 SmallVector<Value> getPotentialIncomingValuesRes(Operation *op,
630 OpResult res)
const {
631 auto forOp = cast<affine::AffineForOp>(op);
633 forOp.getInits()[res.getResultNumber()],
634 forOp.getBody()->getTerminator()->getOperand(res.getResultNumber())};
636 SmallVector<Value> getPotentialIncomingValuesArg(Operation *op,
637 BlockArgument arg)
const {
638 auto forOp = cast<affine::AffineForOp>(op);
639 if (arg.getArgNumber() < 1) {
642 auto idx = arg.getArgNumber() - 1;
643 return {forOp.getInits()[idx],
644 forOp.getBody()->getTerminator()->getOperand(idx)};
648 auto forOp = cast<affine::AffineForOp>(op);
649 SmallVector<Value> sv;
651 for (
auto &&[res, arg, barg] :
652 llvm::zip_equal(forOp->getResults(), term->getOperands(),
653 forOp.getRegionIterArgs())) {
664struct AffineForOpEnzymeOpsRemover
666 affine::AffineForOp> {
669 static std::optional<int64_t>
670 getConstantNumberOfIterations(affine::AffineForOp forOp) {
671 if (!forOp.hasConstantLowerBound())
673 if (!forOp.hasConstantUpperBound())
675 return (forOp.getConstantUpperBound() - forOp.getConstantLowerBound()) /
676 forOp.getStepAsInt();
679 static SmallVector<IntOrValue, 1>
680 getDimensionBounds(OpBuilder &builder, affine::AffineForOp forOp) {
681 auto iters = getConstantNumberOfIterations(forOp);
683 return {IntOrValue(*iters)};
685 auto lb = AffineApplyOp::create(builder, forOp.getLoc(),
686 forOp.getLowerBoundMap(),
687 forOp.getLowerBoundOperands());
688 auto ub = AffineApplyOp::create(builder, forOp.getLoc(),
689 forOp.getUpperBoundMap(),
690 forOp.getUpperBoundOperands());
692 Value diff = arith::SubIOp::create(builder, forOp->getLoc(), ub, lb);
693 if (forOp.getStepAsInt() != 1) {
694 auto step = arith::ConstantIntOp::create(
695 builder, forOp->getLoc(), diff.getType(), forOp.getStepAsInt());
696 diff = arith::DivUIOp::create(builder, forOp->getLoc(), diff, step);
698 return {IntOrValue(diff)};
702 static SmallVector<Value> getCanonicalLoopIVs(OpBuilder &builder,
703 affine::AffineForOp forOp) {
704 Value val = forOp.getBody()->getArgument(0);
705 if (!forOp.hasConstantLowerBound() || forOp.getConstantLowerBound() != 0) {
706 auto lb = AffineApplyOp::create(builder, forOp.getLoc(),
707 forOp.getLowerBoundMap(),
708 forOp.getLowerBoundOperands());
709 val = arith::SubIOp::create(builder, forOp->getLoc(), val, lb);
712 if (forOp.getStepAsInt() != 1) {
713 auto step = arith::ConstantIntOp::create(
714 builder, forOp->getLoc(), val.getType(), forOp.getStepAsInt());
715 val = arith::DivUIOp::create(builder, forOp->getLoc(), val, step);
720 static IRMapping createArgumentMap(PatternRewriter &rewriter,
721 affine::AffineForOp forOp,
722 ArrayRef<Value> indFor,
723 affine::AffineForOp otherForOp,
724 ArrayRef<Value> indOther) {
726 for (
auto &&[f, o] : llvm::zip_equal(indFor, indOther))
729 Value canIdx = forOp.getBody()->getArgument(0);
730 if (!map.contains(canIdx)) {
731 assert(forOp.getLowerBoundMap() == otherForOp.getLowerBoundMap());
733 llvm::zip_equal(forOp.getLowerBoundOperands(),
734 otherForOp.getLowerBoundOperands())) {
737 assert(Equivalent(f, o));
739 assert(forOp.getStep() == otherForOp.getStep());
740 map.map(forOp.getBody()->getArgument(0),
741 otherForOp.getBody()->getArgument(0));
746 static affine::AffineForOp
747 replaceWithNewOperands(PatternRewriter &rewriter,
748 affine::AffineForOp otherForOp,
749 ArrayRef<Value> operands) {
750 auto newOtherForOp = affine::AffineForOp::create(
751 rewriter, otherForOp->getLoc(), otherForOp.getLowerBoundOperands(),
752 otherForOp.getLowerBoundMap(), otherForOp.getUpperBoundOperands(),
753 otherForOp.getUpperBoundMap(), otherForOp.getStepAsInt(), operands);
755 newOtherForOp.getRegion().takeBody(otherForOp.getRegion());
756 rewriter.replaceOp(otherForOp, newOtherForOp->getResults().slice(
757 0, otherForOp->getNumResults()));
758 return newOtherForOp;
761 static ValueRange getInits(affine::AffineForOp forOp) {
762 return forOp.getInits();
765 static bool mustPostAdd(affine::AffineForOp forOp) {
return false; }
767 static Value initialValueInBlock(OpBuilder &builder, Block *body,
769 auto Ty = cast<enzyme::GradientType>(grad.getType()).getBasetype();
770 return body->addArgument(Ty, grad.getLoc());
774#include "Implementations/AffineDerivatives.inc"
778 DialectRegistry ®istry) {
779 registry.addExtension(+[](MLIRContext *context, affine::AffineDialect *) {
780 registerInterfaces(context);
781 affine::AffineLoadOp::attachInterface<AffineLoadOpInterfaceReverse>(
783 affine::AffineStoreOp::attachInterface<AffineStoreOpInterfaceReverse>(
785 affine::AffineForOp::attachInterface<AffineForOpInterfaceReverse>(*context);
786 affine::AffineForOp::attachInterface<AffineForOpEnzymeOpsRemover>(*context);
787 affine::AffineForOp::attachInterface<AffineForOpADDataFlow>(*context);
788 affine::AffineParallelOp::attachInterface<AffineParallelOpInterfaceReverse>(
790 affine::AffineParallelOp::attachInterface<AffineParallelOpEnzymeOpsRemover>(
static std::optional< SmallVector< Value > > getPotentialTerminatorUsers(Operation *op, Value parent)
void setDiffe(mlir::Value origv, mlir::Value newv, mlir::OpBuilder &builder)
mlir::Value diffe(mlir::Value origv, mlir::OpBuilder &builder)
void zeroDiffe(mlir::Value origv, mlir::OpBuilder &builder)
LogicalResult visitChild(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils)
Value popCache(Value cache, OpBuilder &builder)
Value initAndPushCache(Value v, OpBuilder &builder)
void addToDiffe(mlir::Value oldGradient, mlir::Value addedGradient, OpBuilder &builder)
mlir::Type getShadowType(mlir::Type T)
mlir::Value invertPointerM(mlir::Value v, OpBuilder &Builder2)
SmallVector< mlir::Value, 1 > getNewFromOriginal(ValueRange originst) const
bool isConstantValue(mlir::Value v) const
void computeAffineIndices(OpBuilder &builder, Location loc, AffineMap map, ValueRange operands, SmallVectorImpl< Value > &indices)
void localizeGradients(OpBuilder &builder, MGradientUtilsReverse *gutils, Block *fwd)
void registerAffineDialectAutoDiffInterface(DialectRegistry ®istry)