diff --git a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h --- a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h +++ b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h @@ -49,14 +49,13 @@ bool isDefinite; }; -/// A maybe aliasing OpResult. If `isDefinite` is `true`, the OpResult is -/// guaranteed to alias at runtime. -struct AliasingOpResult { - AliasingOpResult(OpResult opResult, BufferRelation relation, - bool isDefinite = true) - : opResult(opResult), relation(relation), isDefinite(isDefinite) {} +/// A maybe aliasing Value. If `isDefinite` is `true`, the Value is guaranteed +/// to alias at runtime. +struct AliasingValue { + AliasingValue(Value value, BufferRelation relation, bool isDefinite = true) + : value(value), relation(relation), isDefinite(isDefinite) {} - OpResult opResult; + Value value; BufferRelation relation; bool isDefinite; }; @@ -90,12 +89,12 @@ }; /// A list of possible aliasing OpOperands. This list models the runtime -/// aliasing relationship for an OpResult. +/// aliasing relationship for a Value. using AliasingOpOperandList = AliasList; -/// A list of possible aliasing OpResults. This list models the runtime -/// aliasing relationship for an OpOperand. -using AliasingOpResultList = AliasList; +/// A list of possible aliasing Values. This list models the runtime aliasing +/// relationship for an OpOperand. +using AliasingValueList = AliasList; class OpFilter { public: @@ -418,15 +417,14 @@ /// tensor values. class AnalysisState { public: - /// Determine which OpOperand* will alias with `result` if the op is + /// Determine which OpOperand* will alias with `value` if the op is /// bufferized in place. Return all tensor OpOperand* if the op is not /// bufferizable. - AliasingOpOperandList getAliasingOpOperands(OpResult result) const; + AliasingOpOperandList getAliasingOpOperands(Value value) const; - /// Determine which OpResult will alias with `opOperand` if the op is - /// bufferized in place. Return all tensor OpResults if the op is not - /// bufferizable. - AliasingOpResultList getAliasingOpResults(OpOperand &opOperand) const; + /// Determine which Value will alias with `opOperand` if the op is bufferized + /// in place. Return all tensor Values if the op is not bufferizable. + AliasingValueList getAliasingValues(OpOperand &opOperand) const; /// Return true if `opOperand` bufferizes to a memory read. Return `true` if /// the op is not bufferizable. @@ -685,7 +683,7 @@ /// This is the default implementation of /// BufferizableOpInterface::getAliasingOpOperands. Should not be called from /// other places. -AliasingOpOperandList defaultGetAliasingOpOperands(OpResult opResult, +AliasingOpOperandList defaultGetAliasingOpOperands(Value value, const AnalysisState &state); /// This is the default implementation of @@ -709,11 +707,11 @@ /// This is the default implementation of getAliasingOpOperands in case the /// defining op does not implement the BufferizableOpInterface. -AliasingOpOperandList unknownGetAliasingOpOperands(OpResult opResult); +AliasingOpOperandList unknownGetAliasingOpOperands(Value value); -/// This is the default implementation of getAliasingOpResults in case the -/// owner op does not implement the BufferizableOpInterface. -AliasingOpResultList unknownGetAliasingOpResults(OpOperand &opOperand); +/// This is the default implementation of getAliasingValues in case the owner +/// op does not implement the BufferizableOpInterface. +AliasingValueList unknownGetAliasingValues(OpOperand &opOperand); } // namespace detail } // namespace bufferization diff --git a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.td b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.td --- a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.td +++ b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizableOpInterface.td @@ -18,23 +18,23 @@ Note: All "bufferizesTo*" and "getAliasing*" interface methods must be implemented conservatively. If it is not statically known whether an - OpOperand/OpResult bufferizes in a certain way (e.g., to a memory write), + OpOperand/Value bufferizes in a certain way (e.g., to a memory write), the worst case must be assumed (e.g., that it does). Similarly, "getAliasing*" interface methods may always return additional OpOperands or - OpResults, but must not miss an OpOperand or OpResult that could potentially + Values, but must not miss an OpOperand or Value that could potentially alias at runtime. }]; let cppNamespace = "::mlir::bufferization"; let methods = [ InterfaceMethod< /*desc=*/[{ - Return `true` if the given OpResult may bufferize to a new buffer - allocation. If it is statically unknown if the given OpResult + Return `true` if the given Value may bufferize to a new buffer + allocation. If it is statically unknown that the given Value bufferizes to a buffer allocation, `true` should be returned. }], /*retType=*/"bool", /*methodName=*/"bufferizesToAllocation", - /*args=*/(ins "::mlir::OpResult":$opResult), + /*args=*/(ins "::mlir::Value":$value), /*methodBody=*/"", /*defaultImplementation=*/"return false;" >, @@ -68,10 +68,9 @@ tensor type. This method will never be called on OpOperands that do not have an - aliasing OpResult. Intuitively, it does not make sense for an - OpOperand to bufferize to a memory write without returning an aliasing - tensor, because the write would have no visible effect outside of the - op. + aliasing Value. Intuitively, it does not make sense for an OpOperand + to bufferize to a memory write without returning an aliasing tensor, + because the write would have no visible effect outside of the op. Note: It is always safe to consider an OpOperand as a memory write, even if it does actually not write; however, this can introduce @@ -87,7 +86,7 @@ /*defaultImplementation=*/[{ // Does not have to be implemented for ops without tensor OpOperands. // Does not have to be implemented for OpOperands that do not have an - // aliasing OpResult. + // aliasing Value. llvm_unreachable("bufferizesToMemoryWrite not implemented"); }] >, @@ -221,21 +220,21 @@ >, InterfaceMethod< /*desc=*/[{ - Return the OpResults that may alias with a given OpOperand when + Return the Values that may alias with a given OpOperand when bufferized in-place. This method will never be called on OpOperands that do not have a tensor type. - This method can return multiple OpResults, indicating that a given + This method can return multiple Values, indicating that a given OpOperand may at runtime alias with any (or multiple) of the returned - OpResults. + Values. Each alias is specified with a degree of certainty: * MAYBE (`isDefinite = false`): At runtime, buffer(opOperand) may - alias with the specified OpResult. + alias with the specified Value. * DEFINITE (`isDefinite = true`, default): At runtime, buffer(opOperand) is guaranteed to alias the buffer of the specified - OpResult. This is a stronger property than MAYBE and allows for more + Value. This is a stronger property than MAYBE and allows for more precise analyses. DEFINITE properties should be used when possible. Furthermore, each alias is specified with a buffer relation: @@ -245,39 +244,39 @@ * `BufferRelation::Unknown`: There is no further information apart from the fact that both buffers alias. - False positives are allowed in the list of OpResults, but they can + False positives are allowed in the list of Values, but they can adversely affect the accuracy of the anlysis. On the contrary, omitting potential aliases is incorrect. One possible (conservative) implementation of this interface method, - that is always safe, is to return all tensor OpResults with + that is always safe, is to return all tensor Values with BufferRelation::Unknown and MAYBE. Examples: ``` - // aliasingOpResults(%t) = DEFINITE {Equivalent %r} + // aliasingValues(%t) = DEFINITE {Equivalent %r} %r = tensor.insert_slice %f into %t : tensor<10xf32> - // aliasingOpResults(%t) = DEFINITE {Unknown %r} + // aliasingValues(%t) = DEFINITE {Unknown %r} // Note: "Buffer is subset of buffer" relationship are not yet // supported, so "Unknown" is the best we can do for now. %r = tensor.extract_slice %t[0]][5][1] : tensor<10xf32> to tensor<5xf32> - // aliasingOpResults(%t1) = MAYBE {Equivalent %r} - // aliasingOpResults(%t2) = MAYBE {Equivalent %r} + // aliasingValues(%t1) = MAYBE {Equivalent %r} + // aliasingValues(%t2) = MAYBE {Equivalent %r} %r = arith.select %c, %t1, %t2 : tensor<10xf32> // A hypothetical op that bufferizes to rolling a dice and based on // the result to either return buffer(%t) or a newly allocated copy // thereof. - // aliasingOpResults(%t) = MAYBE {Equivalent %r} + // aliasingValues(%t) = MAYBE {Equivalent %r} %r = "dummy.alias_or_copy(%t) : (tensor<10xf32>) -> (tensor<10xf32>)" ``` }], - /*retType=*/"::mlir::bufferization::AliasingOpResultList", - /*methodName=*/"getAliasingOpResults", + /*retType=*/"::mlir::bufferization::AliasingValueList", + /*methodName=*/"getAliasingValues", /*args=*/(ins "::mlir::OpOperand &":$opOperand, "const ::mlir::bufferization::AnalysisState &":$state), /*methodBody=*/"", @@ -285,29 +284,29 @@ // Does not have to be implemented for ops without tensor OpOperands. assert(::llvm::isa<::mlir::TensorType>(opOperand.get().getType()) && "expected OpOperand with tensor type"); - llvm_unreachable("getAliasingOpResults not implemented"); + llvm_unreachable("getAliasingValues not implemented"); }] >, InterfaceMethod< /*desc=*/[{ - Return the OpOperands that alias with a given OpResult when - bufferized in-place. This method will never be called on OpResults - that do not have a tensor type. + Return the OpOperands that alias with a given Value when bufferized + in-place. This method will never be called on Values that do not + have a tensor type. - By default, this method is the inverse of `getAliasingOpResults`. Ops + By default, this method is the inverse of `getAliasingValues`. Ops with a region that yield values may want to override this method to return the OpOperands that are yielded by the terminator. This method can return multiple OpOperands, indicating that a given - OpResult may at runtime alias with any (or multiple) of the returned + Value may at runtime alias with any (or multiple) of the returned OpOperands. This property is specified with a degree of certainty: - * MAYBE (`isDefinite = false`): At runtime, buffer(opResult) may - alias with the specified OpOperand. + * MAYBE (`isDefinite = false`): At runtime, buffer(value) may alias + with the specified OpOperand. * DEFINITE (`isDefinite = true`, default): At runtime, - buffer(opResult) is guaranteed to alias the buffer of the specified + buffer(value) is guaranteed to alias the buffer of the specified OpOperand. This is a stronger property than MAYBE and allows for more precise analyses. DEFINITE properties should be used when possible. @@ -350,14 +349,14 @@ }], /*retType=*/"::mlir::bufferization::AliasingOpOperandList", /*methodName=*/"getAliasingOpOperands", - /*args=*/(ins "::mlir::OpResult":$opResult, + /*args=*/(ins "::mlir::Value":$value, "const ::mlir::bufferization::AnalysisState &":$state), /*methodBody=*/"", /*defaultImplementation=*/[{ - assert(::llvm::isa<::mlir::TensorType>(opResult.getType()) && - "expected OpResult with tensor type"); + assert(isa<::mlir::TensorType>(value.getType()) && + "expected tensor type"); return ::mlir::bufferization::detail::defaultGetAliasingOpOperands( - opResult, state); + value, state); }] >, InterfaceMethod< @@ -568,7 +567,7 @@ ::llvm::cast<::mlir::bufferization::BufferizableOpInterface>(getOperation()); return !bufferizableOp.bufferizesToMemoryRead(opOperand, state) && !bufferizableOp.bufferizesToMemoryWrite(opOperand, state) - && bufferizableOp.getAliasingOpResults(opOperand, state) + && bufferizableOp.getAliasingValues(opOperand, state) .getNumAliases() != 0; } }]; diff --git a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizationOps.td b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizationOps.td --- a/mlir/include/mlir/Dialect/Bufferization/IR/BufferizationOps.td +++ b/mlir/include/mlir/Dialect/Bufferization/IR/BufferizationOps.td @@ -95,7 +95,7 @@ bool resultBufferizesToMemoryWrite(OpResult opResult, const AnalysisState &state); - bool bufferizesToAllocation(OpResult opResult) { return true; } + bool bufferizesToAllocation(Value value) { return true; } bool bufferizesToMemoryRead(OpOperand &opOperand, const AnalysisState &state); @@ -103,7 +103,7 @@ bool bufferizesToMemoryWrite(OpOperand &opOperand, const AnalysisState &state); - AliasingOpResultList getAliasingOpResults( + AliasingValueList getAliasingValues( OpOperand &opOperand, const AnalysisState &state); FailureOr getBufferType( @@ -237,7 +237,7 @@ bool bufferizesToMemoryWrite(OpOperand &opOperand, const AnalysisState &state); - AliasingOpResultList getAliasingOpResults( + AliasingValueList getAliasingValues( OpOperand &opOperand, const AnalysisState &state); RankedTensorType getType() { @@ -295,7 +295,7 @@ return false; } - AliasingOpResultList getAliasingOpResults( + AliasingValueList getAliasingValues( OpOperand &opOperand, const AnalysisState &state) const { return {}; } @@ -459,7 +459,7 @@ return !getReadOnly(); } - AliasingOpResultList getAliasingOpResults( + AliasingValueList getAliasingValues( OpOperand &opOperand, const AnalysisState &state) const { return {}; } diff --git a/mlir/include/mlir/Dialect/Bufferization/IR/DstBufferizableOpInterfaceImpl.h b/mlir/include/mlir/Dialect/Bufferization/IR/DstBufferizableOpInterfaceImpl.h --- a/mlir/include/mlir/Dialect/Bufferization/IR/DstBufferizableOpInterfaceImpl.h +++ b/mlir/include/mlir/Dialect/Bufferization/IR/DstBufferizableOpInterfaceImpl.h @@ -36,8 +36,8 @@ return dstOp.isDpsInit(&opOperand); } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { // Output operands alias with their respective tied OpResults. auto dstOp = cast(op); if (dstOp.isDpsInit(&opOperand)) diff --git a/mlir/lib/Dialect/Arith/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/Arith/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/Arith/Transforms/BufferizableOpInterfaceImpl.cpp @@ -76,8 +76,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getResult(0), BufferRelation::Equivalent}}; } @@ -123,8 +123,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getOpResult(0) /*result*/, BufferRelation::Equivalent, /*isDefinite=*/false}}; } diff --git a/mlir/lib/Dialect/Bufferization/IR/BufferizableOpInterface.cpp b/mlir/lib/Dialect/Bufferization/IR/BufferizableOpInterface.cpp --- a/mlir/lib/Dialect/Bufferization/IR/BufferizableOpInterface.cpp +++ b/mlir/lib/Dialect/Bufferization/IR/BufferizableOpInterface.cpp @@ -209,9 +209,9 @@ SmallVector outOfPlaceOpOperands; DenseSet copiedOpOperands; DenseSet escapingOpOperandCopies; - SmallVector outOfPlaceOpResults; - DenseSet copiedOpResults; - DenseSet escapingOpResultCopies; + SmallVector outOfPlaceValues; + DenseSet copiedOpValues; + DenseSet escapingValueCopies; // Find all out-of-place OpOperands. for (OpOperand &opOperand : op->getOpOperands()) { @@ -223,34 +223,33 @@ if (llvm::isa(operandType)) return op->emitError("copying of unranked tensors is not implemented"); - AliasingOpResultList aliasingOpResults = - state.getAliasingOpResults(opOperand); + AliasingValueList aliasingValues = state.getAliasingValues(opOperand); // Is the result yielded from a block? Or are deallocations turned off // entirely? In either case, mark the allocation as "escaping", so that it // will not be deallocated. bool escape = !state.getOptions().createDeallocs || - llvm::any_of(aliasingOpResults, [&](AliasingOpResult a) { - return state.isTensorYielded(a.opResult); + llvm::any_of(aliasingValues, [&](AliasingValue a) { + return state.isTensorYielded(a.value); }); - if (aliasingOpResults.getNumAliases() == 1 && + if (aliasingValues.getNumAliases() == 1 && !state.bufferizesToMemoryWrite(opOperand) && - state.getAliasingOpOperands(aliasingOpResults.getAliases()[0].opResult) + state.getAliasingOpOperands(aliasingValues.getAliases()[0].value) .getNumAliases() == 1 && - !llvm::isa( - aliasingOpResults.getAliases()[0].opResult.getType())) { + !isa( + aliasingValues.getAliases()[0].value.getType())) { // The op itself does not write but may create exactly one alias. Instead // of copying the OpOperand, copy the OpResult. The OpResult can sometimes // be smaller than the OpOperand (e.g., in the case of an extract_slice, // where the result is usually a smaller part of the source). Do not apply // this optimization if the OpResult is an unranked tensor (because those // cannot be copied at the moment). - OpResult opResult = aliasingOpResults.getAliases()[0].opResult; - outOfPlaceOpResults.push_back(opResult); + Value value = aliasingValues.getAliases()[0].value; + outOfPlaceValues.push_back(value); if (!state.canOmitTensorCopy(opOperand)) - copiedOpResults.insert(opResult); + copiedOpValues.insert(value); if (escape) - escapingOpResultCopies.insert(opResult); + escapingValueCopies.insert(value); } else { // In all other cases, make a copy of the OpOperand. outOfPlaceOpOperands.push_back(&opOperand); @@ -273,17 +272,16 @@ rewriter.updateRootInPlace(op, [&]() { opOperand->set(*copy); }); } - // Insert copies of OpResults. + // Insert copies of Values. rewriter.setInsertionPointAfter(op); - for (OpResult opResult : outOfPlaceOpResults) { + for (Value value : outOfPlaceValues) { FailureOr copy = allocateTensorForShapedValue( - rewriter, op->getLoc(), opResult, - escapingOpResultCopies.contains(opResult), state.getOptions(), - copiedOpResults.count(opResult)); + rewriter, op->getLoc(), value, escapingValueCopies.contains(value), + state.getOptions(), copiedOpValues.count(value)); if (failed(copy)) return failure(); - SmallVector uses = llvm::to_vector(llvm::map_range( - opResult.getUses(), [](OpOperand &use) { return &use; })); + SmallVector uses = llvm::to_vector( + llvm::map_range(value.getUses(), [](OpOperand &use) { return &use; })); for (OpOperand *use : uses) { // Do not update the alloc_tensor op that we just created. if (use->getOwner() == copy->getDefiningOp()) @@ -425,29 +423,26 @@ } } -/// Determine which OpOperand* will alias with `opResult` if the op is -/// bufferized in place. Return all tensor OpOperand* if the op is not -/// bufferizable. -AliasingOpOperandList -AnalysisState::getAliasingOpOperands(OpResult opResult) const { - if (Operation *op = opResult.getDefiningOp()) +/// Determine which OpOperand* will alias with `value` if the op is bufferized +/// in place. Return all tensor OpOperand* if the op is not bufferizable. +AliasingOpOperandList AnalysisState::getAliasingOpOperands(Value value) const { + if (Operation *op = getOwnerOfValue(value)) if (auto bufferizableOp = getOptions().dynCastBufferizableOp(op)) - return bufferizableOp.getAliasingOpOperands(opResult, *this); + return bufferizableOp.getAliasingOpOperands(value, *this); // The op is not bufferizable. - return detail::unknownGetAliasingOpOperands(opResult); + return detail::unknownGetAliasingOpOperands(value); } -/// Determine which OpResult will alias with `opOperand` if the op is bufferized -/// in place. Return all tensor OpResults if the op is not bufferizable. -AliasingOpResultList -AnalysisState::getAliasingOpResults(OpOperand &opOperand) const { +/// Determine which Values will alias with `opOperand` if the op is bufferized +/// in place. Return all tensor Values if the op is not bufferizable. +AliasingValueList AnalysisState::getAliasingValues(OpOperand &opOperand) const { if (auto bufferizableOp = getOptions().dynCastBufferizableOp(opOperand.getOwner())) - return bufferizableOp.getAliasingOpResults(opOperand, *this); + return bufferizableOp.getAliasingValues(opOperand, *this); // The op is not bufferizable. - return detail::unknownGetAliasingOpResults(opOperand); + return detail::unknownGetAliasingValues(opOperand); } /// Return true if `opOperand` bufferizes to a memory read. Return `true` if the @@ -509,8 +504,8 @@ OpOperand *uMaybeReading = workingSet.pop_back_val(); // Skip over all ops that neither read nor write (but create an alias). if (bufferizesToAliasOnly(*uMaybeReading)) - for (AliasingOpResult alias : getAliasingOpResults(*uMaybeReading)) - for (OpOperand &use : alias.opResult.getUses()) + for (AliasingValue alias : getAliasingValues(*uMaybeReading)) + for (OpOperand &use : alias.value.getUses()) workingSet.push_back(&use); if (bufferizesToMemoryRead(*uMaybeReading)) return true; @@ -536,16 +531,7 @@ continue; } - if (llvm::isa(value)) { - if (config.alwaysIncludeLeaves) - result.insert(value); - continue; - } - - OpResult opResult = llvm::cast(value); - BufferizableOpInterface bufferizableOp = - options.dynCastBufferizableOp(opResult.getDefiningOp()); - if (!config.followUnknownOps && !bufferizableOp) { + if (!config.followUnknownOps && !options.dynCastBufferizableOp(value)) { // Stop iterating if `followUnknownOps` is unset and the op is either // not bufferizable or excluded in the OpFilter. if (config.alwaysIncludeLeaves) @@ -553,7 +539,7 @@ continue; } - AliasingOpOperandList aliases = getAliasingOpOperands(opResult); + AliasingOpOperandList aliases = getAliasingOpOperands(value); if (aliases.getNumAliases() == 0) { // The traversal ends naturally if there are no more OpOperands that // could be followed. @@ -582,7 +568,7 @@ if (config.followSameTypeOrCastsOnly && a.opOperand->get().getType() != value.getType() && - !opResult.getDefiningOp()) { + !value.getDefiningOp()) { // Stop iterating if `followSameTypeOrCastsOnly` is set but the alias is // has a different type and the op is not a cast. if (config.alwaysIncludeLeaves) @@ -626,10 +612,10 @@ return true; // Do not copy if the tensor is never read. - AliasingOpResultList aliases = getAliasingOpResults(opOperand); + AliasingValueList aliases = getAliasingValues(opOperand); if (!bufferizesToMemoryRead(opOperand) && - llvm::none_of( - aliases, [&](AliasingOpResult a) { return isValueRead(a.opResult); })) + llvm::none_of(aliases, + [&](AliasingValue a) { return isValueRead(a.value); })) return true; // Default: Cannot omit the copy. @@ -693,12 +679,12 @@ if (isa(op)) return true; - // Add all aliasing OpResults to the worklist. + // Add all aliasing Values to the worklist. // Note: In the absence of detailed analysis information (e.g., there may be - // no function call analysis information), this `getAliasingOpResult` is - // conservative and may report additional OpResults as potentially aliasing. - for (AliasingOpResult alias : getAliasingOpResults(*operand)) - for (OpOperand &use : alias.opResult.getUses()) + // no function call analysis information), this `getAliasingValues` is + // conservative and may report additional Values as potentially aliasing. + for (AliasingValue alias : getAliasingValues(*operand)) + for (OpOperand &use : alias.value.getUses()) worklist.push_back(&use); } @@ -991,19 +977,18 @@ return false; } -// Compute the AliasingOpOperandList for a given OpResult based on -// getAliasingOpResults. +// Compute the AliasingOpOperandList for a given Value based on +// getAliasingValues. AliasingOpOperandList bufferization::detail::defaultGetAliasingOpOperands( - OpResult opResult, const AnalysisState &state) { - Operation *op = opResult.getDefiningOp(); + Value value, const AnalysisState &state) { + Operation *op = getOwnerOfValue(value); SmallVector result; for (OpOperand &opOperand : op->getOpOperands()) { if (!llvm::isa(opOperand.get().getType())) continue; - AliasingOpResultList aliasingOpResults = - state.getAliasingOpResults(opOperand); - for (const auto &it : aliasingOpResults) - if (it.opResult == opResult) + AliasingValueList aliasingValues = state.getAliasingValues(opOperand); + for (const auto &it : aliasingValues) + if (it.value == value) result.emplace_back(&opOperand, it.relation, it.isDefinite); } return AliasingOpOperandList(std::move(result)); @@ -1051,21 +1036,37 @@ } AliasingOpOperandList -bufferization::detail::unknownGetAliasingOpOperands(OpResult opResult) { - // Conservatively assume that everything may be aliasing. +bufferization::detail::unknownGetAliasingOpOperands(Value value) { + // TODO: Take into account successor blocks. + // No aliasing in case of non-entry blocks. + if (auto bbArg = dyn_cast(value)) + if (bbArg.getOwner() != &bbArg.getOwner()->getParent()->getBlocks().front()) + return {}; + + // Unknown op: Conservatively assume that each OpResult may alias with every + // OpOperand. In addition, each block argument of an entry block may alias + // with every OpOperand. AliasingOpOperandList r; - for (OpOperand &operand : opResult.getDefiningOp()->getOpOperands()) - if (llvm::isa(operand.get().getType())) + for (OpOperand &operand : value.getDefiningOp()->getOpOperands()) + if (isa(operand.get().getType())) r.addAlias({&operand, BufferRelation::Unknown, /*isDefinite=*/false}); return r; } -AliasingOpResultList -bufferization::detail::unknownGetAliasingOpResults(OpOperand &opOperand) { - // Conservatively assume that everything may be aliasing. - AliasingOpResultList r; +AliasingValueList +bufferization::detail::unknownGetAliasingValues(OpOperand &opOperand) { + // TODO: Take into account successor blocks. + // Unknown op: Conservatively assume that each OpResult may alias with every + // OpOperand. In addition, each block argument of an entry block may alias + // with every OpOperand. + AliasingValueList r; for (OpResult result : opOperand.getOwner()->getOpResults()) if (llvm::isa(result.getType())) r.addAlias({result, BufferRelation::Unknown, /*isDefinite=*/false}); + for (Region ®ion : opOperand.getOwner()->getRegions()) + if (!region.getBlocks().empty()) + for (BlockArgument bbArg : region.getBlocks().front().getArguments()) + if (bbArg.getType().isa()) + r.addAlias({bbArg, BufferRelation::Unknown, /*isDefinite=*/false}); return r; } diff --git a/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp b/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp --- a/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp +++ b/mlir/lib/Dialect/Bufferization/IR/BufferizationOps.cpp @@ -224,9 +224,8 @@ return false; } -AliasingOpResultList -AllocTensorOp::getAliasingOpResults(OpOperand &opOperand, - const AnalysisState &state) { +AliasingValueList AllocTensorOp::getAliasingValues(OpOperand &opOperand, + const AnalysisState &state) { // This is a new allocation. It does not alias with any other buffer. return {}; } @@ -460,9 +459,8 @@ return false; } -AliasingOpResultList -CopyTensorOp::getAliasingOpResults(OpOperand &opOperand, - const AnalysisState &state) { +AliasingValueList CopyTensorOp::getAliasingValues(OpOperand &opOperand, + const AnalysisState &state) { if (&opOperand == &getOperation()->getOpOperand(1) /*dest*/) return {{getOperation()->getResult(0), BufferRelation::Equivalent}}; return {}; diff --git a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp @@ -164,14 +164,14 @@ opOperand.getOperandNumber()); } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { func::CallOp callOp = cast(op); FuncOp funcOp = getCalledFunction(callOp); assert(funcOp && "expected CallOp to a FuncOp"); if (getFuncOpAnalysisState(state, funcOp) != FuncOpAnalysisState::Analyzed) // FuncOp not analyzed yet. Any OpResult may be aliasing. - return detail::unknownGetAliasingOpResults(opOperand); + return detail::unknownGetAliasingValues(opOperand); // Get aliasing results from state. const FuncAnalysisState &funcState = getFuncAnalysisState(state); @@ -188,7 +188,7 @@ *equivalent == opOperand.getOperandNumber()) && "inconsistent analysis state"); } - AliasingOpResultList result; + AliasingValueList result; for (int64_t resultIdx : aliasingReturnVals) result.addAlias({callOp->getOpResult(resultIdx), equivalent.has_value() ? BufferRelation::Equivalent @@ -299,8 +299,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } diff --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp --- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp +++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp @@ -79,7 +79,10 @@ /// Attribute marker to specify op operands that bufferize in-place. constexpr StringLiteral kInPlaceOperandsAttrName = "__inplace_operands_attr__"; -constexpr StringLiteral kAliasSetAttrName = "__alias_set_attr__"; +constexpr StringLiteral kOpResultAliasSetAttrName = + "__opresult_alias_set_attr__"; + +constexpr StringLiteral kBbArgAliasSetAttrName = "__bbarg_alias_set_attr__"; /// Mark whether OpOperand will be bufferized inplace. static void setInPlaceOpOperand(OpOperand &opOperand, bool inPlace) { @@ -161,8 +164,8 @@ if (inplaceBufferized.contains(&operand)) return; inplaceBufferized.insert(&operand); - for (AliasingOpResult alias : getAliasingOpResults(operand)) - aliasInfo.unionSets(alias.opResult, operand.get()); + for (AliasingValue alias : getAliasingValues(operand)) + aliasInfo.unionSets(alias.value, operand.get()); ++statNumTensorInPlace; } @@ -258,14 +261,10 @@ bool OneShotAnalysisState::isWritable(Value value) const { // TODO: Out-of-place bufferized value could be considered writable. - if (auto bufferizableOp = getOptions().dynCastBufferizableOp(value)) - return bufferizableOp.isWritable(value, *this); - // Query BufferizableOpInterface to see if the BlockArgument is writable. - if (auto bbArg = dyn_cast(value)) - if (auto bufferizableOp = - getOptions().dynCastBufferizableOp(bbArg.getOwner()->getParentOp())) - return bufferizableOp.isWritable(bbArg, *this); + if (auto bufferizableOp = + getOptions().dynCastBufferizableOp(getOwnerOfValue(value))) + return bufferizableOp.isWritable(value, *this); // Not a bufferizable op: The conservative answer is "not writable". return false; @@ -614,10 +613,9 @@ // No conflict if the conflicting write and the definition are the same // use. - AliasingOpResultList aliases = - state.getAliasingOpResults(*uConflictingWrite); + AliasingValueList aliases = state.getAliasingValues(*uConflictingWrite); if (aliases.getNumAliases() == 1 && - aliases.getAliases()[0].opResult == definition) { + aliases.getAliases()[0].value == definition) { LLVM_DEBUG(llvm::dbgs() << " no conflict: definition and write are same\n"); continue; @@ -674,9 +672,9 @@ // there would then be no flow of data from the extract_slice operand to // its result's uses.) if (!state.bufferizesToMemoryWrite(use)) { - AliasingOpResultList aliases = state.getAliasingOpResults(use); - if (llvm::any_of(aliases, [&](AliasingOpResult a) { - return state.isValueRead(a.opResult); + AliasingValueList aliases = state.getAliasingValues(use); + if (llvm::any_of(aliases, [&](AliasingValue a) { + return state.isValueRead(a.value); })) res.insert(&use); } @@ -720,9 +718,9 @@ DenseSet usesRead, usesWrite; getAliasingReads(usesRead, operand.get(), state); getAliasingInplaceWrites(usesWrite, operand.get(), state); - for (AliasingOpResult alias : state.getAliasingOpResults(operand)) { - getAliasingReads(usesRead, alias.opResult, state); - getAliasingInplaceWrites(usesWrite, alias.opResult, state); + for (AliasingValue alias : state.getAliasingValues(operand)) { + getAliasingReads(usesRead, alias.value, state); + getAliasingInplaceWrites(usesWrite, alias.value, state); } if (!checkConsistencyOnly && state.bufferizesToMemoryWrite(operand)) usesWrite.insert(&operand); @@ -760,8 +758,8 @@ // Collect writes of all aliases of OpOperand and OpResult. DenseSet usesWrite; getAliasingInplaceWrites(usesWrite, operand.get(), state); - for (AliasingOpResult alias : state.getAliasingOpResults(operand)) - getAliasingInplaceWrites(usesWrite, alias.opResult, state); + for (AliasingValue alias : state.getAliasingValues(operand)) + getAliasingInplaceWrites(usesWrite, alias.value, state); foundWrite = !usesWrite.empty(); } @@ -778,8 +776,8 @@ } }; state.applyOnAliases(operand.get(), checkReadOnly); - for (AliasingOpResult alias : state.getAliasingOpResults(operand)) - state.applyOnAliases(alias.opResult, checkReadOnly); + for (AliasingValue alias : state.getAliasingValues(operand)) + state.applyOnAliases(alias.value, checkReadOnly); if (foundReadOnly) { LLVM_DEBUG(llvm::dbgs() << "=> NOT WRITABLE\n"); return true; @@ -881,8 +879,8 @@ // allocation, it is a definite, equivalent alias. E.g.: // // aliasingOpOperands(%r) = {(%t0, EQUIV, MAYBE), (%t1, EQUIV, MAYBE)} - // aliasingOpResults(%t0) = {(%r, EQUIV, MAYBE)} - // aliasingOpResults(%t1) = {(%r, EQUIV, MAYBE)} + // aliasingValues(%t0) = {(%r, EQUIV, MAYBE)} + // aliasingValues(%t1) = {(%r, EQUIV, MAYBE)} // %r = arith.select %c, %t0, %t1 : tensor // // If %t0 and %t1 are equivalent, it is safe to union the equivalence @@ -1032,22 +1030,48 @@ const OneShotAnalysisState &state) { AsmState asmState(op); Builder b(op->getContext()); + // Helper function to build an array attribute of aliasing SSA value strings. + auto buildAliasesArray = [&](Value v) { + SmallVector aliases; + state.applyOnAliases(v, [&](Value alias) { + std::string buffer; + llvm::raw_string_ostream stream(buffer); + alias.printAsOperand(stream, asmState); + aliases.push_back(b.getStringAttr(stream.str())); + }); + return b.getArrayAttr(aliases); + }; + op->walk([&](Operation *op) { - SmallVector aliasSets; + // Build alias set array for every OpResult. + SmallVector opResultAliasSets; for (OpResult opResult : op->getOpResults()) { if (llvm::isa(opResult.getType())) { - SmallVector aliases; - state.applyOnAliases(opResult, [&](Value alias) { - std::string buffer; - llvm::raw_string_ostream stream(buffer); - alias.printAsOperand(stream, asmState); - aliases.push_back(b.getStringAttr(stream.str())); - }); - aliasSets.push_back(b.getArrayAttr(aliases)); + opResultAliasSets.push_back(buildAliasesArray(opResult)); + } + } + if (!opResultAliasSets.empty()) + op->setAttr(kOpResultAliasSetAttrName, b.getArrayAttr(opResultAliasSets)); + + // Build alias set array for every BlockArgument. + SmallVector regionAliasSets; + bool hasTensorBbArg = false; + for (Region &r : op->getRegions()) { + SmallVector blockAliasSets; + for (Block &block : r.getBlocks()) { + SmallVector bbArgAliasSets; + for (BlockArgument bbArg : block.getArguments()) { + if (llvm::isa(bbArg.getType())) { + bbArgAliasSets.push_back(buildAliasesArray(bbArg)); + hasTensorBbArg = true; + } + } + blockAliasSets.push_back(b.getArrayAttr(bbArgAliasSets)); } + regionAliasSets.push_back(b.getArrayAttr(blockAliasSets)); } - if (!aliasSets.empty()) - op->setAttr(kAliasSetAttrName, b.getArrayAttr(aliasSets)); + if (hasTensorBbArg) + op->setAttr(kBbArgAliasSetAttrName, b.getArrayAttr(regionAliasSets)); }); } diff --git a/mlir/lib/Dialect/MemRef/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/MemRef/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/MemRef/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/MemRef/Transforms/BufferizableOpInterfaceImpl.cpp @@ -21,8 +21,8 @@ struct TensorStoreOpInterface : public BufferizableOpInterface::ExternalModel { - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } diff --git a/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp @@ -57,8 +57,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -105,7 +105,7 @@ : public BufferizableOpInterface::ExternalModel { AliasingOpOperandList - getAliasingOpOperands(Operation *op, OpResult opResult, + getAliasingOpOperands(Operation *op, Value value, const AnalysisState &state) const { // ExecuteRegionOps do not have tensor OpOperands. The yielded value can be // any SSA value that is in scope. To allow for use-def chain traversal @@ -113,7 +113,7 @@ // is considered to be aliasing with the result. auto executeRegionOp = cast(op); size_t resultNum = std::distance(op->getOpResults().begin(), - llvm::find(op->getOpResults(), opResult)); + llvm::find(op->getOpResults(), value)); // TODO: Support multiple blocks. assert(executeRegionOp.getRegion().getBlocks().size() == 1 && "expected exactly 1 block"); @@ -160,7 +160,7 @@ struct IfOpInterface : public BufferizableOpInterface::ExternalModel { AliasingOpOperandList - getAliasingOpOperands(Operation *op, OpResult opResult, + getAliasingOpOperands(Operation *op, Value value, const AnalysisState &state) const { // IfOps do not have tensor OpOperands. The yielded value can be any SSA // value that is in scope. To allow for use-def chain traversal through @@ -168,7 +168,7 @@ // branches are considered to be aliasing with the result. auto ifOp = cast(op); size_t resultNum = std::distance(op->getOpResults().begin(), - llvm::find(op->getOpResults(), opResult)); + llvm::find(op->getOpResults(), value)); OpOperand *thenOperand = &ifOp.thenYield()->getOpOperand(resultNum); OpOperand *elseOperand = &ifOp.elseYield()->getOpOperand(resultNum); return {{thenOperand, BufferRelation::Equivalent, /*isDefinite=*/false}, @@ -426,8 +426,8 @@ return true; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { auto forOp = cast(op); OpResult opResult = forOp.getResultForOpOperand(opOperand); BufferRelation relation = bufferRelation(op, opResult, state); @@ -643,8 +643,8 @@ return true; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { auto whileOp = cast(op); unsigned int idx = opOperand.getOperandNumber(); @@ -938,8 +938,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { if (auto ifOp = dyn_cast(op->getParentOp())) { return {{op->getParentOp()->getResult(opOperand.getOperandNumber()), BufferRelation::Equivalent, /*isDefinite=*/false}}; @@ -1039,8 +1039,8 @@ return true; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { auto forallOp = cast(op); return { {{forallOp.getTiedOpResult(&opOperand), BufferRelation::Equivalent}}}; diff --git a/mlir/lib/Dialect/Shape/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Shape/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/Shape/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/Shape/Transforms/BufferizableOpInterfaceImpl.cpp @@ -28,7 +28,7 @@ : public BufferizableOpInterface::ExternalModel { AliasingOpOperandList - getAliasingOpOperands(Operation *op, OpResult opResult, + getAliasingOpOperands(Operation *op, Value value, const AnalysisState &state) const { // AssumingOps do not have tensor OpOperands. The yielded value can be any // SSA value that is in scope. To allow for use-def chain traversal through @@ -36,7 +36,7 @@ // to be aliasing with the result. auto assumingOp = cast(op); size_t resultNum = std::distance(op->getOpResults().begin(), - llvm::find(op->getOpResults(), opResult)); + llvm::find(op->getOpResults(), value)); // TODO: Support multiple blocks. assert(assumingOp.getDoRegion().getBlocks().size() == 1 && "expected exactly 1 block"); @@ -94,8 +94,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { assert(isa(op->getParentOp()) && "expected that parent is an AssumingOp"); OpResult opResult = diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.cpp @@ -29,9 +29,7 @@ struct ConcatenateOpInterface : public BufferizableOpInterface::ExternalModel< ConcatenateOpInterface, sparse_tensor::ConcatenateOp> { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand, const AnalysisState &state) const { @@ -43,8 +41,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -57,7 +55,7 @@ struct ConvertOpInterface : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { + bool bufferizesToAllocation(Operation *op, Value value) const { // ConvertOps may allocate. (Unless they convert between two identical // types, then they fold away.) return true; @@ -73,8 +71,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -97,8 +95,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getOpResult(0), BufferRelation::Equivalent}}; } }; @@ -112,15 +110,13 @@ return false; } - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } }; struct PackOpInterface : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { + bool bufferizesToAllocation(Operation *op, Value value) const { // PackOp reuses all the buffers instead of allocating new ones return false; } @@ -135,8 +131,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { assert(op->getNumResults() == 1); // PackOp reuses the input tensors as values/coordinates instead of // creating new ones when packing into a COO format. @@ -152,7 +148,7 @@ struct UnpackOpInterface : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { + bool bufferizesToAllocation(Operation *op, Value value) const { // The output buffer is pre-allocated by the user. return false; } @@ -170,8 +166,8 @@ return opOperand.getOperandNumber() > 0; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { assert(2 * (op->getNumOperands() - 1) == op->getNumResults()); if (opOperand.getOperandNumber() == 0) @@ -196,8 +192,8 @@ return true; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { // InsertOp returns an alias of its operand. assert(op->getNumResults() == 1); return {{op->getOpResult(0), BufferRelation::Equivalent}}; @@ -217,8 +213,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } }; @@ -239,8 +235,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } }; @@ -260,8 +256,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } }; @@ -281,8 +277,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } }; @@ -302,8 +298,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } }; diff --git a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp @@ -41,8 +41,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getResult(0), BufferRelation::Equivalent}}; } @@ -125,8 +125,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { // TODO: CollapseShapeOp may allocate at runtime. return {{op->getOpResult(0), BufferRelation::Equivalent}}; } @@ -235,8 +235,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -295,8 +295,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getOpResult(0), BufferRelation::Equivalent}}; } @@ -349,8 +349,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getOpResult(0), BufferRelation::Unknown}}; } @@ -413,8 +413,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -458,9 +458,7 @@ : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const { @@ -578,9 +576,7 @@ : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const { @@ -838,9 +834,7 @@ struct PadOpInterface : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand, const AnalysisState &state) const { @@ -852,8 +846,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -950,8 +944,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -983,8 +977,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getOpResult(0), BufferRelation::Equivalent}}; } @@ -1025,8 +1019,8 @@ struct ParallelInsertSliceOpInterface : public BufferizableOpInterface::ExternalModel< ParallelInsertSliceOpInterface, ParallelInsertSliceOp> { - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -1113,9 +1107,7 @@ : public BufferizableOpInterface::ExternalModel { - bool bufferizesToAllocation(Operation *op, OpResult opResult) const { - return true; - } + bool bufferizesToAllocation(Operation *op, Value value) const { return true; } LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const { diff --git a/mlir/lib/Dialect/Vector/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Vector/Transforms/BufferizableOpInterfaceImpl.cpp --- a/mlir/lib/Dialect/Vector/Transforms/BufferizableOpInterfaceImpl.cpp +++ b/mlir/lib/Dialect/Vector/Transforms/BufferizableOpInterfaceImpl.cpp @@ -42,8 +42,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -142,8 +142,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {}; } @@ -169,13 +169,13 @@ : public BufferizableOpInterface::ExternalModel { AliasingOpOperandList - getAliasingOpOperands(Operation *op, OpResult opResult, + getAliasingOpOperands(Operation *op, Value value, const AnalysisState &state) const { // MaskOps do not have tensor OpOperands. The yielded values are the result // of the wrapped op. auto maskOp = cast(op); size_t resultNum = std::distance(op->getOpResults().begin(), - llvm::find(op->getOpResults(), opResult)); + llvm::find(op->getOpResults(), value)); auto yieldOp = cast(maskOp.getMaskRegion().front().getTerminator()); return {{&yieldOp->getOpOperand(resultNum), BufferRelation::Equivalent}}; @@ -265,8 +265,8 @@ return false; } - AliasingOpResultList getAliasingOpResults(Operation *op, OpOperand &opOperand, - const AnalysisState &state) const { + AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, + const AnalysisState &state) const { return {{op->getParentOp()->getResult(opOperand.getOperandNumber()), BufferRelation::Equivalent}}; } diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir --- a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir +++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir @@ -6,6 +6,7 @@ // RUN: FileCheck %s --check-prefix=CHECK-ALIAS-SETS // CHECK-LABEL: func @unknown_op_aliasing( +// CHECK-ALIAS-SETS-LABEL: func @unknown_op_aliasing( func.func @unknown_op_aliasing(%f: f32, %f2: f32, %pos: index) -> f32 { // CHECK-ALIAS-SETS: %[[empty:.*]] = tensor.empty @@ -20,8 +21,8 @@ %alias = "dummy.dummy_op"(%1) : (tensor<10xf32>) -> (tensor<10xf32>) // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]} - // CHECK-ALIAS-SETS: %[[fill2:.*]] = linalg.fill {__alias_set_attr__ = [ - // CHECK-ALIAS-SETS-SAME: ["%[[fill2]]", "%[[fill1]]", "%[[empty]]"]] + // CHECK-ALIAS-SETS: %[[fill2:.*]] = linalg.fill + // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = [{{\[}}"%[[fill2]]", "%[[fill1]]", "%[[empty]]"]] %2 = linalg.fill ins(%f2 : f32) outs(%1 : tensor<10xf32>) -> tensor<10xf32> %3 = tensor.extract %alias[%pos] : tensor<10xf32> return %3 : f32 @@ -29,6 +30,23 @@ // ----- +// CHECK-LABEL: func @unknown_op_bbarg_aliasing( +// CHECK-ALIAS-SETS-LABEL: func @unknown_op_bbarg_aliasing( +func.func @unknown_op_bbarg_aliasing() { + %0 = tensor.empty() : tensor<7xf32> + + // %arg0 is not aliasing with %0 because it bufferizes out-of-place. + // CHECK-ALIAS-SETS: "dummy.dummy_op" + // CHECK-ALIAS-SETS-NEXT: ^{{.*}}(%[[arg:.*]]: tensor<7xf32>): + // CHECK-ALIAS-SETS-NEXT: }) {__bbarg_alias_set_attr__ = [{{\[}}[{{\[}}"%[[arg]]"]]]], __inplace_operands_attr__ = ["false"]} : (tensor<7xf32>) -> () + "dummy.dummy_op"(%0) ({ + ^bb0(%arg1: tensor<7xf32>): + }) : (tensor<7xf32>) -> () + return +} + +// ----- + // CHECK-LABEL: func @unknown_op_writing( func.func @unknown_op_writing(%f: f32, %f2: f32, %pos: index) -> f32 { %0 = tensor.empty() : tensor<10xf32> @@ -98,3 +116,45 @@ %2 = tensor.extract %t[%idx] : tensor<5xf32> return %2 : f32 } + +// ----- + +// CHECK-LABEL: func @bbarg_of_unknown_op( +func.func @bbarg_of_unknown_op(%f: f32) { + %0 = tensor.empty() : tensor<10xf32> + // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]} + %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32> + + // The op is not bufferizable because %1 is assumed to alias with %arg1. + // BlockArguments are considered "not writable" by default. So %1 is also + // considered "not writable". + + // CHECK: "dummy.dummy_op" + // CHECK: {__inplace_operands_attr__ = ["false"]} : (tensor<10xf32>) -> () + "dummy.dummy_op"(%1) ({ + ^bb0(%arg1: tensor<10xf32>): + }) : (tensor<10xf32>) -> () + return +} + +// ----- + +// CHECK-LABEL: func @bbarg_of_unknown_op_2( +func.func @bbarg_of_unknown_op_2(%f: f32) { + %0 = tensor.empty() : tensor<10xf32> + // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]} + %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32> + + // The op is not bufferizable because %1 is assumed to alias with %arg1. + // BlockArguments are considered "not writable" by default. So %1 is also + // considered "not writable". + + // CHECK: "dummy.dummy_op" + "dummy.dummy_op"(%1) ({ + ^bb0(%arg1: tensor<10xf32>): + // CHECK: "dummy.another_op"(%{{.*}}) {__inplace_operands_attr__ = ["false"]} + "dummy.another_op"(%arg1) : (tensor<10xf32>) -> () + }) : (tensor<10xf32>) -> () + // CHECK: {__inplace_operands_attr__ = ["false"]} : (tensor<10xf32>) -> () + return +}