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 AliasingValueList aliases = state.getAliasingValues(operand);
1183 for (AliasingValue alias : aliases)
1185 if (!checkConsistencyOnly && state.bufferizesToMemoryWrite(operand))
1186 usesWrite.insert(&operand);
1187 if (usesWrite.empty())
1192 for (AliasingValue alias : aliases)
1202 std::string
id =
"W_" + std::to_string(counter++);
1203 if (
auto opResult = dyn_cast<OpResult>(value)) {
1204 std::string attr =
id +
"[NOT-WRITABLE: result " +
1205 std::to_string(opResult.getResultNumber()) +
"]";
1206 opResult.getDefiningOp()->setDiscardableAttr(attr,
b.getUnitAttr());
1208 auto bbArg = cast<BlockArgument>(value);
1209 std::string attr =
id +
"[NOT-WRITABLE: bbArg " +
1210 std::to_string(bbArg.getArgNumber()) +
"]";
1211 bbArg.getOwner()->getParentOp()->setDiscardableAttr(attr,
b.getUnitAttr());
1220 bool checkConsistencyOnly =
false) {
1222 !checkConsistencyOnly && state.bufferizesToMemoryWrite(operand);
1228 for (AliasingValue alias : state.getAliasingValues(operand))
1230 foundWrite = !usesWrite.empty();
1237 bool foundReadOnly =
false;
1238 auto checkReadOnly = [&](
Value v) {
1240 foundReadOnly =
true;
1246 for (AliasingValue alias : state.getAliasingValues(operand))
1248 if (foundReadOnly) {
1249 LDBG() <<
"=> NOT WRITABLE";
1261const llvm::SetVector<Value> &
1264 if (!cachedDefinitions.count(value))
1265 cachedDefinitions[value] = findDefinitions(opOperand);
1266 return cachedDefinitions[value];
1271 if (!cfgReachabilityCache)
1272 cfgReachabilityCache = std::make_unique<CFGReachabilityCache>();
1273 return cfgReachabilityCache->isReachable(from, to, barriers);
1279 if (!opDominanceBlockCache)
1280 opDominanceBlockCache = std::make_unique<OpDominanceBlockCache>();
1281 return opDominanceBlockCache->getOrCompute(readBlock, writeBlock, defBlocks,
1287 auto key = std::make_pair(uRead, uConflictingWrite);
1288 auto [it,
inserted] = nonConflictingSubsetCache.try_emplace(key,
false);
1295 AnalysisState::resetCache();
1296 cachedDefinitions.clear();
1297 nonConflictingSubsetCache.clear();
1298 if (cfgReachabilityCache)
1299 cfgReachabilityCache->clear();
1300 if (opDominanceBlockCache)
1301 opDominanceBlockCache->clear();
1308 LDBG() <<
"//===-------------------------------------------===//\n"
1312 bool foundInterference =
1316 if (foundInterference)
1321 LDBG() <<
"//===-------------------------------------------===//";
1329 if (isa<TensorLikeType>(opOperand.get().getType()))
1339 if (
auto bufferizableOp = state.
getOptions().dynCastBufferizableOp(op)) {
1340 for (
OpResult opResult : op->getOpResults()) {
1341 if (!isa<TensorLikeType>(opResult.getType()))
1343 AliasingOpOperandList aliases = state.getAliasingOpOperands(opResult);
1344 if (aliases.getNumAliases() == 0)
1348 Value firstOperand = aliases.begin()->opOperand->get();
1349 bool allEquivalent =
true;
1350 for (AliasingOpOperand alias : aliases) {
1351 bool isEquiv = alias.relation == BufferRelation::Equivalent;
1352 bool isInPlace = state.
isInPlace(*alias.opOperand);
1353 Value operand = alias.opOperand->get();
1354 if (isEquiv && isInPlace && alias.isDefinite) {
1358 allEquivalent =
false;
1361 if (!isEquiv || !isInPlace)
1362 allEquivalent =
false;
1364 allEquivalent =
false;
1377 if (allEquivalent && !bufferizableOp.bufferizesToAllocation(opResult))
1400static SmallVector<Operation *>
1407 if (!traversedOps.insert(term))
1412 for (
Value v : term->getOperands()) {
1413 if (!isa<TensorLikeType>(v.
getType()))
1415 auto opResult = dyn_cast<OpResult>(v);
1418 worklist.push_back(opResult);
1420 while (!worklist.empty()) {
1421 OpResult opResult = worklist.pop_back_val();
1423 if (!traversedOps.insert(defOp))
1425 if (!term->getParentRegion()->findAncestorOpInRegion(*defOp))
1427 AliasingOpOperandList aliases = state.getAliasingOpOperands(opResult);
1428 for (
auto alias : aliases) {
1429 Value v = alias.opOperand->get();
1430 if (!isa<TensorLikeType>(v.
getType()))
1432 auto opResult = dyn_cast<OpResult>(v);
1435 worklist.push_back(opResult);
1443 if (!traversedOps.contains(op) && hasTensorSemantics(op))
1461 if (!hasTensorSemantics(op))
1463 orderedOps.push_back(op);
1465 switch (heuristic) {
1468 std::reverse(orderedOps.begin(), orderedOps.end());
1477 "expected that fuzzer seed it set");
1482 std::mt19937 g(
getOptions().analysisFuzzerSeed);
1483 llvm::shuffle(orderedOps.begin(), orderedOps.end(), g);
1487 llvm_unreachable(
"unsupported heuristic");
1508 WalkResult walkResult = op->
walk([&](BufferizableOpInterface op) {
1510 if (!
options.isOpAllowed(op.getOperation()))
1514 if (!op.supportsUnstructuredControlFlow()) {
1516 if (r.getBlocks().size() > 1) {
1517 op->
emitOpError(
"op or BufferizableOpInterface implementation does "
1518 "not support unstructured control flow, but at least "
1519 "one region has multiple blocks");
1530 walkResult = op->
walk([&](BufferizableOpInterface op) {
1532 if (!
options.isOpAllowed(op.getOperation()))
1538 if (
auto toTensorOp = dyn_cast<ToTensorOp>(op.getOperation())) {
1539 if (!toTensorOp.getRestrict() && !toTensorOp->getUses().empty()) {
1540 op->
emitOpError(
"to_tensor ops without `restrict` are not supported by "
1541 "One-Shot Analysis");
1547 if (isa<TensorLikeType>(opOperand.get().getType())) {
1549 opOperand, domInfo, state,
1556 op->
emitOpError(
"not bufferizable under the given constraints: "
1557 "cannot avoid RaW conflict");
1563 opOperand, state,
true)) {
1564 op->
emitOpError(
"not bufferizable under the given constraints: would "
1565 "write to read-only buffer");
1584 if (isa<TensorLikeType>(opOperand.get().getType()))
1594 auto buildAliasesArray = [&](
Value v) {
1598 llvm::raw_string_ostream stream(buffer);
1600 aliases.push_back(
b.getStringAttr(buffer));
1602 return b.getArrayAttr(aliases);
1609 if (llvm::isa<TensorLikeType>(opResult.getType())) {
1610 opResultAliasSets.push_back(buildAliasesArray(opResult));
1613 if (!opResultAliasSets.empty())
1615 b.getArrayAttr(opResultAliasSets));
1619 bool hasTensorBbArg =
false;
1622 for (
Block &block : r.getBlocks()) {
1625 if (llvm::isa<TensorLikeType>(bbArg.getType())) {
1626 bbArgAliasSets.push_back(buildAliasesArray(bbArg));
1627 hasTensorBbArg =
true;
1630 blockAliasSets.push_back(
b.getArrayAttr(bbArgAliasSets));
1632 regionAliasSets.push_back(
b.getArrayAttr(blockAliasSets));
1636 b.getArrayAttr(regionAliasSets));
1650 if (failed(state.
analyzeOp(op, domInfo)))
1658 bool failedAnalysis =
false;
1667 if (BufferizableOpInterface bufferizableOp =
1668 options.dynCastBufferizableOp(op))
1669 failedAnalysis |= failed(bufferizableOp.verifyAnalysis(state));
1678 return success(!failedAnalysis);
1687 "invalid combination of bufferization flags");
1689 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