58#include "llvm/ADT/DenseMap.h"
59#include "llvm/ADT/DenseSet.h"
60#include "llvm/ADT/STLExtras.h"
61#include "llvm/ADT/SetVector.h"
62#include "llvm/ADT/SmallPtrSet.h"
63#include "llvm/ADT/SmallVector.h"
64#include "llvm/Support/DebugLog.h"
70#define DEBUG_TYPE "one-shot-analysis"
89 "__opresult_alias_set_attr__";
99 cast<ArrayAttr>(attr).getAsValueRange<StringAttr>()));
108 if (isa<TensorLikeType>(opOperand.
get().
getType()))
113 OpBuilder(op).getStrArrayAttr(inPlaceVector));
125 if (region.getBlocks().size() > 1) {
138 op->
walk([&](BufferizableOpInterface bufferizableOp) ->
WalkResult {
139 if (!
options.isOpAllowed(bufferizableOp))
141 for (
Region ®ion : bufferizableOp->getRegions()) {
142 if (bufferizableOp.isParallelRegion(region.getRegionNumber()))
153 mayHaveUnstructuredCF =
options.mayHaveUnstructuredControlFlow.value_or(
155 if (!
options.mayHaveParallelRegions.has_value()) {
156 mayHaveParallelRegionsFlag = detectParallelRegions(op, options);
158 mayHaveParallelRegionsFlag = *options.mayHaveParallelRegions;
163 for (Value v : op->getResults())
164 if (isa<TensorLikeType>(v.getType()))
165 createAliasInfoEntry(v);
166 for (Region &r : op->getRegions())
167 for (Block &b : r.getBlocks())
168 for (auto bbArg : b.getArguments())
169 if (isa<TensorLikeType>(bbArg.getType()))
170 createAliasInfoEntry(bbArg);
174 op->
walk([&](BufferizableOpInterface bufferizableOp) {
175 if (!options.isOpAllowed(bufferizableOp))
176 return WalkResult::skip();
178 for (OpOperand &opOperand : bufferizableOp->getOpOperands())
179 if (isa<TensorLikeType>(opOperand.get().getType()))
180 if (bufferizableOp.mustBufferizeInPlace(opOperand, *this))
181 bufferizeInPlace(opOperand);
182 return WalkResult::advance();
188 auto leaderIt = equivalentInfo.findLeader(v);
189 for (
auto mit = leaderIt, meit = equivalentInfo.member_end(); mit != meit;
197 auto leaderIt = aliasInfo.findLeader(v);
198 for (
auto mit = leaderIt, meit = aliasInfo.member_end(); mit != meit; ++mit) {
205 return equivalentInfo.isEquivalent(v1, v2);
210 return aliasInfo.isEquivalent(v1, v2);
214 if (inplaceBufferized.contains(&operand))
216 inplaceBufferized.insert(&operand);
217 for (AliasingValue alias : getAliasingValues(operand))
218 aliasInfo.unionSets(alias.value, operand.
get());
219 ++statNumTensorInPlace;
223 assert(!inplaceBufferized.contains(&operand) &&
224 "OpOperand was already decided to bufferize inplace");
225 ++statNumTensorOutOfPlace;
230 equivalentInfo.insert(v);
236 auto bufferizableOp =
getOptions().dynCastBufferizableOp(op);
242 if (!isa<TensorLikeType>(opResult.getType()))
247 if (opResult.getUses().empty())
251 OpOperand *opOperand = &(*opResult.getUses().begin());
253 for (
OpOperand &use : opResult.getUses())
254 undefinedTensorUses.insert(&use);
262 return undefinedTensorUses.contains(opOperand);
266 return inplaceBufferized.contains(&opOperand);
270 bool isWritten =
false;
273 if (
isInPlace(use) && bufferizesToMemoryWrite(use))
282 if (
auto bufferizableOp =
284 return bufferizableOp.isWritable(value, *
this);
291 aliasInfo.unionSets(v1, v2);
295 equivalentInfo.unionSets(v1, v2);
308 if (!state.bufferizesToMemoryWrite(opOperand))
323 BarrierKey sortedBarriers;
324 if (barriers && !barriers->empty()) {
325 except.insert(barriers->begin(), barriers->end());
326 sortedBarriers.append(barriers->begin(), barriers->end());
327 llvm::sort(sortedBarriers);
330 ReachableByBarriers &inner = cached[from];
331 if (
auto it = inner.find(sortedBarriers); it != inner.end())
332 return it->second.contains(to);
336 ReachableSet reachable;
338 while (!worklist.empty()) {
339 Block *next = worklist.pop_back_val();
340 if (!except.insert(next).second)
342 reachable.insert(next);
343 worklist.append(next->succ_begin(), next->succ_end());
345 bool result = reachable.contains(to);
346 inner.try_emplace(std::move(sortedBarriers), std::move(reachable));
373 if (sortedDefBlocks.size() == 1) {
375 std::make_tuple(readBlock, writeBlock, sortedDefBlocks.front());
376 if (
auto it = singleDef.find(key); it != singleDef.end())
379 singleDef.try_emplace(key,
result);
383 DefKey key(sortedDefBlocks.begin(), sortedDefBlocks.end());
384 auto &byDefs = manyDefs[std::make_pair(readBlock, writeBlock)];
385 if (
auto it = byDefs.find(key); it != byDefs.end())
388 byDefs.try_emplace(std::move(key),
result);
443 Block *bBlock =
b->getBlock();
532 for (
Value def : definitions) {
535 Region *rDef = state.getEnclosingRepetitiveRegion(def,
options);
545 Region *nextRegion = getNextEnclosingRepetitiveRegion(rRead,
options);
546 if (nextRegion == rDef)
548 assert(nextRegion &&
"expected to find another repetitive region");
582 assert(!definitions.empty() &&
"expected at least one definition");
583 Region *outermostDefRegion =
nullptr;
584 for (
Value def : definitions) {
586 if (!outermostDefRegion || defRegion->
isAncestor(outermostDefRegion))
587 outermostDefRegion = defRegion;
589 assert(outermostDefRegion &&
"expected a definition region");
601 Block *readBlock = readOp->getBlock();
603 for (
Value def : definitions) {
609 if (region == outermostDefRegion)
622 assert(!definitions.empty() &&
"expected at least one definition");
627 defBlocks.reserve(definitions.size());
628 for (
Value def : definitions)
629 defBlocks.push_back(def.getParentBlock());
630 llvm::sort(defBlocks);
631 defBlocks.erase(llvm::unique(defBlocks), defBlocks.end());
634 readBlock, writeBlock, defBlocks, [&] {
650 static uint64_t counter = 0;
655 std::string
id =
"C_" + std::to_string(counter++);
657 std::string conflictingWriteAttr =
664 std::string readAttr =
668 if (
auto opResult = dyn_cast<OpResult>(definition)) {
669 std::string defAttr =
670 id +
"[DEF: result " + std::to_string(opResult.getResultNumber()) +
"]";
671 opResult.getDefiningOp()->setDiscardableAttr(defAttr,
b.getUnitAttr());
673 auto bbArg = cast<BlockArgument>(definition);
674 std::string defAttr =
675 id +
"[DEF: bbArg " + std::to_string(bbArg.getArgNumber()) +
"]";
676 bbArg.getOwner()->getParentOp()->setDiscardableAttr(defAttr,
692 TraversalConfig config;
693 config.followEquivalentOnly =
true;
694 config.alwaysIncludeLeaves =
false;
695 config.followSameTypeOrCastsOnly =
true;
697 .findValueInReverseUseDefChain(
698 start, [&](
Value v) {
return v == other; }, config)
706 SubsetInsertionOpInterface subsetOp) {
707 auto matchingSubset = [&](
Value val) {
708 if (
auto opResult = dyn_cast<OpResult>(val))
709 if (subsetOp.isEquivalentSubset(opResult, [&](
Value v1,
Value v2) {
710 return state.areEquivalentBufferizedValues(v1, v2);
718 state.findValueInReverseUseDefChain(opOperand, matchingSubset);
719 return llvm::all_of(backwardSlice, matchingSubset);
735 if (
auto subsetOp = dyn_cast<SubsetInsertionOpInterface>(readingOp)) {
743 if (uRead == &subsetOp.getDestinationOperand() &&
759 if (uRead == &subsetOp.getSourceOperand() &&
760 uConflictingWrite == &subsetOp.getDestinationOperand() &&
775 dyn_cast<SubsetInsertionOpInterface>(conflictingWritingOp)) {
776 if (uConflictingWrite == &subsetOp.getDestinationOperand()) {
777 auto writtenSubset = cast<SubsetOpInterface>(conflictingWritingOp);
778 auto isDisjointSubset = [&](SubsetOpInterface readSubset) {
779 return readSubset.operatesOnDisjointSubset(
781 return state.areEquivalentBufferizedValues(v1, v2);
784 auto isDisjointExtraction = [&](
Value value) {
785 auto extraction = value.getDefiningOp<SubsetExtractionOpInterface>();
786 return extraction && isDisjointSubset(cast<SubsetOpInterface>(
787 extraction.getOperation()));
798 if (
auto extraction = dyn_cast<SubsetExtractionOpInterface>(readingOp)) {
799 if (uRead == &extraction.getSourceOperand() &&
800 isDisjointSubset(cast<SubsetOpInterface>(readingOp)))
817 state.findValueInReverseUseDefChain(uRead, isDisjointExtraction);
818 if (!readOrigins.empty() &&
819 llvm::all_of(readOrigins, isDisjointExtraction))
839 if (uConflictingWrite == &subsetOp.getDestinationOperand() &&
840 state.areEquivalentBufferizedValues(
841 uRead->
get(), subsetOp.getSourceOperand().get()) &&
867 for (
OpOperand *uConflictingWrite : usesWrite) {
875 state.findValueInReverseUseDefChain(uConflictingWrite, [&](
Value v) {
876 return state.bufferizesToMemoryWrite(v);
878 assert(!definitionsOrLeaves.empty() &&
879 "expected at least one definition or leaf");
883 Region *writeParallelRegion = getParallelRegion(
884 uConflictingWrite->getOwner()->getParentRegion(),
options);
885 for (
Value def : definitionsOrLeaves) {
886 if (getParallelRegion(def.getParentRegion(),
options) !=
887 writeParallelRegion) {
888 LDBG() <<
"\n- bufferizes out-of-place due to parallel region:\n"
889 <<
" unConflictingWrite = operand "
890 << uConflictingWrite->getOperandNumber() <<
" of "
900 Operation *readingOp = uRead->getOwner();
901 LDBG() <<
"\n- check conflict:\n"
902 <<
" uRead = operand " << uRead->getOperandNumber() <<
" of "
916 if (definitions.empty()) {
918 LDBG() <<
" no conflict: read value has no definitions";
939 for (
Value def : definitions)
940 if (isa<BlockArgument>(def))
941 bbArgDefBlocks.insert(def.getParentBlock());
945 for (
OpOperand *uConflictingWrite : usesWrite) {
946 LDBG() <<
" unConflictingWrite = operand "
947 << uConflictingWrite->getOperandNumber() <<
" of "
955 LDBG() <<
"\n- useDominance = " << useDominance;
959 Operation *conflictingWritingOp = uConflictingWrite->getOwner();
972 LDBG() <<
" no conflict: read cannot happen after write";
983 if (uConflictingWrite == uRead) {
984 LDBG() <<
" no conflict: read and write are same use";
993 if (state.insideMutuallyExclusiveRegions(readingOp,
994 conflictingWritingOp)) {
995 LDBG() <<
" no conflict: read and write are in "
996 "mutually exclusive regions";
1003 if (conflictingWritingOp == readingOp) {
1004 if (
auto bufferizableOp =
options.dynCastBufferizableOp(readingOp)) {
1005 if (bufferizableOp.bufferizesToElementwiseAccess(
1006 state, {uRead, uConflictingWrite})) {
1008 state, uRead, uConflictingWrite->get()) ||
1010 state, uConflictingWrite, uRead->get())) {
1011 LDBG() <<
" no conflict: op bufferizes to element-wise access";
1021 LDBG() <<
" no conflict: non-conflicting subsets";
1026 if (
auto bufferizableOp =
options.dynCastBufferizableOp(readingOp)) {
1027 if (bufferizableOp.isNotConflicting(uRead, uConflictingWrite, state)) {
1028 LDBG() <<
" no conflict: op interace of reading op says 'no'";
1033 if (conflictingWritingOp != readingOp) {
1034 if (
auto bufferizableOp =
1035 options.dynCastBufferizableOp(conflictingWritingOp)) {
1036 if (bufferizableOp.isNotConflicting(uRead, uConflictingWrite,
1038 LDBG() <<
" no conflict: op interace of writing op says 'no'";
1045 for (
Value definition : definitions) {
1046 LDBG() <<
" * definition = " << definition;
1050 if (
Operation *defOp = definition.getDefiningOp()) {
1052 LDBG() <<
" no conflict: write cannot happen after definition";
1056 if (defOp->isProperAncestor(conflictingWritingOp)) {
1057 LDBG() <<
" no conflict: write is contained in definition";
1064 Block *defBlock = cast<BlockArgument>(definition).getOwner();
1066 *conflictingWritingOp);
1068 (writeOp->
getBlock() != defBlock &&
1070 LDBG() <<
" no conflict: definition is bbArg and write cannot "
1078 AliasingValueList aliases = state.getAliasingValues(*uConflictingWrite);
1079 if (aliases.getNumAliases() == 1 &&
1080 aliases.getAliases()[0].value == definition) {
1081 LDBG() <<
" no conflict: definition and write are same";
1089 LDBG() <<
" => RaW CONFLICT FOUND";
1102 for (
auto &use : alias.
getUses())
1113 for (
auto &use : alias.
getUses()) {
1115 if (state.bufferizesToMemoryRead(use)) {
1135 if (!state.bufferizesToMemoryWrite(use)) {
1136 AliasingValueList aliases = state.getAliasingValues(use);
1137 if (llvm::any_of(aliases, [&](AliasingValue a) {
1138 return state.isValueRead(a.value);
1182 for (AliasingValue alias : state.getAliasingValues(operand)) {
1186 if (!checkConsistencyOnly && state.bufferizesToMemoryWrite(operand))
1187 usesWrite.insert(&operand);
1196 std::string
id =
"W_" + std::to_string(counter++);
1197 if (
auto opResult = dyn_cast<OpResult>(value)) {
1198 std::string attr =
id +
"[NOT-WRITABLE: result " +
1199 std::to_string(opResult.getResultNumber()) +
"]";
1200 opResult.getDefiningOp()->setDiscardableAttr(attr,
b.getUnitAttr());
1202 auto bbArg = cast<BlockArgument>(value);
1203 std::string attr =
id +
"[NOT-WRITABLE: bbArg " +
1204 std::to_string(bbArg.getArgNumber()) +
"]";
1205 bbArg.getOwner()->getParentOp()->setDiscardableAttr(attr,
b.getUnitAttr());
1214 bool checkConsistencyOnly =
false) {
1216 !checkConsistencyOnly && state.bufferizesToMemoryWrite(operand);
1222 for (AliasingValue alias : state.getAliasingValues(operand))
1224 foundWrite = !usesWrite.empty();
1231 bool foundReadOnly =
false;
1232 auto checkReadOnly = [&](
Value v) {
1234 foundReadOnly =
true;
1240 for (AliasingValue alias : state.getAliasingValues(operand))
1242 if (foundReadOnly) {
1243 LDBG() <<
"=> NOT WRITABLE";
1255const llvm::SetVector<Value> &
1258 if (!cachedDefinitions.count(value))
1259 cachedDefinitions[value] = findDefinitions(opOperand);
1260 return cachedDefinitions[value];
1265 if (!cfgReachabilityCache)
1266 cfgReachabilityCache = std::make_unique<CFGReachabilityCache>();
1267 return cfgReachabilityCache->isReachable(from, to, barriers);
1273 if (!opDominanceBlockCache)
1274 opDominanceBlockCache = std::make_unique<OpDominanceBlockCache>();
1275 return opDominanceBlockCache->getOrCompute(readBlock, writeBlock, defBlocks,
1281 auto key = std::make_pair(uRead, uConflictingWrite);
1282 auto [it,
inserted] = nonConflictingSubsetCache.try_emplace(key,
false);
1289 AnalysisState::resetCache();
1290 cachedDefinitions.clear();
1291 nonConflictingSubsetCache.clear();
1292 if (cfgReachabilityCache)
1293 cfgReachabilityCache->clear();
1294 if (opDominanceBlockCache)
1295 opDominanceBlockCache->clear();
1302 LDBG() <<
"//===-------------------------------------------===//\n"
1306 bool foundInterference =
1310 if (foundInterference)
1315 LDBG() <<
"//===-------------------------------------------===//";
1323 if (isa<TensorLikeType>(opOperand.get().getType()))
1333 if (
auto bufferizableOp = state.
getOptions().dynCastBufferizableOp(op)) {
1334 for (
OpResult opResult : op->getOpResults()) {
1335 if (!isa<TensorLikeType>(opResult.getType()))
1337 AliasingOpOperandList aliases = state.getAliasingOpOperands(opResult);
1338 if (aliases.getNumAliases() == 0)
1342 Value firstOperand = aliases.begin()->opOperand->get();
1343 bool allEquivalent =
true;
1344 for (AliasingOpOperand alias : aliases) {
1345 bool isEquiv = alias.relation == BufferRelation::Equivalent;
1346 bool isInPlace = state.
isInPlace(*alias.opOperand);
1347 Value operand = alias.opOperand->get();
1348 if (isEquiv && isInPlace && alias.isDefinite) {
1352 allEquivalent =
false;
1355 if (!isEquiv || !isInPlace)
1356 allEquivalent =
false;
1358 allEquivalent =
false;
1371 if (allEquivalent && !bufferizableOp.bufferizesToAllocation(opResult))
1394static SmallVector<Operation *>
1401 if (!traversedOps.insert(term))
1406 for (
Value v : term->getOperands()) {
1407 if (!isa<TensorLikeType>(v.
getType()))
1409 auto opResult = dyn_cast<OpResult>(v);
1412 worklist.push_back(opResult);
1414 while (!worklist.empty()) {
1415 OpResult opResult = worklist.pop_back_val();
1417 if (!traversedOps.insert(defOp))
1419 if (!term->getParentRegion()->findAncestorOpInRegion(*defOp))
1421 AliasingOpOperandList aliases = state.getAliasingOpOperands(opResult);
1422 for (
auto alias : aliases) {
1423 Value v = alias.opOperand->get();
1424 if (!isa<TensorLikeType>(v.
getType()))
1426 auto opResult = dyn_cast<OpResult>(v);
1429 worklist.push_back(opResult);
1437 if (!traversedOps.contains(op) && hasTensorSemantics(op))
1455 if (!hasTensorSemantics(op))
1457 orderedOps.push_back(op);
1459 switch (heuristic) {
1462 std::reverse(orderedOps.begin(), orderedOps.end());
1471 "expected that fuzzer seed it set");
1476 std::mt19937 g(
getOptions().analysisFuzzerSeed);
1477 llvm::shuffle(orderedOps.begin(), orderedOps.end(), g);
1481 llvm_unreachable(
"unsupported heuristic");
1502 WalkResult walkResult = op->
walk([&](BufferizableOpInterface op) {
1504 if (!
options.isOpAllowed(op.getOperation()))
1508 if (!op.supportsUnstructuredControlFlow()) {
1510 if (r.getBlocks().size() > 1) {
1511 op->
emitOpError(
"op or BufferizableOpInterface implementation does "
1512 "not support unstructured control flow, but at least "
1513 "one region has multiple blocks");
1524 walkResult = op->
walk([&](BufferizableOpInterface op) {
1526 if (!
options.isOpAllowed(op.getOperation()))
1532 if (
auto toTensorOp = dyn_cast<ToTensorOp>(op.getOperation())) {
1533 if (!toTensorOp.getRestrict() && !toTensorOp->getUses().empty()) {
1534 op->
emitOpError(
"to_tensor ops without `restrict` are not supported by "
1535 "One-Shot Analysis");
1541 if (isa<TensorLikeType>(opOperand.get().getType())) {
1543 opOperand, domInfo, state,
1550 op->
emitOpError(
"not bufferizable under the given constraints: "
1551 "cannot avoid RaW conflict");
1557 opOperand, state,
true)) {
1558 op->
emitOpError(
"not bufferizable under the given constraints: would "
1559 "write to read-only buffer");
1578 if (isa<TensorLikeType>(opOperand.get().getType()))
1588 auto buildAliasesArray = [&](
Value v) {
1592 llvm::raw_string_ostream stream(buffer);
1594 aliases.push_back(
b.getStringAttr(buffer));
1596 return b.getArrayAttr(aliases);
1603 if (llvm::isa<TensorLikeType>(opResult.getType())) {
1604 opResultAliasSets.push_back(buildAliasesArray(opResult));
1607 if (!opResultAliasSets.empty())
1609 b.getArrayAttr(opResultAliasSets));
1613 bool hasTensorBbArg =
false;
1616 for (
Block &block : r.getBlocks()) {
1619 if (llvm::isa<TensorLikeType>(bbArg.getType())) {
1620 bbArgAliasSets.push_back(buildAliasesArray(bbArg));
1621 hasTensorBbArg =
true;
1624 blockAliasSets.push_back(
b.getArrayAttr(bbArgAliasSets));
1626 regionAliasSets.push_back(
b.getArrayAttr(blockAliasSets));
1630 b.getArrayAttr(regionAliasSets));
1644 if (failed(state.
analyzeOp(op, domInfo)))
1652 bool failedAnalysis =
false;
1661 if (BufferizableOpInterface bufferizableOp =
1662 options.dynCastBufferizableOp(op))
1663 failedAnalysis |= failed(bufferizableOp.verifyAnalysis(state));
1672 return success(!failedAnalysis);
1681 "invalid combination of bufferization flags");
1683 if (
options.copyBeforeWrite) {
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static bool hasReadAfterWriteInterference(const DenseSet< OpOperand * > &usesRead, const DenseSet< OpOperand * > &usesWrite, const DominanceInfo &domInfo, OneShotAnalysisState &state)
Given sets of uses and writes, return true if there is a RaW conflict under the assumption that all g...
static void getAliasingReads(DenseSet< OpOperand * > &res, Value root, const OneShotAnalysisState &state)
static void equivalenceAnalysis(SmallVector< Operation * > &ops, OneShotAnalysisState &state)
Analyze equivalence of tied OpResult/OpOperand pairs of the given ops.
static void setInPlaceOpOperand(OpOperand &opOperand, bool inPlace)
Mark whether OpOperand will be bufferized inplace.
static bool computeCanUseOpDominanceDueToBlocks(OpOperand *uRead, OpOperand *uWrite, const SetVector< Value > &definitions, OneShotAnalysisState &state)
Return true if op dominance can be used to rule out a read-after-write conflicts based on the orderin...
constexpr StringLiteral kInPlaceOperandsAttrName
Attribute marker to specify op operands that bufferize in-place.
static bool detectUnstructuredControlFlow(Operation *op)
A region with more than one block is unstructured control flow.
static bool isaTensor(Type t)
static void annotateNonWritableTensor(Value value)
Annotate IR with details about the detected non-writability conflict.
static SmallVector< Operation * > bottomUpFromTerminatorsHeuristic(Operation *op, const OneShotAnalysisState &state)
"Bottom-up from terminators" heuristic.
static bool canUseOpDominanceDueToRegions(OpOperand *uRead, OpOperand *uWrite, const SetVector< Value > &definitions, AnalysisState &state)
Return true if op dominance can be used to rule out a read-after-write conflicts based on the orderin...
static LogicalResult bufferizableInPlaceAnalysisImpl(OpOperand &operand, OneShotAnalysisState &state, const DominanceInfo &domInfo)
Determine if operand can be bufferized in-place.
constexpr StringLiteral kOpResultAliasSetAttrName
static bool cannotHappenAfter(Operation *a, Operation *b, const DominanceInfo &domInfo, OneShotAnalysisState &state, SmallPtrSet< Block *, 16 > extraBarriers={})
Return true if a cannot happen after b.
static bool matchesInsertDestination(const AnalysisState &state, OpOperand *opOperand, SubsetInsertionOpInterface subsetOp)
Return "true" if the given operand's value is originating from a subset that is equivalent to the sub...
static bool detectParallelRegions(Operation *op, const BufferizationOptions &options)
Return "true" if any allowed op has a parallel region.
static bool wouldCreateWriteToNonWritableBuffer(OpOperand &operand, OneShotAnalysisState &state, bool checkConsistencyOnly=false)
Return true if bufferizing operand inplace would create a write to a non-writable buffer.
static void annotateOpsWithAliasSets(Operation *op, const OneShotAnalysisState &state)
static void annotateOpsWithBufferizationMarkers(Operation *op, const OneShotAnalysisState &state)
Annotate the IR with the result of the analysis. For testing/debugging only.
static bool canUseOpDominanceDueToBlocks(OpOperand *uRead, OpOperand *uWrite, const SetVector< Value > &definitions, OneShotAnalysisState &state)
static bool wouldCreateReadAfterWriteInterference(OpOperand &operand, const DominanceInfo &domInfo, OneShotAnalysisState &state, bool checkConsistencyOnly=false)
Return true if bufferizing operand inplace would create a conflict.
constexpr StringLiteral kBbArgAliasSetAttrName
static void getAliasingInplaceWrites(DenseSet< OpOperand * > &res, Value root, const OneShotAnalysisState &state)
static bool areNonConflictingSubsets(OpOperand *uRead, OpOperand *uConflictingWrite, const AnalysisState &state)
Return "true" if the given "read" and potentially conflicting "write" are not conflicting due to thei...
static bool canUseOpDominance(OpOperand *uRead, OpOperand *uWrite, const SetVector< Value > &definitions, OneShotAnalysisState &state)
static void annotateConflict(OpOperand *uRead, OpOperand *uConflictingWrite, Value definition)
Annotate IR with details about the detected RaW conflict.
static bool hasEquivalentValueInReverseUseDefChain(AnalysisState &state, OpOperand *start, Value other)
Return 'true' if a tensor that is equivalent to other can be found in the reverse use-def chain of st...
static bool isInplaceMemoryWrite(OpOperand &opOperand, const OneShotAnalysisState &state)
Return true if opOperand has been decided to bufferize in-place.
static llvm::ManagedStatic< PassManagerOptions > options
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
static Operation * getOwnerOfValue(Value value)
Cached CFG reachability to avoid repeated linear BFS traversals from Block->isReachable.
bool isReachable(Block *from, Block *to, const SmallPtrSetImpl< Block * > *barriers=nullptr)
canUseOpDominanceDueToBlocks walks enclosing regions and queries CFG reachability,...
bool getOrCompute(Block *readBlock, Block *writeBlock, ArrayRef< Block * > sortedDefBlocks, function_ref< bool()> compute)
Base class for generic analysis states.
AnalysisState(LatticeAnchor anchor)
Create the analysis state on the given lattice anchor.
This class provides management for the lifetime of the state used when printing the IR.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
succ_iterator succ_begin()
This class is a general helper class for creating context-global objects like types,...
A class for computing basic dominance information.
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
IRValueT get() const
Return the current value being used by this operand.
This class helps build Operations.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Set of flags used to control the behavior of the various IR print methods (e.g.
This is a value defined by a result of an operation.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Operation is the basic unit of execution within MLIR.
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
Block * getBlock()
Returns the operation block that contains this operation.
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
MutableArrayRef< OpOperand > getOpOperands()
unsigned getNumOperands()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_type_range getResultTypes()
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getOpResults()
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
MLIRContext * getContext()
Return the context this operation is associated with.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Operation * findAncestorOpInRegion(Operation &op)
Returns 'op' if 'op' lies in this region, or otherwise finds the ancestor of 'op' that lies in this r...
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Operation * getParentOp()
Return the parent operation this region is attached to.
This class provides an efficient unique identifier for a specific C++ type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
void printAsOperand(raw_ostream &os, AsmState &state) const
Print this value as if it were an operand.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
bool wasInterrupted() const
Returns true if the walk was interrupted.
static WalkResult interrupt()
virtual ~Extension()
Base virtual destructor.
State for analysis-enabled bufferization.
void bufferizeOutOfPlace(OpOperand &operand)
Mark the given OpOperand as out-of-place.
bool isWritable(Value value) const
Return true if the buffer of the given tensor value is writable.
const SetVector< Value > & findDefinitionsCached(OpOperand *opOperand)
Find the definitions of the given operand's value or retrieve them from the cache.
bool isReachableCached(Block *from, Block *to, const llvm::SmallPtrSetImpl< Block * > *barriers=nullptr)
Return true if to is reachable from from without crossing barriers.
bool isInPlace(OpOperand &opOperand) const override
Return true if the given OpResult has been decided to bufferize inplace.
bool canUseOpDominanceDueToBlocksCached(Block *readBlock, Block *writeBlock, llvm::ArrayRef< Block * > defBlocks, function_ref< bool()> compute)
Cached canUseOpDominanceDueToBlocks.
LogicalResult analyzeOp(Operation *op, const DominanceInfo &domInfo)
Analyze the given op and its nested ops.
bool isValueWritten(Value value) const
Return true if the buffer of the given tensor value is written to.
void unionEquivalenceClasses(Value v1, Value v2)
Union the equivalence classes of v1 and v2.
void gatherUndefinedTensorUses(Operation *op)
Find all tensor values in the given operation that have undefined contents and store them in undefine...
void resetCache() override
Reset cached data structures.
const OneShotBufferizationOptions & getOptions() const
Return a reference to the BufferizationOptions.
LogicalResult analyzeSingleOp(Operation *op, const DominanceInfo &domInfo)
Analyze a single op (without nested ops).
void applyOnEquivalenceClass(Value v, function_ref< void(Value)> fun) const
Apply fun to all the members of the equivalence class of v.
int64_t getStatNumTensorOutOfPlace() const
bool mayHaveUnstructuredControlFlow() const
True if any region in the analyzed IR has more than one block.
bool hasUndefinedContents(OpOperand *opOperand) const override
Return true if the given tensor has undefined contents.
bool areNonConflictingSubsetsCached(OpOperand *uRead, OpOperand *uConflictingWrite)
Return whether uRead and uConflictingWrite are non-conflicting subsets, with caching.
bool mayHaveParallelRegions() const
True if the analyzed IR may contain a parallel region.
void bufferizeInPlace(OpOperand &operand)
Mark the given OpOperand as in-place and merge the results' and operand's aliasing sets.
void applyOnAliases(Value v, function_ref< void(Value)> fun) const
Apply fun to all aliases of v.
bool areEquivalentBufferizedValues(Value v1, Value v2) const override
Return true if v1 and v2 bufferize to equivalent buffers.
OneShotAnalysisState(Operation *op, const OneShotBufferizationOptions &options)
bool areAliasingBufferizedValues(Value v1, Value v2) const override
Return true if v1 and v2 may bufferize to aliasing buffers.
void unionAliasSets(Value v1, Value v2)
Union the alias sets of v1 and v2.
~OneShotAnalysisState() override
void createAliasInfoEntry(Value v)
Add a new entry for v in the aliasInfo and equivalentInfo.
int64_t getStatNumTensorInPlace() const
Operation * getOwner() const
Return the owner of this operand.
LogicalResult bufferizeOp(Operation *op, const BufferizationOptions &options, BufferizationState &bufferizationState, BufferizationStatistics *statistics=nullptr)
Bufferize op and its nested ops that implement BufferizableOpInterface.
LogicalResult analyzeOp(Operation *op, OneShotAnalysisState &state, BufferizationStatistics *statistics=nullptr)
Analyze op and its nested ops.
LogicalResult insertTensorCopies(Operation *op, const OneShotBufferizationOptions &options, const BufferizationState &bufferizationState, BufferizationStatistics *statistics=nullptr)
Resolve RaW and other conflicts by inserting bufferization.alloc_tensor ops.
LogicalResult runOneShotBufferize(Operation *op, const OneShotBufferizationOptions &options, BufferizationState &state, BufferizationStatistics *statistics=nullptr)
Run One-Shot Bufferize on the given op: Analysis + Bufferization.
LogicalResult checkPreBufferizationAssumptions(Operation *op, const DominanceInfo &domInfo, OneShotAnalysisState &state)
Perform various checks on the input IR to see if it contains IR constructs that are unsupported by On...
Include the generated interface declarations.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
llvm::SetVector< T, Vector, Set, N > SetVector
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref
This iterator enumerates elements in "reverse" order.
Bufferization statistics for debugging.
int64_t numTensorOutOfPlace
Options for analysis-enabled bufferization.
AnalysisHeuristic analysisHeuristic
The heuristic controls the order in which ops are traversed during the analysis.
@ BottomUpFromTerminators