22#include "mlir/Dialect/Affine/IR/AffineOps.h"
23#include "mlir/Dialect/Affine/Utils.h"
24#include "mlir/Dialect/MemRef/IR/MemRef.h"
25#include "mlir/Dialect/SCF/IR/SCF.h"
26#include "mlir/IR/IntegerSet.h"
27#include "llvm/ADT/ScopeExit.h"
38createAffineForWithShadows(Operation *op, OpBuilder &builder,
40 ValueRange remappedOperands, TypeRange rettys) {
41 affine::AffineForOpAdaptor adaptor(remappedOperands,
42 cast<affine::AffineForOp>(original));
43 auto repFor = affine::AffineForOp::create(
44 builder, original->getLoc(), adaptor.getLowerBoundOperands(),
45 adaptor.getLowerBoundMap(), adaptor.getUpperBoundOperands(),
46 adaptor.getUpperBoundMap(), adaptor.getStep().getZExtValue(),
54 remappedOperands.drop_front(adaptor.getLowerBoundOperands().size() +
55 adaptor.getUpperBoundOperands().size()));
59affine::AffineIfOp createAffineIfWithShadows(Operation *op, OpBuilder &builder,
61 affine::AffineIfOp original,
62 ValueRange remappedOperands,
64 affine::AffineIfOpAdaptor adaptor(remappedOperands, original);
65 return affine::AffineIfOp::create(
66 builder, original->getLoc(), rettys, original.getIntegerSet(),
67 adaptor.getOperands(), !original.getElseRegion().empty());
70struct AffineIfOpInterfaceReverse
71 :
public ReverseAutoDiffOpInterface::ExternalModel<
72 AffineIfOpInterfaceReverse, affine::AffineIfOp> {
73 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
75 SmallVector<Value> caches)
const {
76 auto ifOp = cast<affine::AffineIfOp>(op);
77 bool hasElse = ifOp.hasElse();
78 Value cond = gutils->
popCache(caches[0], builder);
80 SmallVector<bool> resultsActive(ifOp.getNumResults(),
false);
81 for (
int i = 0, e = resultsActive.size(); i < e; ++i) {
82 auto result = ifOp.getResult(i);
83 auto iface = dyn_cast<AutoDiffTypeInterface>(result.getType());
84 bool needsGrad = iface && !iface.isMutable();
88 SmallVector<Value> incomingGradients;
89 for (
auto &&[active, res] :
90 llvm::zip_equal(resultsActive, ifOp.getResults())) {
92 incomingGradients.push_back(gutils->
diffe(res, builder));
99 scf::IfOp::create(builder, ifOp.getLoc(), TypeRange{}, cond, hasElse);
102 for (
auto &&[oldReg, newReg] :
103 llvm::zip(op->getRegions(), revIf->getRegions())) {
104 for (
auto &&[oBB, revBB] : llvm::zip(oldReg, newReg)) {
105 OpBuilder bodyBuilder(&revBB, revBB.end());
106 bodyBuilder.setInsertionPoint(revBB.getTerminator());
112 for (
auto &it : oBB.getOperations()) {
113 for (
auto res : it.getResults()) {
115 auto iface = dyn_cast<AutoDiffTypeInterface>(res.getType());
116 if (iface && !iface.isMutable())
122 auto term = oBB.getTerminator();
124 SmallVector<Value> activeTermOperands;
125 activeTermOperands.reserve(incomingGradients.size());
126 for (
auto &&[resultActive, operand] :
127 llvm::zip_equal(resultsActive, term->getOperands())) {
129 activeTermOperands.push_back(operand);
132 for (
auto &&[arg, operand] :
133 llvm::zip_equal(incomingGradients, activeTermOperands)) {
139 gutils->
addToDiffe(operand, arg, bodyBuilder);
143 auto first = oBB.rbegin();
146 auto last = oBB.rend();
148 for (
auto it = first; it != last; ++it) {
149 Operation *op = &*it;
155 return success(valid);
158 SmallVector<Value> cacheValues(Operation *op,
160 auto ifOp = cast<affine::AffineIfOp>(op);
161 auto is = ifOp.getCondition();
164 OpBuilder cacheBuilder(newOp);
166 Value cond =
nullptr;
168 Value zero = arith::ConstantIndexOp::create(cacheBuilder, ifOp.getLoc(), 0);
170 auto getNewFromOriginal = [&](Value v) {
174 SmallVector<Value> dims = llvm::map_to_vector(
175 ifOp->getOperands().take_front(is.getNumDims()), getNewFromOriginal);
176 SmallVector<Value> symbols = llvm::map_to_vector(
177 ifOp->getOperands().drop_front(is.getNumDims()), getNewFromOriginal);
179 for (
auto [eq, E] : llvm::zip_equal(is.getEqFlags(), is.getConstraints())) {
180 Value lhs = affine::expandAffineExpr(cacheBuilder, ifOp.getLoc(), E, dims,
182 Value constraintCond = arith::CmpIOp::create(
183 cacheBuilder, ifOp.getLoc(),
184 eq ? arith::CmpIPredicate::eq : arith::CmpIPredicate::sge, lhs, zero);
187 cond = arith::AndIOp::create(cacheBuilder, ifOp.getLoc(),
188 constraintCond, cond);
190 cond = constraintCond;
195 : arith::ConstantIntOp::create(cacheBuilder, ifOp.getLoc(),
196 cacheBuilder.getI1Type(),
202 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
211struct AffineIfOpEnzymeOpsRemover
213 affine::AffineIfOp, scf::IfOp> {
214 static Block *getThenBlock(affine::AffineIfOp ifOp, OpBuilder &builder) {
215 return ifOp.getThenBlock();
218 static Block *getElseBlock(affine::AffineIfOp ifOp, OpBuilder &builder) {
220 if (ifOp.getElseRegion().empty()) {
221 OpBuilder::InsertionGuard guard(builder);
222 Block &newBlock = ifOp.getElseRegion().emplaceBlock();
223 builder.setInsertionPointToStart(&newBlock);
224 affine::AffineYieldOp::create(builder, ifOp.getLoc());
227 return ifOp.getElseBlock();
230 static Block *getThenBlock(scf::IfOp ifOp, OpBuilder &builder) {
231 return ifOp.thenBlock();
234 static Block *getElseBlock(scf::IfOp ifOp, OpBuilder &builder) {
236 if (ifOp.getElseRegion().empty()) {
237 OpBuilder::InsertionGuard guard(builder);
238 Block &newBlock = ifOp.getElseRegion().emplaceBlock();
239 builder.setInsertionPointToStart(&newBlock);
240 scf::YieldOp::create(builder, ifOp.getLoc());
243 return ifOp.elseBlock();
246 static Value getDummyValue(OpBuilder &builder, Location loc, Type dummyType) {
247 return cast<AutoDiffTypeInterface>(dummyType).createNullValue(builder, loc);
250 static affine::AffineIfOp replace(PatternRewriter &rewriter,
251 affine::AffineIfOp otherIfOp,
252 TypeRange resultTypes) {
253 auto newIf = affine::AffineIfOp::create(
254 rewriter, otherIfOp->getLoc(), resultTypes, otherIfOp.getIntegerSet(),
255 otherIfOp->getOperands(),
true);
257 newIf.getThenRegion().takeBody(otherIfOp.getThenRegion());
258 newIf.getElseRegion().takeBody(otherIfOp.getElseRegion());
260 rewriter.replaceAllUsesWith(
261 otherIfOp->getResults(),
262 newIf->getResults().slice(0, otherIfOp->getNumResults()));
263 rewriter.eraseOp(otherIfOp);
268affine::AffineParallelOp
269createAffineParallelWithShadows(Operation *op, OpBuilder &builder,
271 affine::AffineParallelOp original,
272 ValueRange remappedOperands, TypeRange rettys) {
276 SmallVector<Attribute> reductions;
277 for (
auto &&[reduction, result] :
278 llvm::zip_equal(original.getReductions(), original.getResults())) {
279 reductions.push_back(reduction);
282 auto kind = cast<arith::AtomicRMWKindAttr>(reduction).getValue();
283 if (kind != arith::AtomicRMWKind::addf &&
284 kind != arith::AtomicRMWKind::addi)
285 original.emitError() <<
"forward mode of an active "
286 << stringifyEnum(kind)
287 <<
" reduction is not yet implemented";
288 reductions.push_back(reduction);
290 auto reductionsAttr = builder.getArrayAttr(reductions);
291 return affine::AffineParallelOp::create(
292 builder, original->getLoc(), rettys, reductionsAttr,
293 original.getLowerBoundsMapAttr(), original.getLowerBoundsGroupsAttr(),
294 original.getUpperBoundsMapAttr(), original.getUpperBoundsGroupsAttr(),
295 original.getStepsAttr(), remappedOperands);
298struct AffineForOpInterfaceReverse
299 :
public ReverseAutoDiffOpInterface::ExternalModel<
300 AffineForOpInterfaceReverse, affine::AffineForOp>,
302 affine::AffineForOp> {
306 static std::optional<int64_t>
307 getConstantNumberOfIterations(affine::AffineForOp forOp) {
308 if (!forOp.hasConstantLowerBound())
310 if (!forOp.hasConstantUpperBound())
312 return (forOp.getConstantUpperBound() - forOp.getConstantLowerBound()) /
313 forOp.getStepAsInt();
316 static Value materializeLowerBound(OpBuilder &builder, Location loc,
317 affine::AffineForOp forOp,
319 SmallVector<Value> ops =
320 llvm::map_to_vector(forOp.getLowerBoundOperands(), [&](Value v) {
321 return gutils->getNewFromOriginal(v);
323 return affine::AffineApplyOp::create(builder, loc, forOp.getLowerBoundMap(),
327 static Value materializeUpperBound(OpBuilder &builder, Location loc,
328 affine::AffineForOp forOp,
330 SmallVector<Value> ops =
331 llvm::map_to_vector(forOp.getUpperBoundOperands(), [&](Value v) {
332 return gutils->getNewFromOriginal(v);
334 return affine::AffineApplyOp::create(builder, loc, forOp.getUpperBoundMap(),
338 static Value materializeStep(OpBuilder &builder, Location loc,
339 affine::AffineForOp forOp,
341 return arith::ConstantIndexOp::create(builder, loc, forOp.getStepAsInt());
344 static int64_t getConstantStart(affine::AffineForOp forOp) {
345 return forOp.getConstantLowerBound();
348 static int64_t getConstantStep(affine::AffineForOp forOp) {
349 return forOp.getStepAsInt();
352 static LogicalResult requireSingleResultBounds(affine::AffineForOp forOp) {
353 if (forOp.getLowerBound().getMap().getNumResults() != 1 ||
354 forOp.getUpperBound().getMap().getNumResults() != 1)
355 return forOp->emitError()
356 <<
"cannot differentiate loop with minmax bounds yet";
369 static void cloneOp(OpBuilder &builder, Operation &op, IRMapping &mapping) {
370 if (
auto loadOp = dyn_cast<affine::AffineLoadOp>(&op)) {
371 SmallVector<Value> operands =
372 llvm::map_to_vector(loadOp.getIndices(), [&](Value v) {
373 return mapping.lookupOrDefault(v);
375 auto indices = affine::expandAffineMap(builder, loadOp.getLoc(),
376 loadOp.getAffineMap(), operands);
377 assert(indices &&
"failed to expand affine.load's index map");
378 Value memref = mapping.lookupOrDefault(loadOp.getMemref());
380 memref::LoadOp::create(builder, loadOp.getLoc(), memref, *indices);
381 newLoadOp.setAlignmentAttr(
382 loadOp->getAttrOfType<IntegerAttr>(
"alignment"));
383 Operation *newLoad = newLoadOp;
384 mapping.map(loadOp.getResult(), newLoad->getResult(0));
391 mapping.map(&op, newLoad);
394 if (
auto storeOp = dyn_cast<affine::AffineStoreOp>(&op)) {
395 SmallVector<Value> operands =
396 llvm::map_to_vector(storeOp.getIndices(), [&](Value v) {
397 return mapping.lookupOrDefault(v);
399 auto indices = affine::expandAffineMap(builder, storeOp.getLoc(),
400 storeOp.getAffineMap(), operands);
401 assert(indices &&
"failed to expand affine.store's index map");
402 Value memref = mapping.lookupOrDefault(storeOp.getMemref());
403 Value value = mapping.lookupOrDefault(storeOp.getValue());
404 auto newStoreOp = memref::StoreOp::create(builder, storeOp.getLoc(),
405 value, memref, *indices);
406 newStoreOp.setAlignmentAttr(
407 storeOp->getAttrOfType<IntegerAttr>(
"alignment"));
408 Operation *newStore = newStoreOp;
409 mapping.map(&op, newStore);
412 builder.clone(op, mapping);
424 static bool supportsDynamicPeriodic() {
return false; }
426 static affine::AffineForOp
427 createConstantScaffoldLoop(OpBuilder &builder, Location loc, int64_t lb,
428 int64_t ub, int64_t step, ValueRange inits) {
429 return affine::AffineForOp::create(builder, loc, lb, ub, step, inits);
435 static affine::AffineForOp
436 createForwardOuterLoop(OpBuilder &builder, Location loc,
437 const PeriodicSchedule &sched, ValueRange inits) {
438 return createConstantScaffoldLoop(builder, loc, 0, sched.numSegments(), 1,
442 static affine::AffineForOp
443 createReverseOuterLoop(OpBuilder &builder, Location loc,
444 const PeriodicSchedule &sched, ValueRange inits) {
445 return createConstantScaffoldLoop(builder, loc, 0, sched.numSegments(), 1,
449 static void materializeSegmentValues(OpBuilder &, Location,
450 PeriodicSchedule &sched) {
451 assert(!sched.isDynamic() &&
452 "affine.for has no dynamic periodic path -- see "
453 "supportsDynamicPeriodic");
456 static SmallVector<Value>
457 computeForwardSegmentHint(OpBuilder &, Location, Value,
458 const PeriodicSchedule &) {
467 static affine::AffineForOp
468 createForwardSegmentLoop(OpBuilder &builder, Location loc, Value outerIV,
470 const PeriodicSchedule &sched, ValueRange inits) {
471 int64_t nInner = sched.nInner, nOuter = sched.nOuter,
472 trailingIters = sched.trailingIters;
473 int64_t numIters = nInner * nOuter + trailingIters;
474 MLIRContext *ctx = builder.getContext();
475 AffineExpr j = builder.getAffineDimExpr(0);
476 AffineExpr nInnerExpr = builder.getAffineConstantExpr(nInner);
477 AffineExpr remainingExpr =
478 builder.getAffineConstantExpr(numIters) - nInnerExpr * j;
479 AffineMap ubMap = AffineMap::get(1, 0, {nInnerExpr, remainingExpr}, ctx);
480 return affine::AffineForOp::create(
481 builder, loc, ValueRange{},
482 AffineMap::getConstantMap(0, ctx), ValueRange{outerIV}, ubMap,
496 static affine::AffineForOp
497 createReverseSegmentLoop(OpBuilder &builder, Location loc, Value outerIV,
499 const PeriodicSchedule &sched, ValueRange inits) {
500 int64_t nInner = sched.nInner, nOuter = sched.nOuter,
501 trailingIters = sched.trailingIters;
502 int64_t numIters = nInner * nOuter + trailingIters;
503 int64_t s = nOuter + (trailingIters > 0 ? 1 : 0);
506 int64_t constOffset = numIters - (s - 1) * nInner;
507 MLIRContext *ctx = builder.getContext();
508 AffineExpr j = builder.getAffineDimExpr(0);
509 AffineExpr nInnerExpr = builder.getAffineConstantExpr(nInner);
510 AffineExpr remainingExpr =
511 builder.getAffineConstantExpr(constOffset) + nInnerExpr * j;
512 AffineMap ubMap = AffineMap::get(1, 0, {nInnerExpr, remainingExpr}, ctx);
513 return affine::AffineForOp::create(
514 builder, loc, ValueRange{},
515 AffineMap::getConstantMap(0, ctx), ValueRange{outerIV}, ubMap,
519 static affine::AffineForOp
520 createLoopWithSameBounds(OpBuilder &builder, Location loc,
521 affine::AffineForOp templateLoop, ValueRange inits) {
522 return affine::AffineForOp::create(
523 builder, loc, templateLoop.getLowerBoundOperands(),
524 templateLoop.getLowerBoundMap(), templateLoop.getUpperBoundOperands(),
525 templateLoop.getUpperBoundMap(), templateLoop.getStepAsInt(), inits);
533 static Value computeForwardSegmentIV(OpBuilder &builder, Location loc,
534 affine::AffineForOp forOp, Value outerIV,
536 const PeriodicSchedule &sched,
538 int64_t step = getConstantStep(forOp);
539 int64_t start = getConstantStart(forOp);
540 MLIRContext *ctx = builder.getContext();
541 AffineExpr j = builder.getAffineDimExpr(0),
542 localIdx = builder.getAffineDimExpr(1);
543 AffineExpr expr = (j * sched.nInner + localIdx) * step + start;
544 AffineMap map = AffineMap::get(2, 0, {expr}, ctx);
545 return affine::AffineApplyOp::create(builder, loc, map,
546 ValueRange{outerIV, localIV});
551 static SmallVector<Value>
552 computeReverseSegmentHint(OpBuilder &builder, Location loc, Value outerIV,
553 const PeriodicSchedule &) {
557 static Value computeReverseSegmentIV(OpBuilder &builder, Location loc,
558 affine::AffineForOp forOp, Value outerIV,
560 const PeriodicSchedule &sched,
562 int64_t nInner = sched.nInner;
563 int64_t s = sched.nOuter + (sched.trailingIters > 0 ? 1 : 0);
564 int64_t lastSegmentBase = (s - 1) * nInner;
565 int64_t step = getConstantStep(forOp);
566 int64_t start = getConstantStart(forOp);
567 MLIRContext *ctx = builder.getContext();
568 AffineExpr j = builder.getAffineDimExpr(0),
569 localIdx = builder.getAffineDimExpr(1);
570 AffineExpr segmentBase = lastSegmentBase - j * nInner;
571 AffineExpr flatIV = segmentBase + localIdx;
572 AffineExpr expr = flatIV * step + start;
573 AffineMap map = AffineMap::get(2, 0, {expr}, ctx);
574 return affine::AffineApplyOp::create(builder, loc, map,
575 ValueRange{outerIV, localIV});
578 static void createScaffoldYield(OpBuilder &builder, Location loc,
579 ValueRange operands) {
580 affine::AffineYieldOp::create(builder, loc, operands);
583 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
585 SmallVector<Value> caches)
const {
586 auto forOp = cast<affine::AffineForOp>(op);
588 affine::AffineBound lb = forOp.getLowerBound();
589 affine::AffineBound ub = forOp.getUpperBound();
591 if (lb.getMap().getNumResults() != 1 || ub.getMap().getNumResults() != 1) {
592 op->emitError() <<
"cannot differentiate loop with minmax bounds yet";
596 SmallVector<bool> operandsActive;
597 for (
auto [operand, result] : llvm::zip_equal(
598 op->getOperands().slice(forOp.getNumControlOperands(),
599 forOp->getNumOperands() -
600 forOp.getNumControlOperands()),
611 if (needsBinomialCheckpointing(forOp) || needsCheckpointing(forOp)) {
612 SmallVector<Value> incomingGradients;
613 for (
auto &&[active, res] :
614 llvm::zip_equal(operandsActive, op->getResults())) {
616 incomingGradients.push_back(gutils->
diffe(res, builder));
622 tryCreateReverseModeAdjoint(forOp, op, builder, gutils, caches,
623 operandsActive, incomingGradients))
629 SmallVector<Value> revLBOperands, revUBOperands, incomingGradients;
631 for (
int i = 0, e = lb.getNumOperands(); i < e; ++i) {
632 revLBOperands.push_back(gutils->
popCache(caches[i], builder));
635 for (
int i = lb.getNumOperands(), e = forOp.getNumControlOperands(); i < e;
637 revUBOperands.push_back(gutils->
popCache(caches[i], builder));
640 for (
auto &&[active, res] :
641 llvm::zip_equal(operandsActive, op->getResults())) {
643 incomingGradients.push_back(gutils->
diffe(res, builder));
649 auto revFor = affine::AffineForOp::create(
650 builder, op->getLoc(), revLBOperands, lb.getMap(), revUBOperands,
651 ub.getMap(), forOp.getStepAsInt(), incomingGradients);
652 preserveAttributesButCheckpointing(revFor, op);
655 for (
auto &&[oldReg, newReg] :
656 llvm::zip(op->getRegions(), revFor->getRegions())) {
657 for (
auto &&[oBB, revBB] : llvm::zip(oldReg, newReg)) {
658 OpBuilder bodyBuilder(&revBB, revBB.end());
662 affine::AffineYieldOp::create(bodyBuilder, revFor->getLoc());
664 bodyBuilder.setInsertionPoint(revBB.getTerminator());
669 for (
auto operand : oBB.getArguments().slice(1)) {
675 for (
auto &it : oBB.getOperations()) {
676 for (
auto res : it.getResults()) {
678 auto iface = dyn_cast<AutoDiffTypeInterface>(res.getType());
679 if (iface && !iface.isMutable())
685 auto term = oBB.getTerminator();
688 for (
auto &&[active, operand] :
689 llvm::zip_equal(operandsActive, term->getOperands())) {
694 gutils->
setDiffe(operand, revBB.getArgument(argIdx), bodyBuilder);
699 auto first = oBB.rbegin();
702 auto last = oBB.rend();
704 for (
auto it = first; it != last; ++it) {
705 Operation *op = &*it;
710 SmallVector<Value> newResults;
711 newResults.reserve(incomingGradients.size());
713 for (
auto &&[active, arg] :
714 llvm::zip_equal(operandsActive, oBB.getArguments().slice(1))) {
716 newResults.push_back(gutils->
diffe(arg, bodyBuilder));
723 revBB.getTerminator()->setOperands(newResults);
728 for (
auto &&[active, arg] :
729 llvm::zip_equal(operandsActive, forOp.getInits())) {
732 gutils->
addToDiffe(arg, revFor.getResult(resIdx), builder);
738 return success(valid);
741 SmallVector<Value> cacheValues(Operation *op,
743 auto forOp = cast<affine::AffineForOp>(op);
745 if (
auto r = tryCacheValues(forOp, op, gutils))
748 SmallVector<Value> caches;
750 for (
auto operand : forOp.getControlOperands()) {
758 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
764struct AffineParallelOpInterfaceReverse
765 :
public ReverseAutoDiffOpInterface::ExternalModel<
766 AffineParallelOpInterfaceReverse, affine::AffineParallelOp> {
767 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
769 SmallVector<Value> caches)
const {
770 auto parOp = cast<affine::AffineParallelOp>(op);
771 if (!parOp.getReductions().empty()) {
772 return parOp.emitError() <<
"parallel reductions not yet implemented";
774 if (parOp.hasMinMaxBounds()) {
775 return parOp.emitError() <<
"minmax bounds not yet supported";
778 SmallVector<Value> bounds = llvm::map_to_vector(
779 caches, [&](Value cache) {
return gutils->
popCache(cache, builder); });
780 auto revPar = affine::AffineParallelOp::create(
781 builder, op->getLoc(), parOp.getResultTypes(), parOp.getReductions(),
782 parOp.getLowerBoundsMap(), parOp.getLowerBoundsGroups(),
783 parOp.getUpperBoundsMap(), parOp.getUpperBoundsGroups(),
784 parOp.getSteps(), bounds);
787 OpBuilder::InsertionGuard guard(builder);
788 SmallVector<Type> ivTypes(parOp.getIVs().size(), builder.getIndexType());
789 SmallVector<Location> ivLocs(parOp.getIVs().size(), parOp.getLoc());
790 builder.createBlock(&revPar.getBodyRegion(), revPar.getBodyRegion().begin(),
792 affine::AffineYieldOp::create(builder, parOp.getLoc());
799 Block *oBB = parOp.getBody();
800 Block *rBB = revPar.getBody();
802 OpBuilder bodyBuilder = revPar.getBodyBuilder();
804 bodyBuilder.setInsertionPointToStart(revPar.getBody());
807 bodyBuilder.setInsertionPoint(rBB->getTerminator());
809 auto first = oBB->rbegin();
812 auto last = oBB->rend();
814 for (
auto it = first; it != last; ++it) {
815 Operation *op = &*it;
816 valid &= gutils->
Logic.
visitChild(op, bodyBuilder, gutils).succeeded();
821 return success(valid);
824 SmallVector<Value> cacheValues(Operation *op,
826 auto parOp = cast<affine::AffineParallelOp>(op);
828 SmallVector<Value> caches;
830 for (
auto operand : parOp.getMapOperands()) {
837 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
843struct AffineParallelOpEnzymeOpsRemover
845 affine::AffineParallelOp> {
846 static SmallVector<IntOrValue, 1>
847 getDimensionBounds(OpBuilder &builder, affine::AffineParallelOp parOp) {
848 SmallVector<IntOrValue, 1> bounds;
849 auto ranges = parOp.getConstantRanges();
851 for (
auto &&[r, step] : llvm::zip(*ranges, parOp.getSteps())) {
852 bounds.push_back(r / step);
855 for (
auto &&[dim, step] : llvm::enumerate(parOp.getSteps())) {
856 auto lb = AffineApplyOp::create(builder, parOp.getLoc(),
857 parOp.getLowerBoundMap(dim),
858 parOp.getLowerBoundsOperands());
859 auto ub = AffineApplyOp::create(builder, parOp.getLoc(),
860 parOp.getUpperBoundMap(dim),
861 parOp.getUpperBoundsOperands());
862 Value diff = arith::SubIOp::create(builder, parOp.getLoc(), ub, lb);
865 arith::ConstantIndexOp::create(builder, parOp.getLoc(), step);
866 diff = arith::DivUIOp::create(builder, parOp.getLoc(), diff, stepVal);
868 bounds.push_back(diff);
874 static SmallVector<Value> computeReversedIndices(
875 PatternRewriter &rewriter, affine::AffineParallelOp parOp,
876 ArrayRef<Value> otherInductionVariable, ArrayRef<IntOrValue> bounds) {
877 return SmallVector<Value>(otherInductionVariable);
880 static SmallVector<Value>
881 getCanonicalLoopIVs(OpBuilder &builder, affine::AffineParallelOp parOp) {
882 SmallVector<Value> ivs(parOp.getIVs());
883 for (
auto &&[dim, step] : llvm::enumerate(parOp.getSteps())) {
885 auto lbMap = parOp.getLowerBoundMap(dim);
886 if (!(lbMap.isSingleConstant() && lbMap.getSingleConstantResult() == 0)) {
887 auto lb = AffineApplyOp::create(builder, parOp.getLoc(), lbMap,
888 parOp.getLowerBoundsOperands());
889 iv = arith::SubIOp::create(builder, parOp.getLoc(), iv, lb);
894 arith::ConstantIndexOp::create(builder, parOp.getLoc(), step);
895 iv = arith::DivUIOp::create(builder, parOp.getLoc(), iv, stepVal);
903 static IRMapping createArgumentMap(PatternRewriter &rewriter,
904 affine::AffineParallelOp parOp,
905 ArrayRef<Value> indPar,
906 affine::AffineParallelOp otherParOp,
907 ArrayRef<Value> indOther) {
909 for (
auto &&[f, o] : llvm::zip_equal(indPar, indOther))
912 for (
auto &&[fiv, oiv] :
913 llvm::zip_equal(parOp.getIVs(), otherParOp.getIVs())) {
914 if (!map.contains(fiv)) {
915 assert(parOp.getLowerBoundsMap() == otherParOp.getLowerBoundsMap());
917 llvm::zip_equal(parOp.getLowerBoundsOperands(),
918 otherParOp.getLowerBoundsOperands())) {
921 assert(Equivalent(f, o));
923 for (
auto [fstep, ostep] :
924 llvm::zip_equal(parOp.getSteps(), otherParOp.getSteps())) {
927 assert(fstep == ostep);
935 static affine::AffineParallelOp
936 replaceWithNewOperands(PatternRewriter &rewriter,
937 affine::AffineParallelOp otherParOp,
938 ArrayRef<Value> operands) {
939 SmallVector<mlir::Attribute> reductionKinds(
940 otherParOp.getReductions().begin(), otherParOp.getReductions().end());
942 for (
unsigned i = otherParOp->getNumOperands(); i < operands.size(); i++) {
943 reductionKinds.push_back(arith::AtomicRMWKindAttr::get(
944 otherParOp.getContext(), arith::AtomicRMWKind::addf));
947 ValueRange operands_(operands);
948 auto newOtherParOp = affine::AffineParallelOp::create(
949 rewriter, otherParOp.getLoc(), operands_.getTypes(),
950 ArrayAttr::get(otherParOp.getContext(), reductionKinds),
951 otherParOp.getLowerBoundsMap(), otherParOp.getLowerBoundsGroups(),
952 otherParOp.getUpperBoundsMap(), otherParOp.getUpperBoundsGroups(),
953 otherParOp.getSteps(), otherParOp.getMapOperands());
955 newOtherParOp->setDiscardableAttrs(
956 otherParOp->getDiscardableAttrDictionary());
958 newOtherParOp.getRegion().takeBody(otherParOp.getRegion());
959 rewriter.replaceOp(otherParOp, newOtherParOp->getResults().slice(
960 0, otherParOp->getNumResults()));
961 return newOtherParOp;
964 static ValueRange getInits(affine::AffineParallelOp parOp) {
965 return parOp.getInits();
968 static bool mustPostAdd(affine::AffineParallelOp forOp) {
return true; }
970 static Value initialValueInBlock(OpBuilder &builder, Block *body,
972 OpBuilder::InsertionGuard guard(builder);
973 builder.setInsertionPointToStart(body);
974 return cast<AutoDiffTypeInterface>(
975 cast<enzyme::GradientType>(grad.getType()).getBasetype())
976 .createNullValue(builder, grad.getLoc());
980struct AffineParallelRegionBranchOpInterface
981 :
public mlir::RegionBranchOpInterface::FallbackModel<
982 AffineParallelRegionBranchOpInterface> {
983 OperandRange getEntrySuccessorOperands(Operation *op,
984 RegionSuccessor successor)
const {
985 return cast<AffineParallelOp>(op).getInits();
988 void getEntrySuccessorRegions(
989 Operation *op, ::llvm::ArrayRef<::mlir::Attribute> operands,
990 ::llvm::SmallVectorImpl<::mlir::RegionSuccessor> ®ions)
const {
991 getSuccessorRegions(op, RegionBranchPoint::parent(), regions);
994 void getSuccessorRegions(
995 Operation *op, RegionBranchPoint point,
996 ::llvm::SmallVectorImpl<::mlir::RegionSuccessor> ®ions)
const {
1000 regions.push_back(RegionSuccessor(&cast<AffineParallelOp>(op).getRegion()));
1001 regions.push_back(RegionSuccessor(op));
1004 void getSuccessorRegions(
1005 Operation *op, ::mlir::Region ®ion,
1006 ::llvm::SmallVectorImpl<::mlir::RegionSuccessor> ®ions)
const {
1007 for (::mlir::Block &block : region) {
1010 if (
auto terminator =
1011 dyn_cast<RegionBranchTerminatorOpInterface>(block.back()))
1012 getSuccessorRegions(op, RegionBranchPoint(terminator), regions);
1016 void getRegionInvocationBounds(
1017 Operation *op, ArrayRef<Attribute> operands,
1018 SmallVectorImpl<InvocationBounds> &invocationBounds)
const {
1019 invocationBounds.append(op->getNumRegions(),
1020 InvocationBounds::getUnknown());
1023 bool areTypesCompatible(Operation *op, Type lhs, Type rhs)
const {
1027 ValueRange getSuccessorInputs(Operation *op,
1028 RegionSuccessor successor)
const {
1029 auto parOp = cast<affine::AffineParallelOp>(op);
1030 return successor.isOperation() ? ValueRange(parOp.getResults())
1031 : ValueRange(parOp.getRegionIterArgs());
1034 void getPredecessorValues(Operation *op, RegionSuccessor successor,
int index,
1035 SmallVectorImpl<Value> &predecessorValues)
const {}
1037 void getPredecessors(Operation *op, RegionSuccessor successor,
1038 SmallVectorImpl<RegionBranchPoint> &predecessors)
const {
1042struct AffineLoadOpInterfaceReverse
1043 :
public ReverseAutoDiffOpInterface::ExternalModel<
1044 AffineLoadOpInterfaceReverse, affine::AffineLoadOp> {
1045 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
1047 SmallVector<Value> caches)
const {
1048 auto loadOp = cast<affine::AffineLoadOp>(op);
1049 Value memref = loadOp.getMemref();
1051 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
1057 Value gradient = gutils->
diffe(loadOp, builder);
1058 Value memrefGradient = gutils->
popCache(caches.front(), builder);
1060 SmallVector<Value> retrievedArguments;
1061 for (Value cache : ValueRange(caches).drop_front(1)) {
1062 Value retrievedValue = gutils->
popCache(cache, builder);
1063 retrievedArguments.push_back(retrievedValue);
1067 auto alignAttr = loadOp->getAttrOfType<IntegerAttr>(
"alignment");
1068 bool hasIndex = loadOp.getAffineMap().getNumDims() > 0;
1071 SmallVector<Value> indices;
1073 loadOp.getAffineMap(), retrievedArguments,
1076 auto loadedGradientOp = memref::LoadOp::create(
1077 builder, loadOp.getLoc(), memrefGradient, indices);
1078 loadedGradientOp.setAlignmentAttr(alignAttr);
1079 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
1080 loadedGradientOp, gradient);
1081 auto storeGradientOp =
1082 memref::StoreOp::create(builder, loadOp.getLoc(), addedGradient,
1083 memrefGradient, indices);
1084 storeGradientOp.setAlignmentAttr(alignAttr);
1086 auto loadedGradientOp = affine::AffineLoadOp::create(
1087 builder, loadOp.getLoc(), memrefGradient, loadOp.getAffineMap(),
1088 ArrayRef<Value>(retrievedArguments));
1090 loadedGradientOp->setAttr(
"alignment", alignAttr);
1091 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
1092 loadedGradientOp, gradient);
1093 auto storeGradientOp = affine::AffineStoreOp::create(
1094 builder, loadOp.getLoc(), addedGradient, memrefGradient,
1095 loadOp.getAffineMap(), ArrayRef<Value>(retrievedArguments));
1097 storeGradientOp->setAttr(
"alignment", alignAttr);
1100 bool hasIndex = loadOp.getAffineMap().getNumDims() > 0;
1101 auto alignAttr = loadOp->getAttrOfType<IntegerAttr>(
"alignment");
1104 SmallVector<Value> indices;
1106 loadOp.getAffineMap(), retrievedArguments,
1109 builder, loadOp.getLoc(), gradient.getType(),
1110 arith::AtomicRMWKind::addf, Ordering::monotonic, gradient,
1111 memrefGradient, indices, alignAttr));
1114 builder, loadOp.getLoc(), gradient.getType(),
1115 arith::AtomicRMWKind::addf, Ordering::monotonic, gradient,
1116 memrefGradient, retrievedArguments, loadOp.getAffineMap(),
1125 SmallVector<Value> cacheValues(Operation *op,
1127 auto loadOp = cast<affine::AffineLoadOp>(op);
1128 Value memref = loadOp.getMemref();
1129 ValueRange indices = loadOp.getIndices();
1130 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
1134 SmallVector<Value> caches;
1137 for (Value v : indices) {
1144 return SmallVector<Value>();
1147 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
1149 auto loadOp = cast<affine::AffineLoadOp>(op);
1150 Value memref = loadOp.getMemref();
1151 auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType());
1157 return op->emitError()
1158 <<
"could not compute the shadow of a load of a type without "
1159 "autodiff semantics "
1162 if (!iface.isMutable())
1170 return op->emitError()
1171 <<
"cannot load a non-constant value out of a constant memref "
1175 auto shadowLoad = cast<affine::AffineLoadOp>(builder.clone(*newLoad));
1176 shadowLoad.getMemrefMutable().assign(memrefShadow);
1185struct AffineStoreLike
1186 :
public StoreLikeInterface::ExternalModel<AffineStoreLike,
1187 affine::AffineStoreOp> {
1188 Value getStoredValue(Operation *op)
const {
1189 return cast<affine::AffineStoreOp>(op).getValueToStore();
1191 Value getStoredPointer(Operation *op)
const {
1192 return cast<affine::AffineStoreOp>(op).getMemRef();
1196struct AffineStoreOpInterfaceReverse
1197 :
public ReverseAutoDiffOpInterface::ExternalModel<
1198 AffineStoreOpInterfaceReverse, affine::AffineStoreOp> {
1199 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
1201 SmallVector<Value> caches)
const {
1202 auto storeOp = cast<affine::AffineStoreOp>(op);
1203 Value val = storeOp.getValue();
1204 Value memref = storeOp.getMemref();
1207 auto iface = cast<AutoDiffTypeInterface>(val.getType());
1212 Value memrefGradient = gutils->
popCache(caches.front(), builder);
1214 SmallVector<Value> retrievedArguments;
1215 for (Value cache : ValueRange(caches).drop_front(1)) {
1216 Value retrievedValue = gutils->
popCache(cache, builder);
1217 retrievedArguments.push_back(retrievedValue);
1220 bool hasIndex = storeOp.getAffineMap().getNumDims() > 0;
1221 auto alignAttr = storeOp->getAttrOfType<IntegerAttr>(
"alignment");
1223 if (!iface.isMutable()) {
1225 Value loadedGradient;
1227 SmallVector<Value> indices;
1229 storeOp.getAffineMap(), retrievedArguments,
1231 auto loadedGradientOp = memref::LoadOp::create(
1232 builder, storeOp.getLoc(), memrefGradient, indices);
1233 loadedGradientOp.setAlignmentAttr(alignAttr);
1234 loadedGradient = loadedGradientOp;
1236 auto loadedGradientOp = affine::AffineLoadOp::create(
1237 builder, storeOp.getLoc(), memrefGradient,
1238 storeOp.getAffineMap(), ArrayRef<Value>(retrievedArguments));
1240 loadedGradientOp->setAttr(
"alignment", alignAttr);
1241 loadedGradient = loadedGradientOp;
1243 gutils->
addToDiffe(val, loadedGradient, builder);
1247 cast<AutoDiffTypeInterface>(gutils->
getShadowType(val.getType()))
1248 .createNullValue(builder, op->getLoc());
1252 SmallVector<Value> indices;
1254 storeOp.getAffineMap(), retrievedArguments,
1256 auto zeroStoreOp = memref::StoreOp::create(
1257 builder, storeOp.getLoc(), zero, memrefGradient, indices);
1258 zeroStoreOp.setAlignmentAttr(alignAttr);
1260 auto zeroStoreOp = affine::AffineStoreOp::create(
1261 builder, storeOp.getLoc(), zero, memrefGradient,
1262 storeOp.getAffineMap(), ArrayRef<Value>(retrievedArguments));
1264 zeroStoreOp->setAttr(
"alignment", alignAttr);
1277 SmallVector<Value> cacheValues(Operation *op,
1279 auto storeOp = cast<affine::AffineStoreOp>(op);
1280 Value memref = storeOp.getMemref();
1281 ValueRange indices = storeOp.getIndices();
1282 Value val = storeOp.getValue();
1283 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType())) {
1286 SmallVector<Value> caches;
1289 for (Value v : indices) {
1296 return SmallVector<Value>();
1301 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
1303 auto storeOp = cast<affine::AffineStoreOp>(op);
1304 Value val = storeOp.getValue();
1305 Value memref = storeOp.getMemref();
1306 auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType());
1308 return op->emitError()
1309 <<
"could not compute the shadow of a store of a type without "
1310 "autodiff semantics "
1317 if (!iface.isMutable())
1323 : gutils->invertPointerM(val, builder);
1325 auto shadowOp = cast<affine::AffineStoreOp>(builder.clone(*newOp));
1326 shadowOp.getValueMutable().assign(valShadow);
1327 shadowOp.getMemrefMutable().assign(memrefShadow);
1332struct AffineForOpADDataFlow
1333 :
public ADDataFlowOpInterface::ExternalModel<AffineForOpADDataFlow,
1334 affine::AffineForOp> {
1335 SmallVector<Value> getPotentialIncomingValuesRes(Operation *op,
1336 OpResult res)
const {
1337 auto forOp = cast<affine::AffineForOp>(op);
1339 forOp.getInits()[res.getResultNumber()],
1340 forOp.getBody()->getTerminator()->getOperand(res.getResultNumber())};
1342 SmallVector<Value> getPotentialIncomingValuesArg(Operation *op,
1343 BlockArgument arg)
const {
1344 auto forOp = cast<affine::AffineForOp>(op);
1345 if (arg.getArgNumber() < 1) {
1348 auto idx = arg.getArgNumber() - 1;
1349 return {forOp.getInits()[idx],
1350 forOp.getBody()->getTerminator()->getOperand(idx)};
1354 auto forOp = cast<affine::AffineForOp>(op);
1355 SmallVector<Value> sv;
1357 for (
auto &&[res, arg, barg] :
1358 llvm::zip_equal(forOp->getResults(), term->getOperands(),
1359 forOp.getRegionIterArgs())) {
1370struct AffineForOpEnzymeOpsRemover
1372 affine::AffineForOp> {
1375 static std::optional<int64_t>
1376 getConstantNumberOfIterations(affine::AffineForOp forOp) {
1377 if (!forOp.hasConstantLowerBound())
1378 return std::nullopt;
1379 if (!forOp.hasConstantUpperBound())
1380 return std::nullopt;
1381 return (forOp.getConstantUpperBound() - forOp.getConstantLowerBound()) /
1382 forOp.getStepAsInt();
1390 static Value materializeBound(OpBuilder &builder, Location loc, AffineMap map,
1391 ValueRange operands,
bool isUpper) {
1392 if (map.getNumResults() == 1)
1393 return AffineApplyOp::create(builder, loc, map, operands);
1395 return affine::AffineMinOp::create(builder, loc, map, operands);
1396 return affine::AffineMaxOp::create(builder, loc, map, operands);
1399 static SmallVector<IntOrValue, 1>
1400 getDimensionBounds(OpBuilder &builder, affine::AffineForOp forOp) {
1401 auto iters = getConstantNumberOfIterations(forOp);
1403 return {IntOrValue(*iters)};
1406 materializeBound(builder, forOp.getLoc(), forOp.getLowerBoundMap(),
1407 forOp.getLowerBoundOperands(),
1410 materializeBound(builder, forOp.getLoc(), forOp.getUpperBoundMap(),
1411 forOp.getUpperBoundOperands(),
1414 Value diff = arith::SubIOp::create(builder, forOp->getLoc(), ub, lb);
1415 if (forOp.getStepAsInt() != 1) {
1420 auto step = arith::ConstantOp::create(
1421 builder, forOp->getLoc(),
1422 IntegerAttr::get(diff.getType(), forOp.getStepAsInt()));
1423 diff = arith::DivUIOp::create(builder, forOp->getLoc(), diff, step);
1425 return {IntOrValue(diff)};
1429 static SmallVector<Value> getCanonicalLoopIVs(OpBuilder &builder,
1430 affine::AffineForOp forOp) {
1431 Value val = forOp.getBody()->getArgument(0);
1432 if (!forOp.hasConstantLowerBound() || forOp.getConstantLowerBound() != 0) {
1434 materializeBound(builder, forOp.getLoc(), forOp.getLowerBoundMap(),
1435 forOp.getLowerBoundOperands(),
1437 val = arith::SubIOp::create(builder, forOp->getLoc(), val, lb);
1440 if (forOp.getStepAsInt() != 1) {
1444 auto step = arith::ConstantOp::create(
1445 builder, forOp->getLoc(),
1446 IntegerAttr::get(val.getType(), forOp.getStepAsInt()));
1447 val = arith::DivUIOp::create(builder, forOp->getLoc(), val, step);
1452 static IRMapping createArgumentMap(PatternRewriter &rewriter,
1453 affine::AffineForOp forOp,
1454 ArrayRef<Value> indFor,
1455 affine::AffineForOp otherForOp,
1456 ArrayRef<Value> indOther) {
1458 for (
auto &&[f, o] : llvm::zip_equal(indFor, indOther))
1461 Value canIdx = forOp.getBody()->getArgument(0);
1462 if (!map.contains(canIdx)) {
1463 assert(forOp.getLowerBoundMap() == otherForOp.getLowerBoundMap());
1464 for (
auto &&[f, o] :
1465 llvm::zip_equal(forOp.getLowerBoundOperands(),
1466 otherForOp.getLowerBoundOperands())) {
1469 assert(Equivalent(f, o));
1471 assert(forOp.getStep() == otherForOp.getStep());
1472 map.map(forOp.getBody()->getArgument(0),
1473 otherForOp.getBody()->getArgument(0));
1478 static affine::AffineForOp
1479 replaceWithNewOperands(PatternRewriter &rewriter,
1480 affine::AffineForOp otherForOp,
1481 ArrayRef<Value> operands) {
1482 auto newOtherForOp = affine::AffineForOp::create(
1483 rewriter, otherForOp->getLoc(), otherForOp.getLowerBoundOperands(),
1484 otherForOp.getLowerBoundMap(), otherForOp.getUpperBoundOperands(),
1485 otherForOp.getUpperBoundMap(), otherForOp.getStepAsInt(), operands);
1488 newOtherForOp->setDiscardableAttrs(
1489 otherForOp->getDiscardableAttrDictionary());
1491 newOtherForOp.getRegion().takeBody(otherForOp.getRegion());
1492 rewriter.replaceOp(otherForOp, newOtherForOp->getResults().slice(
1493 0, otherForOp->getNumResults()));
1494 return newOtherForOp;
1497 static ValueRange getInits(affine::AffineForOp forOp) {
1498 return forOp.getInits();
1501 static bool mustPostAdd(affine::AffineForOp forOp) {
return false; }
1503 static Value initialValueInBlock(OpBuilder &builder, Block *body,
1505 auto Ty = cast<enzyme::GradientType>(grad.getType()).getBasetype();
1506 return body->addArgument(Ty, grad.getLoc());
1510#include "Implementations/AffineDerivatives.inc"
1514 DialectRegistry ®istry) {
1515 registry.addExtension(+[](MLIRContext *context, affine::AffineDialect *) {
1520 context->getOrLoadDialect<scf::SCFDialect>();
1521 registerInterfaces(context);
1522 affine::AffineLoadOp::attachInterface<AffineLoadOpInterfaceReverse>(
1524 affine::AffineStoreOp::attachInterface<AffineStoreLike>(*context);
1525 affine::AffineStoreOp::attachInterface<AffineStoreOpInterfaceReverse>(
1527 affine::AffineForOp::attachInterface<AffineForOpInterfaceReverse>(*context);
1528 affine::AffineForOp::attachInterface<AffineForOpEnzymeOpsRemover>(*context);
1529 affine::AffineForOp::attachInterface<AffineForOpADDataFlow>(*context);
1530 affine::AffineParallelOp::attachInterface<AffineParallelOpInterfaceReverse>(
1532 affine::AffineParallelOp::attachInterface<AffineParallelOpEnzymeOpsRemover>(
1534 affine::AffineParallelOp::attachInterface<
1535 AffineParallelRegionBranchOpInterface>(*context);
1536 affine::AffineIfOp::attachInterface<AffineIfOpInterfaceReverse>(*context);
1537 affine::AffineIfOp::attachInterface<AffineIfOpEnzymeOpsRemover>(*context);
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)
bool primalStoreElidable(mlir::Value ptr)
True when a primal store through this pointer may be omitted: declaring the base enzyme_dupnoneed is ...
mlir::Type getShadowType(mlir::Type T)
void erase(Operation *op)
mlir::Value invertPointerM(mlir::Value v, OpBuilder &Builder2)
SmallVector< mlir::Value, 1 > getNewFromOriginal(ValueRange originst) const
void setInvertedPointer(mlir::Value origv, mlir::Value newv)
bool isConstantValue(mlir::Value v) const
Value inactiveStoredValueShadow(Operation *orig, MGradientUtils &gutils, Value stored, OpBuilder &builder)
void computeAffineIndices(OpBuilder &builder, Location loc, AffineMap map, ValueRange operands, SmallVectorImpl< Value > &indices)
void localizeGradients(OpBuilder &builder, MGradientUtilsReverse *gutils, Block *fwd)
void setDerivativeFastMath(Operation *op)
Give an operation built for a derivative the fast-math flags the LLVM path gives everything it builds...
void registerAffineDialectAutoDiffInterface(DialectRegistry ®istry)