30#include "mlir/Analysis/AliasAnalysis.h"
31#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
32#include "mlir/Analysis/DataFlow/SparseAnalysis.h"
33#include "mlir/Analysis/DataFlow/Utils.h"
34#include "mlir/Analysis/DataFlowFramework.h"
35#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
36#include "mlir/Interfaces/FunctionInterfaces.h"
37#include "mlir/Interfaces/SideEffectInterfaces.h"
38#include "mlir/Interfaces/ViewLikeInterface.h"
41#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
44#include "mlir/Dialect/Affine/IR/AffineOps.h"
45#include "mlir/Dialect/MemRef/IR/MemRef.h"
46#include "llvm/ADT/SetOperations.h"
49using namespace mlir::dataflow;
52 return isa<MemRefType, LLVM::LLVMPointerType, enzyme::CacheType>(type);
60static ChangeResult
mergeSets(DenseSet<T> &dest,
const DenseSet<T> &src) {
61 size_t oldSize = dest.size();
62 dest.insert(src.begin(), src.end());
63 return dest.size() == oldSize ? ChangeResult::NoChange : ChangeResult::Change;
68 DenseMap<DistinctAttr, enzyme::AliasClassSet> &summaryMap) {
69 for (
auto pair : summaryAttr.getAsRange<ArrayAttr>()) {
70 assert(pair.size() == 2 &&
71 "Expected summary to be in [[key, value]] format");
72 auto pointer = cast<DistinctAttr>(pair[0]);
75 if (
auto strAttr = dyn_cast<StringAttr>(pair[1])) {
77 (void)pointsToSet.markUnknown();
80 "unrecognized points-to destination");
83 auto pointsTo = cast<ArrayAttr>(pair[1]).getAsRange<DistinctAttr>();
86 (void)pointsToSet.insert(
87 DenseSet<DistinctAttr>(pointsTo.begin(), pointsTo.end()));
89 summaryMap.insert({pointer, pointsToSet});
98 for (
const auto &[srcClass, destClasses] :
map) {
99 os <<
" " << srcClass <<
" points to {";
100 if (destClasses.isUnknown()) {
102 }
else if (destClasses.isUndefined()) {
105 llvm::interleaveComma(destClasses.getElements(), os);
111ChangeResult enzyme::PointsToSets::update(
const AliasClassSet &keysToUpdate,
115 return markAllPointToUnknown();
119 return ChangeResult::NoChange;
123 assert(state == AliasClassSet::State::Defined &&
124 "unknown must have been handled above");
125 return joinPotentiallyMissing(dest, values);
138 return markAllPointToUnknown();
140 return ChangeResult::NoChange;
144 assert(destState == AliasClassSet::State::Defined);
148 if (srcState == AliasClassSet::State::Unknown)
150 else if (srcState == AliasClassSet::State::Defined) {
151 auto it = map.find(src);
153 srcClasses = &it->getSecond();
155 return joinPotentiallyMissing(dest, *srcClasses);
163 return markAllPointToUnknown();
165 return ChangeResult::NoChange;
176 return ChangeResult::NoChange;
178 ChangeResult result = ChangeResult::NoChange;
179 for (
auto &[key, value] : map) {
181 result |= value.markUnknown();
188 if (state == AliasClassSet::State::Defined)
189 assert(map.contains(dest) &&
"unknown dest cannot be preserved");
190 return ChangeResult::NoChange;
198std::optional<Value>
getStored(Operation *op);
202 ProgramPoint *dependent,
PointsToSets *after, Value capturedValue,
205 getOrCreateFor<AliasClassLattice>(dependent, capturedValue);
207 getOrCreateFor<AliasClassLattice>(dependent, destinationAddress);
211 if (destClasses->isUnknown()) {
218 if (srcClasses->isUnknown()) {
225 destClasses->getAliasClassesObject(),
226 srcClasses->getAliasClassesObject()));
228 propagateIfChanged(after,
229 after->
insert(destClasses->getAliasClassesObject(),
230 srcClasses->getAliasClassesObject()));
247 return isa<LLVM::NoAliasScopeDeclOp, LLVM::LifetimeStartOp,
248 LLVM::LifetimeEndOp, LLVM::AssumeOp, LLVM::UnreachableOp>(op);
257 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
259 if (
isNoOp(op) || isMemoryEffectFree(op))
265 SmallVector<MemoryEffects::EffectInstance> effects;
266 memory.getEffects(effects);
270 if (effects.size() == 1 &&
271 isa<MemoryEffects::Allocate>(effects.front().getEffect()) &&
272 effects.front().getValue()) {
273 const auto *destClasses = getOrCreateFor<AliasClassLattice>(
274 getProgramPointAfter(op), effects.front().getValue());
280 for (
const auto &effect : effects) {
281 if (!isa<MemoryEffects::Write>(effect.getEffect()))
284 SmallVector<Value> targetValues;
285 Value value = effect.getValue();
286 auto pointerLikeOperands =
287 llvm::make_filter_range(op->getOperands(), [](Value operand) {
288 return isPointerLike(operand.getType());
294 targetValues.push_back(value);
296 llvm::append_range(targetValues, pointerLikeOperands);
302 SmallVector<Value> storedValues;
303 if (std::optional<Value> stored =
getStored(op)) {
304 storedValues.push_back(*stored);
305 }
else if (std::optional<Value> stored =
getCopySource(op)) {
308 llvm::append_range(storedValues, pointerLikeOperands);
311 for (Value address : targetValues) {
312 for (Value stored : storedValues) {
313 processCapturingStore(getProgramPointAfter(op), after, stored, address,
324 return modRef ? (*modRef == LLVM::ModRefInfo::Mod ||
325 *modRef == LLVM::ModRefInfo::ModRef)
330 return modRef ? (*modRef == LLVM::ModRefInfo::Ref ||
331 *modRef == LLVM::ModRefInfo::ModRef)
335static bool mayReadArg(FunctionOpInterface callee,
unsigned argNo,
336 std::optional<LLVM::ModRefInfo> argMemMRI) {
341 unsigned numArguments = callee.getNumArguments();
342 if (argNo >= numArguments)
345 bool hasWriteOnlyAttr =
346 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getWriteOnlyAttrName());
347 bool hasReadNoneAttr =
348 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadnoneAttrName());
349 return !hasWriteOnlyAttr && !hasReadNoneAttr && funcMayRead;
352static bool mayWriteArg(FunctionOpInterface callee,
unsigned argNo,
353 std::optional<LLVM::ModRefInfo> argMemMRI) {
358 unsigned numArguments = callee.getNumArguments();
359 if (argNo >= numArguments)
364 bool hasReadOnlyAttr =
365 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadonlyAttrName());
366 bool hasReadNoneAttr =
367 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadnoneAttrName());
368 return !hasReadOnlyAttr && !hasReadNoneAttr && funcMayWrite;
373static std::optional<LLVM::ModRefInfo>
376 StringRef name = cast<SymbolOpInterface>(func.getOperation()).getName();
377 auto hardcoded = llvm::StringSwitch<std::optional<LLVM::ModRefInfo>>(name)
379 .Case(
"printf", LLVM::ModRefInfo::Ref)
380 .Case(
"puts", LLVM::ModRefInfo::Ref)
381 .Case(
"memset_pattern16", LLVM::ModRefInfo::ModRef)
382 .Case(
"free", LLVM::ModRefInfo::NoModRef)
384 .Case(
"_ZdlPv", LLVM::ModRefInfo::NoModRef)
385 .Default(std::nullopt);
389 if (
auto memoryAttr =
391 return memoryAttr.getArgMem();
398static std::optional<LLVM::ModRefInfo>
401 StringRef name = cast<SymbolOpInterface>(func.getOperation()).getName();
403 llvm::StringSwitch<std::optional<LLVM::ModRefInfo>>(name)
406 .Case(
"printf", LLVM::ModRefInfo::NoModRef)
407 .Case(
"puts", LLVM::ModRefInfo::NoModRef)
408 .Case(
"gettimeofday", LLVM::ModRefInfo::Ref)
410 .Case(
"_ZdlPv", LLVM::ModRefInfo::NoModRef)
411 .Case(
"free", LLVM::ModRefInfo::NoModRef)
412 .Case(
"memset_pattern16", LLVM::ModRefInfo::NoModRef)
413 .Default(std::nullopt);
417 if (
auto memoryAttr =
419 return memoryAttr.getOther();
424 CallOpInterface call,
425 const DenseMap<DistinctAttr, enzyme::AliasClassSet> &summary,
427 StringRef calleeName = cast<SymbolRefAttr>(call.getCallableForCallee())
430 auto lookup = [&](
unsigned argNumber,
431 unsigned depth) -> std::optional<AliasClassSet> {
432 for (
const auto &[attr, aliasClassSet] : summary) {
433 if (
auto pseudoClass = dyn_cast_if_present<PseudoAliasClassAttr>(
434 attr.getReferencedAttr())) {
435 if (pseudoClass.getFunction().getValue() == calleeName &&
436 pseudoClass.getArgNumber() == argNumber &&
437 pseudoClass.getDepth() == depth) {
438 return aliasClassSet;
445 ChangeResult changed = ChangeResult::NoChange;
447 for (
auto &&[i, argOperand] : llvm::enumerate(call.getArgOperands())) {
448 auto *arg = getOrCreateFor<AliasClassLattice>(getProgramPointAfter(call),
451 std::optional<AliasClassSet> calleePointsTo = lookup(i, 0);
459 if (calleePointsTo->isUnknown()) {
460 changed |= after->
insert(arg->getAliasClassesObject(),
464 if (calleePointsTo->isUndefined())
467 for (DistinctAttr ac : calleePointsTo->getElements()) {
468 if (!isa<PseudoAliasClassAttr>(ac.getReferencedAttr())) {
480 propagateIfChanged(after, changed);
486static std::optional<LLVM::ModRefInfo>
488 if (
auto memoryAttr =
490 return memoryAttr.getInaccessibleMem();
495 CallOpInterface call, CallControlFlowAction action,
499 if (action == CallControlFlowAction::ExternalCallee) {
503 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
508 if (symbol.getLeafReference().getValue() ==
"posix_memalign") {
511 OperandRange arguments = call.getArgOperands();
512 auto *memPtr = getOrCreateFor<AliasClassLattice>(
513 getProgramPointAfter(call), arguments[0]);
519 originalClasses.getOriginalClass(arguments[0],
"memalign"));
520 return propagateIfChanged(
522 single.getAliasClassesObject()));
535 if (
auto callee = SymbolTable::lookupNearestSymbolFrom<FunctionOpInterface>(
536 call, symbol.getLeafReference())) {
537 if (
auto summaryAttr = callee->getAttrOfType<ArrayAttr>(
538 EnzymeDialect::getPointerSummaryAttrName())) {
539 DenseMap<DistinctAttr, AliasClassSet> summary;
541 return processCallToSummarizedFunc(call, summary, after);
545 std::optional<LLVM::ModRefInfo> otherModRef =
548 SmallVector<unsigned> pointerLikeOperands;
549 for (
auto &&[i, operand] : llvm::enumerate(call.getArgOperands())) {
551 pointerLikeOperands.push_back(i);
560 unsigned numArguments = callee.getNumArguments();
561 if (funcMayReadOther) {
566 for (
unsigned pointerAsData : pointerLikeOperands) {
568 if ((pointerAsData < numArguments &&
569 !!callee.getArgAttr(pointerAsData,
570 LLVM::LLVMDialect::getNoCaptureAttrName())))
573 const auto *srcClasses = getOrCreateFor<AliasClassLattice>(
574 getProgramPointAfter(call), call.getArgOperands()[pointerAsData]);
575 (void)functionMayCapture.
join(srcClasses->getAliasClassesObject());
583 ChangeResult changed = ChangeResult::NoChange;
584 for (
unsigned pointerOperand : pointerLikeOperands) {
585 auto *destClasses = getOrCreateFor<AliasClassLattice>(
586 getProgramPointAfter(call), call.getArgOperands()[pointerOperand]);
589 if (!
mayWriteArg(callee, pointerOperand, argModRef)) {
590 (void)nonWritableOperandClasses.
join(
591 destClasses->getAliasClassesObject());
594 (void)writableClasses.
join(destClasses->getAliasClassesObject());
599 if (destClasses->isUnknown()) {
605 if (destClasses->isUndefined())
611 changed |= destClasses->getAliasClassesObject().foreachElement(
613 return after->
insert(dest, functionMayCapture);
645 for (OpResult result : call->getResults()) {
663 const auto *destClasses = getOrCreateFor<AliasClassLattice>(
664 getProgramPointAfter(call), result);
667 if (destClasses->isUnknown() || nonWritableOperandClasses.
isUnknown()) {
668 (void)resultWithoutNonWritableOperands.
markUnknown();
669 }
else if (!destClasses->isUndefined() &&
671 DenseSet<DistinctAttr> nonOperandClasses =
672 llvm::set_difference(destClasses->getAliasClasses(),
674 (void)resultWithoutNonWritableOperands.
insert(nonOperandClasses);
676 (void)resultWithoutNonWritableOperands.
join(
677 destClasses->getAliasClassesObject());
681 if (funcMayReadOther) {
683 resultWithoutNonWritableOperands));
687 for (
unsigned operandNo : pointerLikeOperands) {
688 const auto *srcClasses = getOrCreateFor<AliasClassLattice>(
689 getProgramPointAfter(call), call.getArgOperands()[operandNo]);
690 if (
mayReadArg(callee, operandNo, argModRef)) {
691 changed |= after->
addSetsFrom(resultWithoutNonWritableOperands,
692 srcClasses->getAliasClassesObject());
696 (operandNo < numArguments &&
697 !!callee.getArgAttr(operandNo,
698 LLVM::LLVMDialect::getNoCaptureAttrName()));
701 changed |= after->
insert(resultWithoutNonWritableOperands,
702 srcClasses->getAliasClassesObject());
705 return propagateIfChanged(after, changed);
721 if (elements.isUnknown()) {
723 }
else if (elements.isUndefined()) {
724 os <<
"Undefined AC";
726 os <<
"size: " << elements.getElements().size() <<
":\n";
727 for (
auto aliasClass : elements.getElements()) {
728 os <<
" " << aliasClass <<
"\n";
737 assert(!isUndefined() && !rhs->isUndefined() &&
"incomplete alias analysis");
739 if (getAnchor() == rhs->getAnchor())
740 return AliasResult::MustAlias;
742 if (elements.isUnknown() || rhs->elements.isUnknown())
743 return AliasResult::MayAlias;
746 llvm::count_if(elements.getElements(), [rhs](DistinctAttr aliasClass) {
747 return rhs->elements.getElements().contains(aliasClass);
751 return AliasResult::NoAlias;
761 return AliasResult::MayAlias;
768 return elements.
join(otherAliasClass->elements);
776 if (
auto arg = dyn_cast<BlockArgument>(lattice->getAnchor())) {
778 dyn_cast<FunctionOpInterface>(arg.getOwner()->getParentOp())) {
783 funcOp.getArgAttr(arg.getArgNumber(),
784 LLVM::LLVMDialect::getNoAliasAttrName())) {
791 Attribute debugLabel = funcOp.getArgAttrOfType<StringAttr>(
792 arg.getArgNumber(),
"enzyme.tag");
795 PseudoAliasClassAttr::get(FlatSymbolRefAttr::get(funcOp),
796 arg.getArgNumber(), 0);
798 DistinctAttr argClass = originalClasses.getOriginalClass(
799 lattice->getAnchor(), debugLabel);
800 return propagateIfChanged(lattice,
802 lattice->getAnchor(), argClass)));
804 return propagateIfChanged(lattice,
806 lattice->getAnchor(), entryClass)));
824 if (
auto call = dyn_cast<CallOpInterface>(op)) {
825 if (
auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee())) {
826 if (symbol.getLeafReference().getValue() ==
"malloc") {
831 return isa<memref::LoadOp, memref::StoreOp, affine::AffineLoadOp,
832 affine::AffineStoreOp, LLVM::LoadOp, LLVM::StoreOp, enzyme::PushOp,
836void enzyme::AliasAnalysis::transfer(
837 Operation *op, ArrayRef<MemoryEffects::EffectInstance> effects,
838 ArrayRef<const AliasClassLattice *> operands,
839 ArrayRef<AliasClassLattice *> results) {
840 bool globalRead =
false;
841 for (
const auto &effect : effects) {
843 Value value = effect.getValue();
845 globalRead |= isa<MemoryEffects::Read>(effect.getEffect());
849 if (isa<MemoryEffects::Allocate>(effect.getEffect())) {
851 for (AliasClassLattice *result : results) {
852 if (result->getAnchor() == value) {
853 std::string debugLabel;
854 llvm::raw_string_ostream sstream(debugLabel);
858 if (op->hasAttr(
"tag")) {
859 if (
auto stringTag = dyn_cast<StringAttr>(op->getAttr(
"tag"))) {
860 sstream << stringTag.getValue();
862 op->getAttr(
"tag").print(sstream);
866 result->getAnchor(), originalClasses.getOriginalClass(
867 result->getAnchor(), debugLabel));
868 propagateIfChanged(result, result->join(fresh));
871 }
else if (isa<MemoryEffects::Read>(effect.getEffect())) {
872 auto *pointsToSets = getOrCreateFor<PointsToSets>(
873 getProgramPointAfter(op), getProgramPointAfter(op));
874 AliasClassLattice *latticeElement = getLatticeElement(value);
875 if (latticeElement->isUnknown()) {
876 for (AliasClassLattice *result : results) {
877 propagateIfChanged(result, result->markUnknown());
879 }
else if (latticeElement->isUndefined()) {
882 for (
auto srcClass : latticeElement->getAliasClasses()) {
883 const auto &srcPointsTo = pointsToSets->getPointsTo(srcClass);
884 for (AliasClassLattice *result : results) {
890 if (srcPointsTo.isUnknown()) {
891 propagateIfChanged(result, result->markUnknown());
892 }
else if (srcPointsTo.isUndefined()) {
894 createImplicitArgDereference(op, latticeElement, srcClass,
897 propagateIfChanged(result,
898 result->insert(srcPointsTo.getElements()));
910 for (
auto *resultLattice : results)
911 propagateIfChanged(resultLattice, resultLattice->markUnknown());
920 for (AliasClassLattice *resultLattice : results) {
924 constexpr bool pruneNonPointers =
false;
925 if (pruneNonPointers &&
929 ChangeResult r = ChangeResult::NoChange;
930 for (
const AliasClassLattice *operandLattice : operands)
931 r |= resultLattice->join(*operandLattice);
932 propagateIfChanged(resultLattice, r);
936void enzyme::AliasAnalysis::createImplicitArgDereference(
937 Operation *op, AliasClassLattice *source, DistinctAttr srcClass,
938 AliasClassLattice *result) {
939 assert(relative &&
"only valid to create implicit argument dereferences when "
940 "operating in relative mode");
942 Value readResult = result->getAnchor();
943 auto parent = op->getParentOfType<FunctionOpInterface>();
944 assert(parent &&
"failed to find function parent");
945 auto *entryPointsToSets = getOrCreateFor<PointsToSets>(
946 getProgramPointAfter(op),
947 getProgramPointAfter(&parent.getCallableRegion()->front()));
948 if (!entryPointsToSets->getPointsTo(srcClass).isUndefined()) {
953 if (
auto pseudoClass = dyn_cast_if_present<PseudoAliasClassAttr>(
954 srcClass.getReferencedAttr())) {
955 auto pseudoDeref = PseudoAliasClassAttr::get(pseudoClass.getFunction(),
956 pseudoClass.getArgNumber(),
957 pseudoClass.getDepth() + 1);
958 DistinctAttr derefClass =
959 originalClasses.getOriginalClass(readResult, pseudoDeref);
961 readResult, derefClass)));
963 auto *pointsToState = getOrCreate<PointsToSets>(
964 getProgramPointBefore(&parent.getCallableRegion()->front()));
965 propagateIfChanged(pointsToState,
966 pointsToState->insert(source->getAliasClassesObject(),
972 CallOpInterface call,
973 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
974 auto rng = call.getArgOperands();
975 for (
auto it = rng.begin(); it != rng.end(); it++) {
977 auto argOp = it.getBase();
978 assert(argOp->get() == argument);
982 effects.emplace_back(MemoryEffects::Read::get(), argOp);
983 effects.emplace_back(MemoryEffects::Write::get(), argOp);
990 CallOpInterface call,
991 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
992 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
997 StringRef callableName = symbol.getLeafReference().getValue();
998 if (callableName ==
"malloc" || callableName ==
"calloc" ||
999 callableName ==
"_Znwm") {
1000 assert(call->getNumResults() == 1);
1001 effects.push_back(MemoryEffects::EffectInstance(
1002 MemoryEffects::Allocate::get(), call->getResult(0)));
1010 Operation *op, ArrayRef<const AliasClassLattice *> operands,
1011 ArrayRef<AliasClassLattice *> results) {
1015 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
1017 for (OpResult result : op->getResults()) {
1021 (void)results[result.getResultNumber()]->markUnknown();
1026 SmallVector<MemoryEffects::EffectInstance> effects;
1027 memory.getEffects(effects);
1028 transfer(op, effects, operands, results);
1029 if (
auto view = dyn_cast<OffsetSizeAndStrideOpInterface>(op)) {
1039 SmallVectorImpl<enzyme::AliasClassSet> &out) {
1040 for (Attribute element : summary) {
1041 if (
auto strAttr = dyn_cast<StringAttr>(element)) {
1050 for (DistinctAttr aliasClass :
1051 cast<ArrayAttr>(element).getAsRange<DistinctAttr>()) {
1052 (void)aliasClasses.
insert({aliasClass});
1054 out.push_back(aliasClasses);
1060 CallOpInterface call, ArrayRef<const AliasClassLattice *> operands,
1061 ArrayRef<AliasClassLattice *> results) {
1063 SmallVector<MemoryEffects::EffectInstance> effects;
1065 return transfer(call, effects, operands, results);
1067 auto markResultsUnknown = [&] {
1069 propagateIfChanged(resultLattice, resultLattice->markUnknown());
1079 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
1081 return markResultsUnknown();
1082 auto callee = SymbolTable::lookupNearestSymbolFrom<FunctionOpInterface>(
1083 call, symbol.getLeafReference());
1085 return markResultsUnknown();
1087 if (
auto aliasSummaryAttr = callee->getAttrOfType<ArrayAttr>(
1088 EnzymeDialect::getAliasSummaryAttrName())) {
1090 SmallVector<AliasClassSet> aliasSummary;
1093 assert(results.size() == aliasSummary.size());
1094 for (
auto &&[i, resultSummary] : llvm::enumerate(aliasSummary)) {
1098 ChangeResult changed = ChangeResult::NoChange;
1099 if (resultSummary.isUndefined())
1101 if (resultSummary.isUnknown())
1104 changed |= resultSummary.foreachElement(
1106 assert(state == AliasClassSet::State::Defined);
1107 if (
auto pseudoClass = dyn_cast<PseudoAliasClassAttr>(
1108 aliasClass.getReferencedAttr())) {
1110 pseudoClass.getDepth() == 0 &&
1111 "sparse alias summaries for depth > 0 not yet implemented");
1112 return result->
join(*operands[pseudoClass.getArgNumber()]);
1114 return result->
insert({aliasClass});
1118 propagateIfChanged(result, changed);
1126 std::optional<LLVM::ModRefInfo> inaccessibleModRef =
1129 for (
auto [operandNo, operand] : llvm::enumerate(call.getArgOperands())) {
1136 if (!
mayReadArg(callee, operandNo, argModRef))
1141 const auto *pointsToLattice = getOrCreateFor<PointsToSets>(
1142 getProgramPointAfter(call), getProgramPointAfter(call));
1147 if (srcClass ==
nullptr)
1148 return ChangeResult::NoChange;
1149 (void)operandAliasClasses.join(
1150 pointsToLattice->getPointsTo(srcClass));
1151 return ChangeResult::NoChange;
1155 auto debugLabel = call->getAttrOfType<StringAttr>(
"tag");
1156 DistinctAttr commonResultAttr =
nullptr;
1160 SmallVector<Value> aliasGroupResults;
1161 for (OpResult result : call->getResults()) {
1162 if (!callee.getResultAttr(result.getResultNumber(),
1163 LLVM::LLVMDialect::getNoAliasAttrName()))
1164 aliasGroupResults.push_back(result);
1167 for (OpResult result : call->getResults()) {
1169 if (!llvm::is_contained(aliasGroupResults, result)) {
1170 Attribute individualDebugLabel =
1172 ? StringAttr::get(debugLabel.getContext(),
1173 debugLabel.getValue().str() +
1174 std::to_string(result.getResultNumber()))
1177 resultLattice->getAnchor(),
1178 originalClasses.getOriginalClass(resultLattice->getAnchor(),
1179 individualDebugLabel));
1180 propagateIfChanged(resultLattice, resultLattice->
join(individualAlloc));
1184 if (!commonResultAttr) {
1185 std::string label = !debugLabel
1186 ?
"func-result-common"
1187 : debugLabel.getValue().str() +
"-common";
1189 originalClasses.getSameOriginalClass(aliasGroupResults, label);
1193 resultLattice->getAnchor(), std::move(commonClass)));
1201 resultLattice->getAnchor(),
AliasClassSet(operandAliasClasses)));
1202 propagateIfChanged(resultLattice, changed);
1204 propagateIfChanged(resultLattice, resultLattice->
markUnknown());
1210 CallableOpInterface funcOp, function_ref<
void(Operation *op)> annotate) {
1211 DataFlowSolver solver(DataFlowConfig().setInterprocedural(
false));
1212 dataflow::loadBaselineAnalyses(solver);
1215 if (failed(solver.initializeAndRun(funcOp))) {
1216 assert(
false &&
"dataflow analysis failed");
1221 AliasClassLattice modified(
1222 nullptr, DistinctAttr::create(UnitAttr::get(funcOp.getContext())));
1223 funcOp.walk([&](Operation *op) {
1224 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
1228 if (isMemoryEffectFree(op) ||
1229 isa<FunctionOpInterface, RegionBranchOpInterface>(op) ||
isNoOp(op)) {
1230 return WalkResult::advance();
1232 (void)modified.markUnknown();
1233 return WalkResult::interrupt();
1236 SmallVector<MemoryEffects::EffectInstance> effects;
1237 memory.getEffects(effects);
1238 for (
const auto &effect : effects) {
1239 if (isa<MemoryEffects::Write>(effect.getEffect())) {
1240 Value val = effect.getValue();
1242 (void)modified.join(*solver.lookupState<AliasClassLattice>(val));
1244 (void)modified.markUnknown();
1245 return WalkResult::interrupt();
1249 return WalkResult::advance();
1252 funcOp.walk([&](MemoryEffectOpInterface memory) {
1253 if (!hasSingleEffect<MemoryEffects::Read>(memory)) {
1256 SmallVector<MemoryEffects::EffectInstance> effects;
1257 memory.getEffects(effects);
1258 assert(effects.size() == 1 &&
1259 isa<MemoryEffects::Read>(effects.front().getEffect()));
1260 Value ptr = effects.front().getValue();
1261 auto *ptrClass = solver.lookupState<AliasClassLattice>(ptr);
1265 if (ptrClass->alias(modified).isNo()) {
static void deserializePointsTo(ArrayAttr summaryAttr, DenseMap< DistinctAttr, enzyme::ValueOriginSet > &summaryMap)
std::optional< Value > getCopySource(Operation *op)
static bool isPointerLike(Type type)
static constexpr llvm::StringLiteral kLLVMMemoryAttrName
static std::optional< LLVM::ModRefInfo > getFunctionInaccessibleModRef(FunctionOpInterface func)
Returns information indicating whether the function may read or write into memory previously inaccess...
static void deserializePointsTo(ArrayAttr summaryAttr, DenseMap< DistinctAttr, enzyme::AliasClassSet > &summaryMap)
static bool mayReadArg(FunctionOpInterface callee, unsigned argNo, std::optional< LLVM::ModRefInfo > argMemMRI)
static std::optional< LLVM::ModRefInfo > getFunctionArgModRef(FunctionOpInterface func)
Returns information indicating whether the function may read or write into the memory pointed to by i...
LogicalResult getEffectsForExternalCall(CallOpInterface call, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
static bool mayWriteArg(FunctionOpInterface callee, unsigned argNo, std::optional< LLVM::ModRefInfo > argMemMRI)
static void deserializeAliasSummary(ArrayAttr summary, SmallVectorImpl< enzyme::AliasClassSet > &out)
static bool isAliasTransferFullyDescribedByMemoryEffects(Operation *op)
Returns true if the alias transfer function of the operation is fully described by its memory effects...
static bool isNoOp(Operation *op)
static bool isMustStore(Operation *op, Value pointer)
static bool modRefMayMod(std::optional< LLVM::ModRefInfo > modRef)
static ChangeResult mergeSets(DenseSet< T > &dest, const DenseSet< T > &src)
static std::optional< LLVM::ModRefInfo > getFunctionOtherModRef(FunctionOpInterface func)
Returns information indicating whether the function may read or write into the memory other than that...
void populateConservativeCallEffects(CallOpInterface call, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
static bool modRefMayRef(std::optional< LLVM::ModRefInfo > modRef)
std::optional< Value > getStored(Operation *op)
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
This analysis implements interprocedural alias analysis.
LogicalResult visitOperation(Operation *op, ArrayRef< const AliasClassLattice * > operands, ArrayRef< AliasClassLattice * > results) override
void visitExternalCall(CallOpInterface call, ArrayRef< const AliasClassLattice * > operands, ArrayRef< AliasClassLattice * > results) override
void setToEntryState(AliasClassLattice *lattice) override
const AliasClassSet & getAliasClassesObject() const
void print(raw_ostream &os) const override
ChangeResult join(const AbstractSparseLattice &other) override
static AliasClassLattice single(Value point, DistinctAttr value)
::mlir::AliasResult alias(const AbstractSparseLattice &other) const
DenseMap< DistinctAttr, SetLattice< DistinctAttr > > map
void processCallToSummarizedFunc(CallOpInterface call, const DenseMap< DistinctAttr, AliasClassSet > &summary, PointsToSets *after)
void setToEntryState(PointsToSets *lattice) override
void processCapturingStore(ProgramPoint *dependent, PointsToSets *after, Value capturedValue, Value destinationAddress, bool isMustStore=false)
LogicalResult visitOperation(Operation *op, const PointsToSets &before, PointsToSets *after) override
void visitCallControlFlowTransfer(CallOpInterface call, dataflow::CallControlFlowAction action, const PointsToSets &before, PointsToSets *after) override
ChangeResult insert(const AliasClassSet &destClasses, const AliasClassSet &values)
Mark the pointer stored in dest as possibly pointing to any of values, in addition to the values it m...
ChangeResult markAllExceptPointToUnknown(const AliasClassSet &destClasses)
Mark all alias classes except the given ones to point to the "unknown" alias set.
ChangeResult setPointingToClasses(const AliasClassSet &destClasses, const AliasClassSet &values)
Mark the pointer stored in dest as possibly pointing to any of values, instead of the values it may b...
ChangeResult markAllPointToUnknown()
Mark the entire data structure as "unknown", that is, any pointer may be containing any other pointer...
ChangeResult markPointToUnknown(const AliasClassSet &destClasses)
Mark dest as pointing to "unknown" alias set, that is, any possible other pointer.
ChangeResult setPointingToEmpty(const AliasClassSet &destClasses)
void print(raw_ostream &os) const override
ChangeResult addSetsFrom(const AliasClassSet &destClasses, const AliasClassSet &srcClasses)
For every alias class in dest, record that it may additionally be pointing to the same as the classes...
ChangeResult markUnknown()
DenseSet< ValueT > & getElements()
ChangeResult insert(const DenseSet< ValueT > &newElements)
ChangeResult foreachElement(function_ref< ChangeResult(ValueT, State)> callback) const
ChangeResult join(const SetLattice< ValueT > &other)
static const SetLattice< DistinctAttr > & getEmpty()
static const SetLattice< DistinctAttr > & getUndefined()
static const SetLattice< DistinctAttr > & getUnknown()
ChangeResult markUnknown()
ChangeResult insert(const DenseSet< ValueT > &newElements)
constexpr llvm::StringLiteral unknownSetString
SetLattice< DistinctAttr > AliasClassSet
A set of alias class identifiers to be treated as a single union.
void markReadOnlyLoads(CallableOpInterface funcOp, function_ref< void(Operation *)> annotate)
constexpr llvm::StringLiteral undefinedSetString