30#include "mlir/Analysis/AliasAnalysis.h"
31#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
32#include "mlir/Analysis/DataFlow/SparseAnalysis.h"
33#include "mlir/Analysis/DataFlowFramework.h"
34#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
35#include "mlir/Interfaces/CastInterfaces.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/MemRef/IR/MemRef.h"
45#include "llvm/ADT/SetOperations.h"
48using namespace mlir::dataflow;
51 return isa<MemRefType, LLVM::LLVMPointerType>(type);
59static ChangeResult
mergeSets(DenseSet<T> &dest,
const DenseSet<T> &src) {
60 size_t oldSize = dest.size();
61 dest.insert(src.begin(), src.end());
62 return dest.size() == oldSize ? ChangeResult::NoChange : ChangeResult::Change;
67 DenseMap<DistinctAttr, enzyme::AliasClassSet> &summaryMap) {
68 for (
auto pair : summaryAttr.getAsRange<ArrayAttr>()) {
69 assert(pair.size() == 2 &&
70 "Expected summary to be in [[key, value]] format");
71 auto pointer = cast<DistinctAttr>(pair[0]);
74 if (
auto strAttr = dyn_cast<StringAttr>(pair[1])) {
76 (void)pointsToSet.markUnknown();
79 "unrecognized points-to destination");
82 auto pointsTo = cast<ArrayAttr>(pair[1]).getAsRange<DistinctAttr>();
85 (void)pointsToSet.insert(
86 DenseSet<DistinctAttr>(pointsTo.begin(), pointsTo.end()));
88 summaryMap.insert({pointer, pointsToSet});
97 for (
const auto &[srcClass, destClasses] :
map) {
98 os <<
" " << srcClass <<
" points to {";
99 if (destClasses.isUnknown()) {
101 }
else if (destClasses.isUndefined()) {
104 llvm::interleaveComma(destClasses.getElements(), os);
110ChangeResult enzyme::PointsToSets::update(
const AliasClassSet &keysToUpdate,
114 return markAllPointToUnknown();
118 return ChangeResult::NoChange;
122 assert(state == AliasClassSet::State::Defined &&
123 "unknown must have been handled above");
126 auto it = map.find(dest);
127 if (it != map.end()) {
133 (void)valuesCopy.join(it->getSecond());
134 values.
print(llvm::errs());
135 llvm::errs() <<
"\n";
136 it->getSecond().print(llvm::errs());
137 llvm::errs() <<
"\n";
138 valuesCopy.print(llvm::errs());
139 llvm::errs() <<
"\n";
140 assert(valuesCopy == values &&
141 "attempting to replace a pointsTo entry with an alias class "
142 "set that is ordered _before_ the existing one -> "
143 "non-monotonous update ");
147 return joinPotentiallyMissing(dest, values);
160 return markAllPointToUnknown();
162 return ChangeResult::NoChange;
166 assert(destState == AliasClassSet::State::Defined);
170 if (srcState == AliasClassSet::State::Unknown)
172 else if (srcState == AliasClassSet::State::Defined) {
173 auto it = map.find(src);
175 srcClasses = &it->getSecond();
177 return joinPotentiallyMissing(dest, *srcClasses);
185 return markAllPointToUnknown();
187 return ChangeResult::NoChange;
198 return ChangeResult::NoChange;
200 ChangeResult result = ChangeResult::NoChange;
201 for (
auto &[key, value] : map) {
203 result |= value.markUnknown();
210 if (state == AliasClassSet::State::Defined)
211 assert(map.contains(dest) &&
"unknown dest cannot be preserved");
212 return ChangeResult::NoChange;
220std::optional<Value>
getStored(Operation *op);
224 ProgramPoint *dependent,
PointsToSets *after, Value capturedValue,
227 getOrCreateFor<AliasClassLattice>(dependent, capturedValue);
229 getOrCreateFor<AliasClassLattice>(dependent, destinationAddress);
233 if (destClasses->isUnknown()) {
240 if (srcClasses->isUnknown()) {
247 destClasses->getAliasClassesObject(),
248 srcClasses->getAliasClassesObject()));
250 propagateIfChanged(after,
251 after->
insert(destClasses->getAliasClassesObject(),
252 srcClasses->getAliasClassesObject()));
269 return isa<LLVM::NoAliasScopeDeclOp, LLVM::LifetimeStartOp,
270 LLVM::LifetimeEndOp, LLVM::AssumeOp, LLVM::UnreachableOp>(op);
279 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
287 SmallVector<MemoryEffects::EffectInstance> effects;
288 memory.getEffects(effects);
292 if (effects.size() == 1 &&
293 isa<MemoryEffects::Allocate>(effects.front().getEffect()) &&
294 effects.front().getValue()) {
295 const auto *destClasses = getOrCreateFor<AliasClassLattice>(
296 getProgramPointAfter(op), effects.front().getValue());
302 for (
const auto &effect : effects) {
303 if (!isa<MemoryEffects::Write>(effect.getEffect()))
306 SmallVector<Value> targetValues;
307 Value value = effect.getValue();
308 auto pointerLikeOperands =
309 llvm::make_filter_range(op->getOperands(), [](Value operand) {
310 return isPointerLike(operand.getType());
316 targetValues.push_back(value);
318 llvm::append_range(targetValues, pointerLikeOperands);
324 SmallVector<Value> storedValues;
325 if (std::optional<Value> stored =
getStored(op)) {
326 storedValues.push_back(*stored);
327 }
else if (std::optional<Value> stored =
getCopySource(op)) {
330 llvm::append_range(storedValues, pointerLikeOperands);
333 for (Value address : targetValues) {
334 for (Value stored : storedValues) {
335 processCapturingStore(getProgramPointAfter(op), after, stored, address,
346 return modRef ? (*modRef == LLVM::ModRefInfo::Mod ||
347 *modRef == LLVM::ModRefInfo::ModRef)
352 return modRef ? (*modRef == LLVM::ModRefInfo::Ref ||
353 *modRef == LLVM::ModRefInfo::ModRef)
357static bool mayReadArg(FunctionOpInterface callee,
unsigned argNo,
358 std::optional<LLVM::ModRefInfo> argMemMRI) {
363 unsigned numArguments = callee.getNumArguments();
364 if (argNo >= numArguments)
367 bool hasWriteOnlyAttr =
368 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getWriteOnlyAttrName());
369 bool hasReadNoneAttr =
370 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadnoneAttrName());
371 return !hasWriteOnlyAttr && !hasReadNoneAttr && funcMayRead;
374static bool mayWriteArg(FunctionOpInterface callee,
unsigned argNo,
375 std::optional<LLVM::ModRefInfo> argMemMRI) {
380 unsigned numArguments = callee.getNumArguments();
381 if (argNo >= numArguments)
386 bool hasReadOnlyAttr =
387 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadonlyAttrName());
388 bool hasReadNoneAttr =
389 !!callee.getArgAttr(argNo, LLVM::LLVMDialect::getReadnoneAttrName());
390 return !hasReadOnlyAttr && !hasReadNoneAttr && funcMayWrite;
395static std::optional<LLVM::ModRefInfo>
398 StringRef name = cast<SymbolOpInterface>(func.getOperation()).getName();
399 auto hardcoded = llvm::StringSwitch<std::optional<LLVM::ModRefInfo>>(name)
401 .Case(
"printf", LLVM::ModRefInfo::Ref)
402 .Case(
"puts", LLVM::ModRefInfo::Ref)
403 .Case(
"memset_pattern16", LLVM::ModRefInfo::ModRef)
404 .Case(
"free", LLVM::ModRefInfo::NoModRef)
406 .Case(
"_ZdlPv", LLVM::ModRefInfo::NoModRef)
407 .Default(std::nullopt);
411 if (
auto memoryAttr =
413 return memoryAttr.getArgMem();
420static std::optional<LLVM::ModRefInfo>
423 StringRef name = cast<SymbolOpInterface>(func.getOperation()).getName();
425 llvm::StringSwitch<std::optional<LLVM::ModRefInfo>>(name)
428 .Case(
"printf", LLVM::ModRefInfo::NoModRef)
429 .Case(
"puts", LLVM::ModRefInfo::NoModRef)
430 .Case(
"gettimeofday", LLVM::ModRefInfo::Ref)
432 .Case(
"_ZdlPv", LLVM::ModRefInfo::NoModRef)
433 .Case(
"free", LLVM::ModRefInfo::NoModRef)
434 .Case(
"memset_pattern16", LLVM::ModRefInfo::NoModRef)
435 .Default(std::nullopt);
439 if (
auto memoryAttr =
441 return memoryAttr.getOther();
446 CallOpInterface call,
447 const DenseMap<DistinctAttr, enzyme::AliasClassSet> &summary,
449 StringRef calleeName = cast<SymbolRefAttr>(call.getCallableForCallee())
452 auto lookup = [&](
unsigned argNumber,
453 unsigned depth) -> std::optional<AliasClassSet> {
454 for (
const auto &[attr, aliasClassSet] : summary) {
455 if (
auto pseudoClass = dyn_cast_if_present<PseudoAliasClassAttr>(
456 attr.getReferencedAttr())) {
457 if (pseudoClass.getFunction().getValue() == calleeName &&
458 pseudoClass.getArgNumber() == argNumber &&
459 pseudoClass.getDepth() == depth) {
460 return aliasClassSet;
467 ChangeResult changed = ChangeResult::NoChange;
469 for (
auto &&[i, argOperand] : llvm::enumerate(call.getArgOperands())) {
470 auto *arg = getOrCreateFor<AliasClassLattice>(getProgramPointAfter(call),
473 std::optional<AliasClassSet> calleePointsTo = lookup(i, 0);
479 for (DistinctAttr ac : calleePointsTo->getElements()) {
480 if (!isa<PseudoAliasClassAttr>(ac.getReferencedAttr())) {
492 propagateIfChanged(after, changed);
498static std::optional<LLVM::ModRefInfo>
500 if (
auto memoryAttr =
502 return memoryAttr.getInaccessibleMem();
507 CallOpInterface call, CallControlFlowAction action,
511 if (action == CallControlFlowAction::ExternalCallee) {
515 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
520 if (symbol.getLeafReference().getValue() ==
"posix_memalign") {
523 OperandRange arguments = call.getArgOperands();
524 auto *memPtr = getOrCreateFor<AliasClassLattice>(
525 getProgramPointAfter(call), arguments[0]);
531 originalClasses.getOriginalClass(arguments[0],
"memalign"));
532 return propagateIfChanged(
534 single.getAliasClassesObject()));
547 if (
auto callee = SymbolTable::lookupNearestSymbolFrom<FunctionOpInterface>(
548 call, symbol.getLeafReference())) {
549 if (
auto summaryAttr = callee->getAttrOfType<ArrayAttr>(
550 EnzymeDialect::getPointerSummaryAttrName())) {
551 DenseMap<DistinctAttr, AliasClassSet> summary;
553 return processCallToSummarizedFunc(call, summary, after);
557 std::optional<LLVM::ModRefInfo> otherModRef =
560 SmallVector<int> pointerLikeOperands;
561 for (
auto &&[i, operand] : llvm::enumerate(call.getArgOperands())) {
563 pointerLikeOperands.push_back(i);
572 unsigned numArguments = callee.getNumArguments();
573 if (funcMayReadOther) {
578 for (
int pointerAsData : pointerLikeOperands) {
580 if ((pointerAsData < numArguments &&
581 !!callee.getArgAttr(pointerAsData,
582 LLVM::LLVMDialect::getNoCaptureAttrName())))
585 const auto *srcClasses = getOrCreateFor<AliasClassLattice>(
586 getProgramPointAfter(call), call.getArgOperands()[pointerAsData]);
587 (void)functionMayCapture.
join(srcClasses->getAliasClassesObject());
595 ChangeResult changed = ChangeResult::NoChange;
596 for (
int pointerOperand : pointerLikeOperands) {
597 auto *destClasses = getOrCreateFor<AliasClassLattice>(
598 getProgramPointAfter(call), call.getArgOperands()[pointerOperand]);
601 if (!
mayWriteArg(callee, pointerOperand, argModRef)) {
602 (void)nonWritableOperandClasses.
join(
603 destClasses->getAliasClassesObject());
606 (void)writableClasses.
join(destClasses->getAliasClassesObject());
611 if (destClasses->isUnknown()) {
617 if (destClasses->isUndefined())
623 changed |= destClasses->getAliasClassesObject().foreachElement(
625 return after->
insert(dest, functionMayCapture);
657 for (OpResult result : call->getResults()) {
675 const auto *destClasses = getOrCreateFor<AliasClassLattice>(
676 getProgramPointAfter(call), result);
679 if (destClasses->isUnknown() || nonWritableOperandClasses.
isUnknown()) {
680 (void)resultWithoutNonWritableOperands.
markUnknown();
681 }
else if (!destClasses->isUndefined() &&
683 DenseSet<DistinctAttr> nonOperandClasses =
684 llvm::set_difference(destClasses->getAliasClasses(),
686 (void)resultWithoutNonWritableOperands.
insert(nonOperandClasses);
688 (void)resultWithoutNonWritableOperands.
join(
689 destClasses->getAliasClassesObject());
693 if (funcMayReadOther) {
695 resultWithoutNonWritableOperands));
699 for (
int operandNo : pointerLikeOperands) {
700 const auto *srcClasses = getOrCreateFor<AliasClassLattice>(
701 getProgramPointAfter(call), call.getArgOperands()[operandNo]);
702 if (
mayReadArg(callee, operandNo, argModRef)) {
703 changed |= after->
addSetsFrom(resultWithoutNonWritableOperands,
704 srcClasses->getAliasClassesObject());
708 (operandNo < numArguments &&
709 !!callee.getArgAttr(operandNo,
710 LLVM::LLVMDialect::getNoCaptureAttrName()));
713 changed |= after->
insert(resultWithoutNonWritableOperands,
714 srcClasses->getAliasClassesObject());
717 return propagateIfChanged(after, changed);
733 if (elements.isUnknown()) {
735 }
else if (elements.isUndefined()) {
736 os <<
"Undefined AC";
738 os <<
"size: " << elements.getElements().size() <<
":\n";
739 for (
auto aliasClass : elements.getElements()) {
740 os <<
" " << aliasClass <<
"\n";
749 assert(!isUndefined() && !rhs->isUndefined() &&
"incomplete alias analysis");
751 if (getAnchor() == rhs->getAnchor())
752 return AliasResult::MustAlias;
754 if (elements.isUnknown() || rhs->elements.isUnknown())
755 return AliasResult::MayAlias;
758 llvm::count_if(elements.getElements(), [rhs](DistinctAttr aliasClass) {
759 return rhs->elements.getElements().contains(aliasClass);
763 return AliasResult::NoAlias;
773 return AliasResult::MayAlias;
780 return elements.
join(otherAliasClass->elements);
788 if (
auto arg = dyn_cast<BlockArgument>(lattice->getAnchor())) {
790 dyn_cast<FunctionOpInterface>(arg.getOwner()->getParentOp())) {
795 funcOp.getArgAttr(arg.getArgNumber(),
796 LLVM::LLVMDialect::getNoAliasAttrName())) {
803 Attribute debugLabel = funcOp.getArgAttrOfType<StringAttr>(
804 arg.getArgNumber(),
"enzyme.tag");
807 PseudoAliasClassAttr::get(FlatSymbolRefAttr::get(funcOp),
808 arg.getArgNumber(), 0);
810 DistinctAttr argClass = originalClasses.getOriginalClass(
811 lattice->getAnchor(), debugLabel);
812 return propagateIfChanged(lattice,
814 lattice->getAnchor(), argClass)));
816 return propagateIfChanged(lattice,
818 lattice->getAnchor(), entryClass)));
836 if (
auto call = dyn_cast<CallOpInterface>(op)) {
837 if (
auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee())) {
838 if (symbol.getLeafReference().getValue() ==
"malloc") {
843 return isa<memref::LoadOp, memref::StoreOp, LLVM::LoadOp, LLVM::StoreOp>(op);
846void enzyme::AliasAnalysis::transfer(
847 Operation *op, ArrayRef<MemoryEffects::EffectInstance> effects,
848 ArrayRef<const AliasClassLattice *> operands,
849 ArrayRef<AliasClassLattice *> results) {
850 bool globalRead =
false;
851 for (
const auto &effect : effects) {
853 Value value = effect.getValue();
855 globalRead |= isa<MemoryEffects::Read>(effect.getEffect());
859 if (isa<MemoryEffects::Allocate>(effect.getEffect())) {
861 for (AliasClassLattice *result : results) {
862 if (result->getAnchor() == value) {
863 std::string debugLabel;
864 llvm::raw_string_ostream sstream(debugLabel);
868 if (op->hasAttr(
"tag")) {
869 if (
auto stringTag = dyn_cast<StringAttr>(op->getAttr(
"tag"))) {
870 sstream << stringTag.getValue();
872 op->getAttr(
"tag").print(sstream);
876 result->getAnchor(), originalClasses.getOriginalClass(
877 result->getAnchor(), debugLabel));
878 propagateIfChanged(result, result->join(fresh));
881 }
else if (isa<MemoryEffects::Read>(effect.getEffect())) {
882 auto *pointsToSets = getOrCreateFor<PointsToSets>(
883 getProgramPointAfter(op), getProgramPointAfter(op));
884 AliasClassLattice *latticeElement = getLatticeElement(value);
885 if (latticeElement->isUnknown()) {
886 for (AliasClassLattice *result : results) {
887 propagateIfChanged(result, result->markUnknown());
889 }
else if (latticeElement->isUndefined()) {
892 for (
auto srcClass : latticeElement->getAliasClasses()) {
893 const auto &srcPointsTo = pointsToSets->getPointsTo(srcClass);
894 for (AliasClassLattice *result : results) {
900 if (srcPointsTo.isUnknown()) {
901 propagateIfChanged(result, result->markUnknown());
902 }
else if (srcPointsTo.isUndefined()) {
904 createImplicitArgDereference(op, latticeElement, srcClass,
907 propagateIfChanged(result,
908 result->insert(srcPointsTo.getElements()));
920 for (
auto *resultLattice : results)
921 propagateIfChanged(resultLattice, resultLattice->markUnknown());
930 for (AliasClassLattice *resultLattice : results) {
934 constexpr bool pruneNonPointers =
false;
935 if (pruneNonPointers &&
939 ChangeResult r = ChangeResult::NoChange;
940 for (
const AliasClassLattice *operandLattice : operands)
941 r |= resultLattice->join(*operandLattice);
942 propagateIfChanged(resultLattice, r);
946void enzyme::AliasAnalysis::createImplicitArgDereference(
947 Operation *op, AliasClassLattice *source, DistinctAttr srcClass,
948 AliasClassLattice *result) {
949 assert(relative &&
"only valid to create implicit argument dereferences when "
950 "operating in relative mode");
952 Value readResult = result->getAnchor();
953 auto parent = op->getParentOfType<FunctionOpInterface>();
954 assert(parent &&
"failed to find function parent");
955 auto *entryPointsToSets = getOrCreateFor<PointsToSets>(
956 getProgramPointAfter(op),
957 getProgramPointAfter(&parent.getCallableRegion()->front()));
958 if (!entryPointsToSets->getPointsTo(srcClass).isUndefined()) {
963 if (
auto pseudoClass = dyn_cast_if_present<PseudoAliasClassAttr>(
964 srcClass.getReferencedAttr())) {
965 auto pseudoDeref = PseudoAliasClassAttr::get(pseudoClass.getFunction(),
966 pseudoClass.getArgNumber(),
967 pseudoClass.getDepth() + 1);
968 DistinctAttr derefClass =
969 originalClasses.getOriginalClass(readResult, pseudoDeref);
971 readResult, derefClass)));
973 auto *pointsToState = getOrCreate<PointsToSets>(
974 getProgramPointBefore(&parent.getCallableRegion()->front()));
975 propagateIfChanged(pointsToState,
976 pointsToState->insert(source->getAliasClassesObject(),
982 CallOpInterface call,
983 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
984 auto rng = call.getArgOperands();
985 for (
auto it = rng.begin(); it != rng.end(); it++) {
987 auto argOp = it.getBase();
988 assert(argOp->get() == argument);
992 effects.emplace_back(MemoryEffects::Read::get(), argOp);
993 effects.emplace_back(MemoryEffects::Write::get(), argOp);
1000 CallOpInterface call,
1001 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
1002 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
1007 StringRef callableName = symbol.getLeafReference().getValue();
1008 if (callableName ==
"malloc" || callableName ==
"calloc" ||
1009 callableName ==
"_Znwm") {
1010 assert(call->getNumResults() == 1);
1011 effects.push_back(MemoryEffects::EffectInstance(
1012 MemoryEffects::Allocate::get(), call->getResult(0)));
1020 Operation *op, ArrayRef<const AliasClassLattice *> operands,
1021 ArrayRef<AliasClassLattice *> results) {
1025 auto memory = dyn_cast<MemoryEffectOpInterface>(op);
1027 for (OpResult result : op->getResults()) {
1031 (void)results[result.getResultNumber()]->markUnknown();
1036 SmallVector<MemoryEffects::EffectInstance> effects;
1037 memory.getEffects(effects);
1038 transfer(op, effects, operands, results);
1039 if (
auto view = dyn_cast<OffsetSizeAndStrideOpInterface>(op)) {
1049 SmallVectorImpl<enzyme::AliasClassSet> &out) {
1050 for (Attribute element : summary) {
1051 if (
auto strAttr = dyn_cast<StringAttr>(element)) {
1060 for (DistinctAttr aliasClass :
1061 cast<ArrayAttr>(element).getAsRange<DistinctAttr>()) {
1062 (void)aliasClasses.
insert({aliasClass});
1064 out.push_back(aliasClasses);
1070 CallOpInterface call, ArrayRef<const AliasClassLattice *> operands,
1071 ArrayRef<AliasClassLattice *> results) {
1073 SmallVector<MemoryEffects::EffectInstance> effects;
1075 return transfer(call, effects, operands, results);
1077 auto markResultsUnknown = [&] {
1079 propagateIfChanged(resultLattice, resultLattice->markUnknown());
1089 auto symbol = dyn_cast<SymbolRefAttr>(call.getCallableForCallee());
1091 return markResultsUnknown();
1092 auto callee = SymbolTable::lookupNearestSymbolFrom<FunctionOpInterface>(
1093 call, symbol.getLeafReference());
1095 return markResultsUnknown();
1097 if (
auto aliasSummaryAttr = callee->getAttrOfType<ArrayAttr>(
1098 EnzymeDialect::getAliasSummaryAttrName())) {
1100 SmallVector<AliasClassSet> aliasSummary;
1103 assert(results.size() == aliasSummary.size());
1104 for (
auto &&[i, resultSummary] : llvm::enumerate(aliasSummary)) {
1108 ChangeResult changed = ChangeResult::NoChange;
1109 if (resultSummary.isUndefined())
1111 if (resultSummary.isUnknown())
1114 changed |= resultSummary.foreachElement(
1116 assert(state == AliasClassSet::State::Defined);
1117 if (
auto pseudoClass = dyn_cast<PseudoAliasClassAttr>(
1118 aliasClass.getReferencedAttr())) {
1120 pseudoClass.getDepth() == 0 &&
1121 "sparse alias summaries for depth > 0 not yet implemented");
1122 return result->
join(*operands[pseudoClass.getArgNumber()]);
1124 return result->
insert({aliasClass});
1128 propagateIfChanged(result, changed);
1136 std::optional<LLVM::ModRefInfo> inaccessibleModRef =
1139 for (
auto [operandNo, operand] : llvm::enumerate(call.getArgOperands())) {
1146 if (!
mayReadArg(callee, operandNo, argModRef))
1151 const auto *pointsToLattice = getOrCreateFor<PointsToSets>(
1152 getProgramPointAfter(call), getProgramPointAfter(call));
1157 if (srcClass ==
nullptr)
1158 return ChangeResult::NoChange;
1159 (void)operandAliasClasses.join(
1160 pointsToLattice->getPointsTo(srcClass));
1161 return ChangeResult::NoChange;
1165 auto debugLabel = call->getAttrOfType<StringAttr>(
"tag");
1166 DistinctAttr commonResultAttr =
nullptr;
1170 SmallVector<Value> aliasGroupResults;
1171 for (OpResult result : call->getResults()) {
1172 if (!callee.getResultAttr(result.getResultNumber(),
1173 LLVM::LLVMDialect::getNoAliasAttrName()))
1174 aliasGroupResults.push_back(result);
1177 for (OpResult result : call->getResults()) {
1179 if (!llvm::is_contained(aliasGroupResults, result)) {
1180 Attribute individualDebugLabel =
1182 ? StringAttr::get(debugLabel.getContext(),
1183 debugLabel.getValue().str() +
1184 std::to_string(result.getResultNumber()))
1187 resultLattice->getAnchor(),
1188 originalClasses.getOriginalClass(resultLattice->getAnchor(),
1189 individualDebugLabel));
1190 propagateIfChanged(resultLattice, resultLattice->
join(individualAlloc));
1194 if (!commonResultAttr) {
1195 std::string label = !debugLabel
1196 ?
"func-result-common"
1197 : debugLabel.getValue().str() +
"-common";
1199 originalClasses.getSameOriginalClass(aliasGroupResults, label);
1203 resultLattice->getAnchor(), std::move(commonClass)));
1211 resultLattice->getAnchor(),
AliasClassSet(operandAliasClasses)));
1212 propagateIfChanged(resultLattice, changed);
1214 propagateIfChanged(resultLattice, resultLattice->
markUnknown());
static void deserializePointsTo(ArrayAttr summaryAttr, DenseMap< DistinctAttr, enzyme::ValueOriginSet > &summaryMap)
std::optional< Value > getStored(Operation *op)
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)
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
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()
LLVM_DUMP_METHOD void print(llvm::raw_ostream &os) const
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.
constexpr llvm::StringLiteral undefinedSetString