375 ChangeResult result = ChangeResult::NoChange;
376 DenseSet<DistinctAttr> known;
377 auto lhsRange = llvm::make_first_range(activityStates);
378 auto rhsRange = llvm::make_first_range(rhs.activityStates);
379 known.insert(lhsRange.begin(), lhsRange.end());
380 known.insert(rhsRange.begin(), rhsRange.end());
383 result |= updatedOther.
merge(rhs.otherMemoryActivity);
384 DenseMap<DistinctAttr, MemoryActivityState> updated;
385 for (DistinctAttr d : known) {
386 auto lhsIt = activityStates.find(d);
387 auto rhsIt = rhs.activityStates.find(d);
388 bool isKnownInLHS = lhsIt != activityStates.end();
389 bool isKnownInRHS = rhsIt != rhs.activityStates.end();
391 isKnownInLHS ? &lhsIt->getSecond() : &otherMemoryActivity;
393 isKnownInRHS ? &rhsIt->getSecond() : &rhs.otherMemoryActivity;
395 (void)updatedActivity.
merge(*rhsActivity);
396 if ((lhsIt != activityStates.end() &&
397 updatedActivity != lhsIt->getSecond()) ||
398 (lhsIt == activityStates.end() &&
399 updatedActivity != otherMemoryActivity)) {
400 result |= ChangeResult::Change;
402 if (updatedActivity != updatedOther)
403 updated.try_emplace(d, updatedActivity);
406 std::swap(updated, activityStates);
407 return otherMemoryActivity.merge(rhs.otherMemoryActivity) | result;
418 ChangeResult result = ChangeResult::NoChange;
428 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
434 SmallVector<MemoryEffects::EffectInstance> effects;
435 memory.getEffects(effects);
437 for (
const auto &effect : effects) {
438 Value value = effect.getValue();
447 if (isa<MemoryEffects::Read>(effect.getEffect())) {
448 auto *ptrAliasClass =
449 getOrCreateFor<AliasClassLattice>(getProgramPointAfter(op), value);
452 for (OpResult opResult : op->getResults()) {
456 if (isa<FloatType, ComplexType>(opResult.getType())) {
458 getOrCreate<enzyme::ForwardValueActivity>(opResult);
465 if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
467 for (OpOperand *inputOperand : linalgOp.getDpsInputOperands()) {
468 if (inputOperand->get() == value) {
469 auto *valueState = getOrCreate<enzyme::ForwardValueActivity>(
470 linalgOp.getMatchingBlockArgument(inputOperand));
481 if (isa<MemoryEffects::Write>(effect.getEffect())) {
482 std::optional<Value> stored =
getStored(op);
483 if (stored.has_value()) {
484 auto *valueState = getOrCreateFor<enzyme::ForwardValueActivity>(
485 getProgramPointAfter(op), *stored);
486 if (valueState->getValue().isActiveVal()) {
487 auto *ptrAliasClass = getOrCreateFor<AliasClassLattice>(
488 getProgramPointAfter(op), value);
495 auto *srcAliasClass = getOrCreateFor<AliasClassLattice>(
496 getProgramPointAfter(op), *copySource);
499 auto *destAliasClass = getOrCreateFor<AliasClassLattice>(
500 getProgramPointAfter(op), value);
506 }
else if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
508 for (OpOperand &dpsInit : linalgOp.getDpsInitsMutable()) {
509 if (dpsInit.get() == value) {
510 int64_t resultIndex =
511 dpsInit.getOperandNumber() - linalgOp.getNumDpsInputs();
513 linalgOp.getBlock()->getTerminator()->getOperand(resultIndex);
514 auto *valueState = getOrCreateFor<enzyme::ForwardValueActivity>(
515 getProgramPointAfter(op), yieldOperand);
516 if (valueState->getValue().isActiveVal()) {
517 auto *ptrAliasClass = getOrCreateFor<AliasClassLattice>(
518 getProgramPointAfter(op), value);
528 propagateIfChanged(after, result);
574 for (
const auto &[arg, argActivity] : llvm::zip(
575 parentOp->getRegions().front().getArguments(), argumentActivity)) {
576 if (argActivity != enzyme::Activity::enzyme_dup &&
577 argActivity != enzyme::Activity::enzyme_dupnoneed) {
580 auto *argAliasClasses =
581 getOrCreateFor<AliasClassLattice>(getProgramPointBefore(op), arg);
582 ChangeResult changed =
583 argAliasClasses->getAliasClassesObject().foreachElement(
584 [before](DistinctAttr argAliasClass,
586 if (state == enzyme::AliasClassSet::State::Undefined)
587 return ChangeResult::NoChange;
590 propagateIfChanged(before, changed);
593 assert(op->getNumOperands() == returnActivity.size() &&
594 "the number of returned arguments must match the number of return "
598 for (
const auto &[operand, operandActivity] :
599 llvm::zip(op->getOperands(), returnActivity)) {
600 if (operandActivity != enzyme::Activity::enzyme_dup &&
601 operandActivity != enzyme::Activity::enzyme_dupnoneed)
604 auto *retAliasClasses =
605 getOrCreateFor<AliasClassLattice>(getProgramPointBefore(op), operand);
606 ChangeResult changed =
607 retAliasClasses->getAliasClassesObject().foreachElement(
608 [before](DistinctAttr retAliasClass,
610 if (state == enzyme::AliasClassSet::State::Undefined)
611 return ChangeResult::NoChange;
614 propagateIfChanged(before, changed);
619 ChangeResult result = ChangeResult::NoChange;
620 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
626 SmallVector<MemoryEffects::EffectInstance> effects;
627 memory.getEffects(effects);
629 for (
const auto &effect : effects) {
630 Value value = effect.getValue();
639 if (isa<MemoryEffects::Read>(effect.getEffect())) {
640 for (Value opResult : op->getResults()) {
641 auto *valueState = getOrCreateFor<enzyme::BackwardValueActivity>(
642 getProgramPointBefore(op), opResult);
643 if (valueState->getValue().isActiveVal()) {
644 auto *ptrAliasClass = getOrCreateFor<AliasClassLattice>(
645 getProgramPointBefore(op), value);
652 if (isa<MemoryEffects::Write>(effect.getEffect())) {
653 auto *ptrAliasClass =
654 getOrCreateFor<AliasClassLattice>(getProgramPointBefore(op), value);
655 std::optional<Value> stored =
getStored(op);
659 if (isa<FloatType, ComplexType>(stored->getType())) {
661 getOrCreate<enzyme::BackwardValueActivity>(*stored);
667 auto *srcAliasClass = getOrCreateFor<AliasClassLattice>(
668 getProgramPointBefore(op), *copySource);
672 }
else if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
674 for (OpOperand &dpsInit : linalgOp.getDpsInitsMutable()) {
675 if (dpsInit.get() == value) {
676 int64_t resultIndex =
677 dpsInit.getOperandNumber() - linalgOp.getNumDpsInputs();
679 linalgOp.getBlock()->getTerminator()->getOperand(
682 getOrCreate<enzyme::BackwardValueActivity>(yieldOperand);
693 propagateIfChanged(before, result);
698 FunctionOpInterface callee,
699 const SmallPtrSet<Operation *, 2> &returnOps,
700 SymbolTableCollection *symbolTable,
701 bool verbose,
bool annotate) {
702 auto isActiveData = [&](Value value) {
706 bool backwardActive = bva && bva->getValue().isActiveVal();
707 return forwardActive && backwardActive;
710 auto isConstantValue = [&](Value value) {
712 if (isa<LLVM::LLVMPointerType, MemRefType>(value.getType())) {
713 assert(returnOps.size() == 1);
715 solver.getProgramPointAfter(*returnOps.begin()));
717 solver.getProgramPointBefore(
718 &callee.getFunctionBody().front().front()));
722 solver.getProgramPointAfter(*returnOps.begin()));
723 auto *aliasClassLattice = solver.lookupState<AliasClassLattice>(value);
725 std::deque<DistinctAttr> frontier;
726 DenseSet<DistinctAttr> visited;
728 (void)aliasClasses.foreachElement(
731 "unhandled undefined/unknown case before visit");
732 if (!visited.contains(neighbor)) {
733 visited.insert(neighbor);
734 frontier.push_back(neighbor);
736 return ChangeResult::NoChange;
743 assert(!aliasClassLattice->isUndefined() &&
744 "didn't compute alias classes");
746 if (aliasClassLattice->isUnknown()) {
752 scheduleVisit(aliasClassLattice->getAliasClassesObject());
754 while (!frontier.empty()) {
755 DistinctAttr aliasClass = frontier.front();
756 frontier.pop_front();
760 if (fma->hasActiveData(aliasClass) &&
761 bma->activeDataFlowsOut(aliasClass))
766 assert(!pointsToSets->getPointsTo(aliasClass).isUndefined() &&
767 "couldn't compute points-to sets");
770 if (pointsToSets->getPointsTo(aliasClass).isUnknown())
773 scheduleVisit(pointsToSets->getPointsTo(aliasClass));
779 return !isActiveData(value);
782 std::function<bool(Operation *)> isConstantInstruction = [&](Operation *op) {
786 return llvm::none_of(op->getResults(), isActiveData);
790 if (
auto storeOp = dyn_cast<LLVM::StoreOp>(op)) {
791 if (!isConstantValue(storeOp.getValue())) {
794 }
else if (
auto callOp = dyn_cast<CallOpInterface>(op)) {
797 auto callable = cast<CallableOpInterface>(callOp.resolveCallable());
798 if (callable.getCallableRegion()) {
801 WalkResult result = callable->walk([&](Operation *op) {
802 if (!isConstantInstruction(op)) {
803 return WalkResult::interrupt();
805 return WalkResult::advance();
807 return !result.wasInterrupted();
812 return llvm::none_of(op->getOperands(), isActiveData) &&
813 llvm::none_of(op->getResults(), isActiveData);
816 llvm::errs() << FlatSymbolRefAttr::get(callee) <<
":\n";
817 for (BlockArgument arg : callee.getArguments()) {
818 if (Attribute tagAttr =
819 callee.getArgAttr(arg.getArgNumber(),
"enzyme.tag")) {
820 llvm::errs() <<
" " << tagAttr <<
": "
821 << (isConstantValue(arg) ?
"Constant" :
"Active") <<
"\n";
826 MLIRContext *ctx = callee.getContext();
828 func.walk([&](Operation *op) {
830 SmallVector<bool> argICVs(func.getNumArguments());
831 llvm::transform(func.getArguments(), argICVs.begin(),
833 func->setAttr(
"enzyme.icv", DenseBoolArrayAttr::get(ctx, argICVs));
837 op->setAttr(
"enzyme.ici",
838 BoolAttr::get(ctx, isConstantInstruction(op)));
841 if (op->getNumResults() == 0) {
843 }
else if (op->getNumResults() == 1) {
844 icv = isConstantValue(op->getResult(0));
847 "annotating icv for op that produces multiple results");
850 op->setAttr(
"enzyme.icv", BoolAttr::get(ctx, icv));
854 callee.walk([&](Operation *op) {
855 if (op->hasAttr(
"tag")) {
856 llvm::errs() <<
" " << op->getAttr(
"tag") <<
": ";
857 for (OpResult opResult : op->getResults()) {
858 llvm::errs() << (isConstantValue(opResult) ?
"Constant" :
"Active")
864 for (OpResult result : op->getResults()) {
865 auto forwardValueActivity =
867 if (forwardValueActivity) {
868 std::string dest, key{
"fva"};
869 llvm::raw_string_ostream os(dest);
870 if (op->getNumResults() != 1)
871 key += result.getResultNumber();
872 forwardValueActivity->getValue().print(os);
873 op->setAttr(key, StringAttr::get(op->getContext(), dest));
876 auto backwardValueActivity =
878 if (backwardValueActivity) {
879 std::string dest, key{
"bva"};
880 llvm::raw_string_ostream os(dest);
881 if (op->getNumResults() != 1)
882 key += result.getResultNumber();
883 backwardValueActivity->getValue().print(os);
884 op->setAttr(key, StringAttr::get(op->getContext(), dest));
892 for (BlockArgument arg : callee.getArguments()) {
893 auto backwardValueActivity =
895 if (backwardValueActivity) {
897 llvm::raw_string_ostream os(dest);
898 backwardValueActivity->getValue().print(os);
899 callee.setArgAttr(arg.getArgNumber(),
"enzyme.bva",
900 StringAttr::get(callee->getContext(), dest));
904 for (Operation *returnOp : returnOps) {
906 solver.getProgramPointAfter(returnOp));
908 llvm::errs() <<
"forward end state:\n" << *state <<
"\n";
910 llvm::errs() <<
"state was null\n";
914 solver.getProgramPointAfter(&callee.getFunctionBody().front().front()));
916 llvm::errs() <<
"backwards end state:\n" << *startState <<
"\n";
918 llvm::errs() <<
"backwards end state was null\n";
923 FunctionOpInterface callee, ArrayRef<enzyme::Activity> argumentActivity,
924 ArrayRef<enzyme::Activity> returnActivity,
bool print,
bool verbose,
926 SymbolTableCollection symbolTable;
927 DataFlowSolver solver;
936 argumentActivity, returnActivity);
939 solver.load<DeadCodeAnalysis>();
940 solver.load<SparseConstantPropagation>();
943 for (
const auto &[arg, activity] :
944 llvm::zip(callee.getArguments(), argumentActivity)) {
947 if (activity == enzyme::Activity::enzyme_active) {
956 SmallPtrSet<Operation *, 2> returnOps;
957 for (Operation &op : callee.getFunctionBody().getOps()) {
959 returnOps.insert(&op);
960 for (Value operand : op.getOperands()) {
961 auto *returnLattice =
964 if (isa<FloatType, ComplexType>(operand.getType())) {
971 if (failed(solver.initializeAndRun(callee->getParentOfType<ModuleOp>()))) {
972 assert(
false &&
"dataflow analysis failed\n");