10#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
11#include "mlir/IR/AffineExpr.h"
12#include "mlir/IR/Builders.h"
13#include "mlir/IR/Matchers.h"
14#include "mlir/IR/PatternMatch.h"
15#include "mlir/IR/Value.h"
16#include "mlir/Interfaces/MemorySlotInterfaces.h"
18#include "mlir/Dialect/Affine/IR/AffineOps.h"
19#include "mlir/Dialect/Func/IR/FuncOps.h"
20#include "mlir/Dialect/MemRef/IR/MemRef.h"
21#include "mlir/IR/IntegerSet.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallVector.h"
26#include "llvm/Support/ErrorHandling.h"
27#include "llvm/Support/LogicalResult.h"
30#define DEBUG_TYPE "enzyme"
33using namespace enzyme;
40llvm::SmallVector<MemorySlot> InitOp::getPromotableSlots() {
41 auto Ty = this->getType();
42 if (isa<CacheType>(Ty))
45 if (!getOperation()->getBlock()->isEntryBlock())
48 auto gTy = cast<GradientType>(Ty);
49 MemorySlot slot = {this->getResult(), gTy.getBasetype()};
54Value InitOp::getDefaultValue(
const MemorySlot &slot, OpBuilder &builder) {
55 auto gTy = cast<GradientType>(this->getType());
56 return cast<AutoDiffTypeInterface>(gTy.getBasetype())
57 .createNullValue(builder, this->getLoc());
60void InitOp::handleBlockArgument(
const MemorySlot &slot, BlockArgument argument,
61 OpBuilder &builder) {}
63std::optional<mlir::PromotableAllocationOpInterface>
64InitOp::handlePromotionComplete(
const MemorySlot &slot, Value defaultValue,
66 if (defaultValue && defaultValue.use_empty())
67 defaultValue.getDefiningOp()->erase();
104 int64_t t = 0, beta = 1;
107 beta = beta * (s + t) / t;
112 int64_t lo = n - beta * s / (s + t);
113 int64_t hi = beta * t / (s + t);
118 int64_t m = (lo + hi) / 2;
124 int64_t cap = n - (s - 1);
127 return m < 1 ? 1 : m;
130OpFoldResult BinomialProgressOp::fold(FoldAdaptor adaptor) {
131 auto numSteps = dyn_cast_or_null<IntegerAttr>(adaptor.getNumSteps());
132 auto budget = dyn_cast_or_null<IntegerAttr>(adaptor.getBudget());
133 if (!numSteps || !budget)
136 int64_t n = numSteps.getInt(), s = budget.getInt();
137 if (n <= 0 || s <= 0)
147bool GetOp::loadsFrom(
const MemorySlot &slot) {
148 return this->getGradient() == slot.ptr;
151bool GetOp::storesTo(
const MemorySlot &slot) {
return false; }
153Value GetOp::getStored(
const MemorySlot &slot, OpBuilder &builder,
154 Value reachingDef,
const DataLayout &dataLayout) {
158bool GetOp::canUsesBeRemoved(
159 const MemorySlot &slot,
160 const llvm::SmallPtrSetImpl<OpOperand *> &blockingUses,
161 llvm::SmallVectorImpl<OpOperand *> &newBlockingUses,
162 const mlir::DataLayout &dataLayout) {
163 if (blockingUses.size() != 1)
166 Value blockingUse = (*blockingUses.begin())->get();
167 return blockingUse == slot.ptr && getGradient() == slot.ptr;
170DeletionKind GetOp::removeBlockingUses(
171 const MemorySlot &slot,
172 const llvm::SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder,
173 Value reachingDefinition,
const DataLayout &dataLayout) {
174 this->getResult().replaceAllUsesWith(reachingDefinition);
175 return DeletionKind::Delete;
178llvm::LogicalResult GetOp::ensureOnlySafeAccesses(
179 const MemorySlot &slot, llvm::SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
180 const DataLayout &dataLayout) {
181 return success(slot.ptr == getGradient());
188bool SetOp::loadsFrom(
const MemorySlot &slot) {
return false; }
190bool SetOp::storesTo(
const MemorySlot &slot) {
191 return this->getGradient() == slot.ptr;
194Value SetOp::getStored(
const MemorySlot &slot, OpBuilder &builder,
195 Value reachingDef,
const DataLayout &dataLayout) {
196 return this->getValue();
199bool SetOp::canUsesBeRemoved(
200 const MemorySlot &slot,
201 const llvm::SmallPtrSetImpl<OpOperand *> &blockingUses,
202 llvm::SmallVectorImpl<OpOperand *> &newBlockingUses,
203 const mlir::DataLayout &dataLayout) {
207DeletionKind SetOp::removeBlockingUses(
208 const MemorySlot &slot,
209 const llvm::SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder,
210 Value reachingDefinition,
const DataLayout &dataLayout) {
211 return DeletionKind::Delete;
214llvm::LogicalResult SetOp::ensureOnlySafeAccesses(
215 const MemorySlot &slot, llvm::SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
216 const DataLayout &dataLayout) {
217 return success(slot.ptr == getGradient());
225ForwardDiffOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
230 auto global = symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(
233 return emitOpError(
"'")
234 << getFn() <<
"' does not reference a valid global funcOp";
239LogicalResult JacobianOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
241 symbolTable.lookupNearestSymbolFrom<func::FuncOp>(*
this, getFnAttr());
243 return emitOpError(
"'")
244 << getFn() <<
"' does not reference a valid global funcOp";
258 static AutoDiffOp
create(PatternRewriter &rewriter, AutoDiffOp uop,
259 TypeRange out_ty, ValueRange in_args,
260 ArrayAttr newInActivity, ArrayAttr newRetActivity) {
262 return AutoDiffOp::create(rewriter, uop.getLoc(), out_ty, uop.getFnAttr(),
263 in_args, newInActivity, newRetActivity,
264 uop.getWidthAttr(), uop.getStrongZeroAttr(),
265 uop.getAtomicAddAttr());
270 static AutoDiffRegionOp
create(PatternRewriter &rewriter,
271 AutoDiffRegionOp uop, TypeRange out_ty,
272 ValueRange in_args, ArrayAttr newInActivity,
273 ArrayAttr newRetActivity) {
274 auto newOp = AutoDiffRegionOp::create(
275 rewriter, uop.getLoc(), out_ty, in_args, newInActivity, newRetActivity,
276 uop.getWidthAttr(), uop.getStrongZeroAttr(), uop.getAtomicAddAttr(),
279 rewriter.inlineRegionBefore(uop.getBody(), newOp.getBody(),
280 newOp.getBody().begin());
286 static ForwardDiffOp
create(PatternRewriter &rewriter, ForwardDiffOp uop,
287 TypeRange out_ty, ValueRange in_args,
288 ArrayAttr newInActivity,
289 ArrayAttr newRetActivity) {
290 return ForwardDiffOp::create(
291 rewriter, uop.getLoc(), out_ty, uop.getFnAttr(), in_args, newInActivity,
292 newRetActivity, uop.getWidthAttr(), uop.getStrongZeroAttr());
297 static ForwardDiffRegionOp
create(PatternRewriter &rewriter,
298 ForwardDiffRegionOp uop, TypeRange out_ty,
299 ValueRange in_args, ArrayAttr newInActivity,
300 ArrayAttr newRetActivity) {
301 auto newOp = ForwardDiffRegionOp::create(
302 rewriter, uop.getLoc(), out_ty, in_args, newInActivity, newRetActivity,
303 uop.getWidthAttr(), uop.getStrongZeroAttr(), uop.getFnAttr());
304 rewriter.inlineRegionBefore(uop.getBody(), newOp.getBody(),
305 newOp.getBody().begin());
312 if (isa<mlir::MemRefType>(type) || isa<mlir::UnrankedMemRefType>(type) ||
313 isa<mlir::LLVM::LLVMPointerType>(type)) {
333template <
typename SourceOp>
339 PatternRewriter &rewriter)
const override {
341 if (uop.getOutputs().size() == 0)
344 auto inActivity = uop.getActivity();
347 SmallVector<mlir::Value, 2> in_args;
348 SmallVector<ActivityAttr, 2> newInActivityArgs;
349 bool changed =
false;
350 for (
auto [idx, act] : llvm::enumerate(inActivity)) {
351 auto iattr = cast<ActivityAttr>(act);
352 auto val = iattr.getValue();
357 mlir::Value inp = uop.getInputs()[in_idx];
361 case mlir::enzyme::Activity::enzyme_const:
362 in_args.push_back(inp);
363 newInActivityArgs.push_back(iattr);
366 case Activity::enzyme_dupnoneed: {
368 in_args.push_back(inp);
372 inp = uop.getInputs()[in_idx];
373 auto ET = inp.getType();
374 auto ETintf = dyn_cast<AutoDiffTypeInterface>(ET);
376 if (ETintf && !
isMutable(ET) && ETintf.isZero(inp)) {
378 auto new_const = mlir::enzyme::ActivityAttr::get(
379 rewriter.getContext(), mlir::enzyme::Activity::enzyme_const);
380 newInActivityArgs.push_back(new_const);
384 in_args.push_back(inp);
385 newInActivityArgs.push_back(iattr);
390 case Activity::enzyme_dup: {
392 in_args.push_back(inp);
396 inp = uop.getInputs()[in_idx];
397 auto ET = inp.getType();
398 auto ETintf = dyn_cast<AutoDiffTypeInterface>(ET);
400 if (ETintf && !
isMutable(ET) && ETintf.isZero(inp)) {
402 auto new_const = mlir::enzyme::ActivityAttr::get(
403 rewriter.getContext(), mlir::enzyme::Activity::enzyme_const);
404 newInActivityArgs.push_back(new_const);
408 in_args.push_back(inp);
409 newInActivityArgs.push_back(iattr);
414 llvm_unreachable(
"unexpected input activity arg");
424 ArrayAttr newInActivity =
425 ArrayAttr::get(rewriter.getContext(),
426 llvm::ArrayRef<Attribute>(newInActivityArgs.begin(),
427 newInActivityArgs.end()));
429 if constexpr (std::is_same_v<SourceOp, ForwardDiffOp>) {
431 rewriter.replaceOpWithNewOp<ForwardDiffOp>(
432 uop, uop->getResultTypes(), uop.getFnAttr(), in_args, newInActivity,
433 uop.getRetActivityAttr(), uop.getWidthAttr(),
434 uop.getStrongZeroAttr());
436 rewriter.replaceOpWithNewOp<ForwardDiffRegionOp>(
437 uop, uop->getResultTypes(), in_args, newInActivity,
438 uop.getRetActivityAttr(), uop.getWidthAttr(), uop.getStrongZeroAttr(),
458template <
typename SourceOp>
467 PatternRewriter &rewriter)
const override {
469 if (uop.getOutputs().size() == 0)
472 auto retActivity = uop.getRetActivity();
474 SmallVector<mlir::Value, 2> outs_args;
475 SmallVector<Type, 2> out_ty;
476 SmallVector<ActivityAttr, 2> newRetActivityArgs;
477 bool changed =
false;
479 for (
auto [idx, act] : llvm::enumerate(retActivity)) {
480 auto iattr = cast<ActivityAttr>(act);
481 auto val = iattr.getValue();
485 if (val == Activity::enzyme_constnoneed) {
486 newRetActivityArgs.push_back(iattr);
490 mlir::Value res = uop.getOutputs()[out_idx];
493 case Activity::enzyme_active:
494 outs_args.push_back(res);
495 out_ty.push_back(res.getType());
496 newRetActivityArgs.push_back(iattr);
499 case mlir::enzyme::Activity::enzyme_const:
500 if (!res.use_empty()) {
501 outs_args.push_back(res);
502 out_ty.push_back(res.getType());
503 newRetActivityArgs.push_back(iattr);
506 auto new_constnn = mlir::enzyme::ActivityAttr::get(
507 rewriter.getContext(),
508 mlir::enzyme::Activity::enzyme_constnoneed);
509 newRetActivityArgs.push_back(new_constnn);
512 case Activity::enzyme_dupnoneed:
514 if (!res.use_empty()) {
515 outs_args.push_back(res);
516 out_ty.push_back(res.getType());
517 newRetActivityArgs.push_back(iattr);
521 auto new_constnn = mlir::enzyme::ActivityAttr::get(
522 rewriter.getContext(),
523 mlir::enzyme::Activity::enzyme_constnoneed);
524 newRetActivityArgs.push_back(new_constnn);
526 outs_args.push_back(res);
527 out_ty.push_back(res.getType());
528 newRetActivityArgs.push_back(iattr);
532 case Activity::enzyme_constnoneed:
533 outs_args.push_back(res);
534 out_ty.push_back(res.getType());
535 newRetActivityArgs.push_back(iattr);
537 case Activity::enzyme_activenoneed:
538 outs_args.push_back(res);
539 out_ty.push_back(res.getType());
540 newRetActivityArgs.push_back(iattr);
542 case Activity::enzyme_dup: {
543 ActivityAttr new_dup = iattr;
544 if (!res.use_empty()) {
545 outs_args.push_back(res);
546 out_ty.push_back(res.getType());
550 new_dup = ActivityAttr::get(rewriter.getContext(),
551 Activity::enzyme_dupnoneed);
557 res = uop.getOutputs()[out_idx];
558 if (!res.use_empty()) {
560 out_ty.push_back(res.getType());
561 outs_args.push_back(res);
567 if (new_dup.getValue() == Activity::enzyme_dupnoneed) {
568 new_dup = ActivityAttr::get(rewriter.getContext(),
569 Activity::enzyme_constnoneed);
571 new_dup = ActivityAttr::get(rewriter.getContext(),
572 Activity::enzyme_const);
575 out_ty.push_back(res.getType());
576 outs_args.push_back(res);
579 newRetActivityArgs.push_back(new_dup);
583 llvm_unreachable(
"unexpected activity arg");
592 ArrayAttr newRetActivity =
593 ArrayAttr::get(rewriter.getContext(),
594 llvm::ArrayRef<Attribute>(newRetActivityArgs.begin(),
595 newRetActivityArgs.end()));
597 SmallVector<Value> in_args = uop.getInputs();
598 SourceOp newOp = SourceOpCreator::create(
599 rewriter, uop, out_ty, in_args, uop.getActivityAttr(), newRetActivity);
604 for (
auto [idx, old_act, new_act] :
605 llvm::enumerate(retActivity, newRetActivityArgs)) {
607 auto iattr = cast<ActivityAttr>(old_act);
608 auto old_val = iattr.getValue();
609 auto new_val = new_act.getValue();
611 if (old_val == new_val) {
613 if (old_val == Activity::enzyme_constnoneed) {
617 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
618 newOp.getOutputs()[newIdx++]);
619 if (old_val == Activity::enzyme_dup) {
621 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
622 newOp.getOutputs()[newIdx++]);
626 if (new_val == Activity::enzyme_dupnoneed &&
627 old_val == Activity::enzyme_dup) {
629 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
630 newOp.getOutputs()[newIdx++]);
631 }
else if (new_val == mlir::enzyme::Activity::enzyme_constnoneed &&
632 old_val == mlir::enzyme::Activity::enzyme_const) {
634 }
else if (new_val == mlir::enzyme::Activity::enzyme_constnoneed &&
635 old_val == mlir::enzyme::Activity::enzyme_dupnoneed) {
637 }
else if (new_val == Activity::enzyme_const &&
638 old_val == Activity::enzyme_dup) {
640 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
641 newOp.getOutputs()[newIdx++]);
643 }
else if (new_val == Activity::enzyme_constnoneed &&
644 old_val == Activity::enzyme_dup) {
651 rewriter.eraseOp(uop);
656void ForwardDiffOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
657 MLIRContext *context) {
662LogicalResult AutoDiffOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
665 auto global = symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(
668 return emitOpError(
"'")
669 << getFn() <<
"' does not reference a valid global funcOp";
674LogicalResult BatchOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
677 auto global = symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(
680 return emitOpError(
"'")
681 << getFn() <<
"' does not reference a valid global funcOp";
690void BroadcastOp::build(OpBuilder &builder, OperationState &result, Value input,
691 ArrayRef<int64_t> shape) {
692 auto shapeAttr = builder.getDenseI64ArrayAttr(shape);
693 auto resultTy = input.getType();
694 for (
auto s : llvm::reverse(shape)) {
695 resultTy = cast<AutoDiffTypeInterface>(resultTy).getShadowType(s);
697 build(builder, result, resultTy, input, shapeAttr);
757template <
typename SourceOp>
766 PatternRewriter &rewriter)
const override {
768 if (uop.getOutputs().size() == 0)
771 auto inpActivity = uop.getActivity();
772 auto retActivity = uop.getRetActivity();
774 SmallVector<mlir::Value, 2> in_args;
775 SmallVector<mlir::Value, 2> outs_args;
776 SmallVector<Type, 2> in_ty;
777 SmallVector<Type, 2> out_ty;
778 SmallVector<ActivityAttr, 2> newInActivityArgs;
779 SmallVector<ActivityAttr, 2> newRetActivityArgs;
781 bool changed =
false;
785 for (
auto [idx, act] : llvm::enumerate(inpActivity)) {
786 auto iattr = cast<ActivityAttr>(act);
787 auto val = iattr.getValue();
788 mlir::Value res = uop.getInputs()[in_idx];
789 in_args.push_back(res);
790 in_ty.push_back(res.getType());
793 if (val == Activity::enzyme_dup || val == Activity::enzyme_dupnoneed) {
794 mlir::Value dres = uop.getInputs()[in_idx];
795 in_args.push_back(dres);
796 in_ty.push_back(dres.getType());
801 if (in_idx == uop.getInputs().size())
805 for (
auto [idx, act] : llvm::enumerate(retActivity)) {
806 auto iattr = cast<ActivityAttr>(act);
807 auto val = iattr.getValue();
810 if (val == Activity::enzyme_constnoneed ||
811 val == Activity::enzyme_dupnoneed) {
812 newRetActivityArgs.push_back(iattr);
819 if (out_idx >= (int64_t)uop.getOutputs().size())
821 mlir::Value res = uop.getOutputs()[out_idx];
824 case Activity::enzyme_active: {
829 if (in_idx >= (int64_t)uop.getInputs().size())
831 mlir::Value dres = uop.getInputs()[in_idx];
834 auto dres_type = dres.getType();
835 auto dres_type_intf = dyn_cast<AutoDiffTypeInterface>(dres_type);
837 if (!res.use_empty()) {
838 outs_args.push_back(res);
839 out_ty.push_back(res.getType());
840 ActivityAttr new_act = iattr;
841 if (dres_type_intf && !
isMutable(dres_type) &&
842 dres_type_intf.isZero(dres)) {
845 new_act = ActivityAttr::get(rewriter.getContext(),
846 Activity::enzyme_const);
848 in_args.push_back(dres);
849 in_ty.push_back(dres_type);
851 newRetActivityArgs.push_back(new_act);
854 ActivityAttr new_act = ActivityAttr::get(
855 rewriter.getContext(), Activity::enzyme_activenoneed);
856 if (dres_type_intf && !
isMutable(dres_type) &&
857 dres_type_intf.isZero(dres)) {
859 new_act = ActivityAttr::get(rewriter.getContext(),
860 Activity::enzyme_constnoneed);
863 in_args.push_back(dres);
864 in_ty.push_back(dres_type);
866 newRetActivityArgs.push_back(new_act);
873 case Activity::enzyme_activenoneed:
876 if (in_idx >= (int64_t)uop.getInputs().size())
878 mlir::Value dres = uop.getInputs()[in_idx];
880 auto new_act = iattr;
882 auto dres_type = dres.getType();
883 auto dres_type_intf = dyn_cast<AutoDiffTypeInterface>(dres_type);
884 if (dres_type_intf && !
isMutable(dres_type) &&
885 dres_type_intf.isZero(dres)) {
887 new_act = ActivityAttr::get(rewriter.getContext(),
888 Activity::enzyme_constnoneed);
890 in_args.push_back(dres);
891 in_ty.push_back(dres_type);
893 newRetActivityArgs.push_back(iattr);
896 case Activity::enzyme_const:
899 auto new_act = iattr;
900 if (!res.use_empty()) {
901 outs_args.push_back(res);
902 out_ty.push_back(res.getType());
903 newRetActivityArgs.push_back(new_act);
906 new_act = ActivityAttr::get(rewriter.getContext(),
907 Activity::enzyme_constnoneed);
908 newRetActivityArgs.push_back(new_act);
914 case Activity::enzyme_dup:
917 outs_args.push_back(res);
918 out_ty.push_back(res.getType());
919 newRetActivityArgs.push_back(iattr);
923 case Activity::enzyme_constnoneed:
924 case Activity::enzyme_dupnoneed:
928 llvm_unreachable(
"unexpected activity arg");
933 for (
auto [idx, act] : llvm::enumerate(inpActivity)) {
934 auto iattr = cast<ActivityAttr>(act);
935 auto val = iattr.getValue();
937 if (val == Activity::enzyme_active) {
938 if (out_idx >= (int64_t)uop.getOutputs().size())
940 mlir::Value res = uop.getOutputs()[out_idx];
941 if (!res.use_empty()) {
942 out_ty.push_back(res.getType());
943 outs_args.push_back(res);
944 newInActivityArgs.push_back(iattr);
949 auto new_const = ActivityAttr::get(rewriter.getContext(),
950 Activity::enzyme_const);
951 newInActivityArgs.push_back(new_const);
954 out_ty.push_back(res.getType());
955 outs_args.push_back(res);
956 newInActivityArgs.push_back(iattr);
961 }
else if (val == Activity::enzyme_activenoneed) {
962 mlir::Value res = uop.getOutputs()[out_idx];
963 out_ty.push_back(res.getType());
964 outs_args.push_back(res);
965 newInActivityArgs.push_back(iattr);
967 llvm_unreachable(
"unsupported arg activenoneed");
969 newInActivityArgs.push_back(iattr);
976 ArrayAttr newInActivity =
977 ArrayAttr::get(rewriter.getContext(),
978 llvm::ArrayRef<Attribute>(newInActivityArgs.begin(),
979 newInActivityArgs.end()));
980 ArrayAttr newRetActivity =
981 ArrayAttr::get(rewriter.getContext(),
982 llvm::ArrayRef<Attribute>(newRetActivityArgs.begin(),
983 newRetActivityArgs.end()));
985 SourceOp newOp = SourceOpCreator::create(rewriter, uop, out_ty, in_args,
986 newInActivity, newRetActivity);
991 for (
auto [idx, old_act, new_act] :
992 llvm::enumerate(retActivity, newRetActivityArgs)) {
993 auto iattr = cast<ActivityAttr>(old_act);
994 auto old_val = iattr.getValue();
995 auto new_val = new_act.getValue();
997 if (old_val == new_val) {
999 if (old_val == Activity::enzyme_constnoneed ||
1000 old_val == Activity::enzyme_activenoneed ||
1001 old_val == Activity::enzyme_dupnoneed) {
1005 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
1006 newOp.getOutputs()[newIdx++]);
1009 if (new_val == Activity::enzyme_activenoneed &&
1010 old_val == Activity::enzyme_active) {
1012 }
else if (new_val == Activity::enzyme_constnoneed &&
1013 old_val == Activity::enzyme_const) {
1015 }
else if (old_val == Activity::enzyme_active &&
1016 new_val == Activity::enzyme_const) {
1017 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
1018 newOp.getOutputs()[newIdx++]);
1019 }
else if (old_val == Activity::enzyme_active &&
1020 new_val == Activity::enzyme_constnoneed) {
1022 }
else if (old_val == Activity::enzyme_activenoneed &&
1023 new_val == Activity::enzyme_constnoneed) {
1029 for (
auto [idx, old_act, new_act] :
1030 llvm::enumerate(inpActivity, newInActivityArgs)) {
1031 auto iattr = cast<ActivityAttr>(old_act);
1032 auto old_val = iattr.getValue();
1033 auto new_val = new_act.getValue();
1035 if (old_val == new_val) {
1036 if (old_val == Activity::enzyme_active ||
1037 old_val == Activity::enzyme_activenoneed) {
1038 uop.getOutputs()[oldIdx++].replaceAllUsesWith(
1039 newOp.getOutputs()[newIdx++]);
1044 if (old_val == Activity::enzyme_active &&
1045 new_val == Activity::enzyme_const) {
1050 rewriter.eraseOp(uop);
1055template <
typename SourceRegionOp>
1065 PatternRewriter &rewriter)
const override {
1066 SmallVector<Value> newInArgs;
1067 SmallVector<size_t> argIdxToErase;
1068 SmallVector<ActivityAttr> newInActivityArgs;
1069 llvm::SmallVector<Value> blockArg(uop.getBody().getArguments());
1071 for (
auto [idx, act] : llvm::enumerate(
1072 uop.getActivity().template getAsRange<ActivityAttr>())) {
1073 auto act_val = act.getValue();
1074 Value res = uop.getInputs()[in_idx++];
1076 if (blockArg[idx].use_empty()) {
1077 argIdxToErase.push_back(idx);
1078 if (act_val == Activity::enzyme_dup ||
1079 act_val == Activity::enzyme_dupnoneed) {
1083 newInActivityArgs.push_back(act);
1084 newInArgs.push_back(res);
1085 if (act_val == Activity::enzyme_dup ||
1086 act_val == Activity::enzyme_dupnoneed) {
1087 res = uop.getInputs()[in_idx++];
1088 newInArgs.push_back(res);
1093 if (argIdxToErase.empty())
1097 if constexpr (std::is_same_v<SourceRegionOp, AutoDiffRegionOp>) {
1098 newInArgs.append(uop.getDifferentialReturns());
1101 ArrayAttr newInActivity =
1102 ArrayAttr::get(rewriter.getContext(),
1103 llvm::ArrayRef<Attribute>(newInActivityArgs.begin(),
1104 newInActivityArgs.end()));
1106 SourceOpCreator::create(rewriter, uop, uop.getResultTypes(), newInArgs,
1107 newInActivity, uop.getRetActivity());
1109 for (
auto idx : llvm::reverse(argIdxToErase)) {
1110 newOp.getBody().eraseArgument(idx);
1113 rewriter.replaceOp(uop, newOp);
1118void AutoDiffOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1119 MLIRContext *context) {
1123void AutoDiffRegionOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1124 MLIRContext *context) {
1130void ForwardDiffRegionOp::getCanonicalizationPatterns(
1131 RewritePatternSet &patterns, MLIRContext *context) {
1140 arith::FastMathFlags fastmath) {
1142 case arith::AtomicRMWKind::addi:
1143 return matchPattern(value, m_Zero());
1144 case arith::AtomicRMWKind::addf:
1145 return matchPattern(value, m_AnyZeroFloat()) &&
1146 bitEnumContainsAll(fastmath, arith::FastMathFlags::nsz);
1156 return ordering == enzyme::Ordering::not_atomic ||
1157 ordering == enzyme::Ordering::unordered ||
1158 ordering == enzyme::Ordering::monotonic;
1161LogicalResult AtomicRMWOp::canonicalize(AtomicRMWOp op,
1162 PatternRewriter &rewriter) {
1166 if (op.getResult().use_empty()) {
1167 rewriter.eraseOp(op);
1174 auto load = memref::LoadOp::create(rewriter, op.getLoc(), op.getMemref(),
1176 if (
auto alignment = op.getAlignmentAttr())
1177 load.setAlignmentAttr(alignment);
1178 rewriter.replaceOp(op, load.getResult());
1184OpFoldResult AtomicRMWOp::fold(FoldAdaptor adaptor) {
1185 if (succeeded(memref::foldMemRefCast(*
this, getValue())))
1187 return OpFoldResult();
1190OpFoldResult AffineAtomicRMWOp::fold(FoldAdaptor adaptor) {
1191 if (succeeded(memref::foldMemRefCast(*
this, getValue())))
1193 return OpFoldResult();
1196LogicalResult AffineAtomicRMWOp::canonicalize(AffineAtomicRMWOp op,
1197 PatternRewriter &rewriter) {
1201 if (op.getResult().use_empty()) {
1202 rewriter.eraseOp(op);
1206 auto load = affine::AffineLoadOp::create(
1207 rewriter, op.getLoc(), op.getMemref(), op.getMap(), op.getIndices());
1208 if (
auto alignment = op.getAlignmentAttr())
1209 load->setAttr(
"alignment", alignment);
1210 rewriter.replaceOp(op, load.getResult());
static bool isMutable(Type type)
static bool readIsUnordered(enzyme::Ordering ordering)
static bool isRemovableAccumulation(arith::AtomicRMWKind kind, Value value, arith::FastMathFlags fastmath)
static int64_t binomialProgress(int64_t n, int64_t s)
Modifies input activites for the FwdDiffOp The activity promotion flow is as follows (depending on va...
LogicalResult matchAndRewrite(SourceOp uop, PatternRewriter &rewriter) const override
Modifies return activites for the FwdDiffOp The activity promotion flow is as follows (depending on v...
LogicalResult matchAndRewrite(SourceOp uop, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(SourceRegionOp uop, PatternRewriter &rewriter) const override
Modifies activities for the AutoDiffOp.
LogicalResult matchAndRewrite(SourceOp uop, PatternRewriter &rewriter) const override
static AutoDiffOp create(PatternRewriter &rewriter, AutoDiffOp uop, TypeRange out_ty, ValueRange in_args, ArrayAttr newInActivity, ArrayAttr newRetActivity)
static AutoDiffRegionOp create(PatternRewriter &rewriter, AutoDiffRegionOp uop, TypeRange out_ty, ValueRange in_args, ArrayAttr newInActivity, ArrayAttr newRetActivity)
static ForwardDiffOp create(PatternRewriter &rewriter, ForwardDiffOp uop, TypeRange out_ty, ValueRange in_args, ArrayAttr newInActivity, ArrayAttr newRetActivity)
static ForwardDiffRegionOp create(PatternRewriter &rewriter, ForwardDiffRegionOp uop, TypeRange out_ty, ValueRange in_args, ArrayAttr newInActivity, ArrayAttr newRetActivity)