22#include "llvm/ADT/SmallVectorExtras.h"
34static Value castBuffer(OpBuilder &
b, Value buffer, Type type,
35 const BufferizationOptions &
options) {
47static bool doesNotAliasExternalValue(Value value, Region *region,
49 const OneShotAnalysisState &state) {
50 assert(region->
hasOneBlock() &&
"expected region with single block");
53 if (llvm::is_contained(exceptions, alias))
58 if (isa<OpResult>(alias) && !region->
isAncestor(aliasRegion))
65struct ConditionOpInterface
66 :
public BufferizableOpInterface::ExternalModel<ConditionOpInterface,
68 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
69 const AnalysisState &state)
const {
73 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
74 const AnalysisState &state)
const {
78 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
79 const AnalysisState &state)
const {
83 bool mustBufferizeInPlace(Operation *op, OpOperand &opOperand,
84 const AnalysisState &state)
const {
91 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
92 const BufferizationOptions &
options,
93 BufferizationState &state)
const {
94 auto conditionOp = cast<scf::ConditionOp>(op);
95 auto whileOp = cast<scf::WhileOp>(conditionOp->getParentOp());
97 SmallVector<Value> newArgs;
98 for (
const auto &it : llvm::enumerate(conditionOp.getArgs())) {
99 Value value = it.value();
100 if (isa<TensorLikeType>(value.
getType())) {
101 FailureOr<Value> maybeBuffer =
102 getBuffer(rewriter, value,
options, state);
105 FailureOr<BufferLikeType> resultType = bufferization::getBufferType(
106 whileOp.getAfterArguments()[it.index()],
options, state);
109 Value buffer = castBuffer(rewriter, *maybeBuffer, *resultType,
options);
110 newArgs.push_back(buffer);
112 newArgs.push_back(value);
116 replaceOpWithNewBufferizedOp<scf::ConditionOp>(
117 rewriter, op, conditionOp.getCondition(), newArgs);
124static scf::YieldOp getUniqueYieldOp(scf::ExecuteRegionOp executeRegionOp) {
126 for (
Block &block : executeRegionOp.getRegion()) {
127 if (
auto yieldOp = dyn_cast<scf::YieldOp>(block.getTerminator())) {
138struct ExecuteRegionOpInterface
139 :
public OpWithUnstructuredControlFlowBufferizableOpInterfaceExternalModel<
140 ExecuteRegionOpInterface, scf::ExecuteRegionOp> {
142 static bool supportsUnstructuredControlFlow() {
return true; }
144 bool isWritable(Operation *op, Value value,
145 const AnalysisState &state)
const {
149 LogicalResult verifyAnalysis(Operation *op,
150 const AnalysisState &state)
const {
151 auto executeRegionOp = cast<scf::ExecuteRegionOp>(op);
153 if (!getUniqueYieldOp(executeRegionOp))
154 return op->
emitOpError(
"op without unique scf.yield is not supported");
158 AliasingOpOperandList
159 getAliasingOpOperands(Operation *op, Value value,
160 const AnalysisState &state)
const {
161 if (
auto bbArg = dyn_cast<BlockArgument>(value))
162 return getAliasingBranchOpOperands(op, bbArg, state);
168 auto executeRegionOp = cast<scf::ExecuteRegionOp>(op);
170 assert(it != op->
getOpResults().end() &&
"invalid value");
171 size_t resultNum = std::distance(op->
getOpResults().begin(), it);
172 auto yieldOp = getUniqueYieldOp(executeRegionOp);
176 return {{&yieldOp->getOpOperand(resultNum), BufferRelation::Equivalent}};
179 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
180 const BufferizationOptions &
options,
181 BufferizationState &state)
const {
182 auto executeRegionOp = cast<scf::ExecuteRegionOp>(op);
183 auto yieldOp = getUniqueYieldOp(executeRegionOp);
184 TypeRange newResultTypes(yieldOp.getResults());
187 auto newOp = scf::ExecuteRegionOp::create(
188 rewriter, op->
getLoc(), newResultTypes, executeRegionOp.getNoInline());
189 newOp.getRegion().takeBody(executeRegionOp.getRegion());
192 for (
Block &block : newOp.getRegion())
199 SmallVector<Value> newResults;
200 for (
const auto &it : llvm::enumerate(executeRegionOp->getResultTypes())) {
201 if (isa<TensorLikeType>(it.value())) {
202 newResults.push_back(bufferization::ToTensorOp::create(
203 rewriter, executeRegionOp.getLoc(), it.value(),
204 newOp->getResult(it.index())));
206 newResults.push_back(newOp->getResult(it.index()));
211 rewriter.
replaceOp(executeRegionOp, newResults);
219 :
public BufferizableOpInterface::ExternalModel<IfOpInterface, scf::IfOp> {
220 AliasingOpOperandList
221 getAliasingOpOperands(Operation *op, Value value,
222 const AnalysisState &state)
const {
227 auto ifOp = cast<scf::IfOp>(op);
228 size_t resultNum = std::distance(op->
getOpResults().begin(),
230 OpOperand *thenOperand = &ifOp.thenYield()->getOpOperand(resultNum);
231 OpOperand *elseOperand = &ifOp.elseYield()->getOpOperand(resultNum);
232 return {{thenOperand, BufferRelation::Equivalent,
false},
233 {elseOperand, BufferRelation::Equivalent,
false}};
236 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
237 const BufferizationOptions &
options,
238 BufferizationState &state)
const {
239 OpBuilder::InsertionGuard g(rewriter);
240 auto ifOp = cast<scf::IfOp>(op);
243 SmallVector<Type> newTypes;
244 for (Value
result : ifOp.getResults()) {
245 if (!isa<TensorLikeType>(
result.getType())) {
246 newTypes.push_back(
result.getType());
249 auto bufferType = bufferization::getBufferType(
result,
options, state);
252 newTypes.push_back(*bufferType);
257 auto newIfOp = scf::IfOp::create(rewriter, ifOp.getLoc(), newTypes,
262 rewriter.
mergeBlocks(ifOp.thenBlock(), newIfOp.thenBlock());
263 rewriter.
mergeBlocks(ifOp.elseBlock(), newIfOp.elseBlock());
266 replaceOpWithBufferizedValues(rewriter, op, newIfOp->getResults());
271 FailureOr<BufferLikeType>
273 const BufferizationState &state,
274 SmallVector<Value> &invocationStack)
const {
275 auto ifOp = cast<scf::IfOp>(op);
276 auto thenYieldOp = cast<scf::YieldOp>(ifOp.thenBlock()->getTerminator());
277 auto elseYieldOp = cast<scf::YieldOp>(ifOp.elseBlock()->getTerminator());
281 auto opResult = cast<OpResult>(value);
282 auto thenValue = thenYieldOp.getOperand(opResult.getResultNumber());
283 auto elseValue = elseYieldOp.getOperand(opResult.getResultNumber());
284 BufferLikeType thenBufferType, elseBufferType;
285 if (isa<BufferLikeType>(thenValue.getType())) {
287 thenBufferType = cast<BufferLikeType>(thenValue.getType());
289 auto maybeBufferType = bufferization::getBufferType(
290 thenValue,
options, state, invocationStack);
291 if (
failed(maybeBufferType))
293 thenBufferType = *maybeBufferType;
295 if (isa<BufferLikeType>(elseValue.getType())) {
297 elseBufferType = cast<BufferLikeType>(elseValue.getType());
299 auto maybeBufferType = bufferization::getBufferType(
300 elseValue,
options, state, invocationStack);
301 if (
failed(maybeBufferType))
303 elseBufferType = *maybeBufferType;
307 if (thenBufferType == elseBufferType)
308 return cast<BufferLikeType>(thenBufferType);
310 auto reconciled =
options.reconcileBufferTypeMismatchFn(
311 thenBufferType, elseBufferType,
options);
313 return op->
emitError(
"incompatible buffer types on then/else branches");
321struct IndexSwitchOpInterface
322 :
public BufferizableOpInterface::ExternalModel<IndexSwitchOpInterface,
323 scf::IndexSwitchOp> {
324 AliasingOpOperandList
325 getAliasingOpOperands(Operation *op, Value value,
326 const AnalysisState &state)
const {
329 auto switchOp = cast<scf::IndexSwitchOp>(op);
330 int64_t resultNum = cast<OpResult>(value).getResultNumber();
331 AliasingOpOperandList
result;
332 for (int64_t i = 0, numCases = switchOp.getNumCases(); i < numCases; ++i) {
334 cast<scf::YieldOp>(switchOp.getCaseBlock(i).getTerminator());
335 result.addAlias(AliasingOpOperand(&yieldOp->getOpOperand(resultNum),
336 BufferRelation::Equivalent,
339 auto defaultYieldOp =
340 cast<scf::YieldOp>(switchOp.getDefaultBlock().getTerminator());
341 result.addAlias(AliasingOpOperand(&defaultYieldOp->getOpOperand(resultNum),
342 BufferRelation::Equivalent,
347 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
348 const BufferizationOptions &
options,
349 BufferizationState &state)
const {
350 OpBuilder::InsertionGuard g(rewriter);
351 auto switchOp = cast<scf::IndexSwitchOp>(op);
354 SmallVector<Type> newTypes;
355 for (Value
result : switchOp.getResults()) {
356 if (!isa<TensorLikeType>(
result.getType())) {
357 newTypes.push_back(
result.getType());
360 auto bufferType = bufferization::getBufferType(
result,
options, state);
363 newTypes.push_back(*bufferType);
368 auto newSwitchOp = scf::IndexSwitchOp::create(
369 rewriter, switchOp.getLoc(), newTypes, switchOp.getArg(),
370 switchOp.getCases(), switchOp.getCases().size());
373 for (
auto [src, dest] :
374 llvm::zip(switchOp.getCaseRegions(), newSwitchOp.getCaseRegions()))
377 newSwitchOp.getDefaultRegion(),
378 newSwitchOp.getDefaultRegion().begin());
381 replaceOpWithBufferizedValues(rewriter, op, newSwitchOp->getResults());
386 FailureOr<BufferLikeType>
388 const BufferizationState &state,
389 SmallVector<Value> &invocationStack)
const {
390 auto switchOp = cast<scf::IndexSwitchOp>(op);
392 int64_t resultNum = cast<OpResult>(value).getResultNumber();
394 auto getYieldedBufferType = [&](
Block &
b) -> FailureOr<BufferLikeType> {
395 auto yieldOp = cast<scf::YieldOp>(
b.getTerminator());
396 Value yieldedValue = yieldOp->getOperand(resultNum);
397 if (
auto bufferType = dyn_cast<BufferLikeType>(yieldedValue.
getType()))
399 return bufferization::getBufferType(yieldedValue,
options, state,
404 auto maybeBufferType = getYieldedBufferType(switchOp.getDefaultBlock());
405 if (
failed(maybeBufferType))
407 BufferLikeType bufferType = *maybeBufferType;
410 for (int64_t i = 0, numCases = switchOp.getNumCases(); i < numCases; ++i) {
411 auto yieldedBufferType = getYieldedBufferType(switchOp.getCaseBlock(i));
412 if (
failed(yieldedBufferType))
416 if (bufferType == *yieldedBufferType)
419 auto reconciled =
options.reconcileBufferTypeMismatchFn(
420 bufferType, *yieldedBufferType,
options);
422 return op->
emitError(
"incompatible buffer types on switch cases");
423 bufferType = *reconciled;
426 return cast<BufferLikeType>(bufferType);
434 for (
const auto &it : llvm::enumerate(values))
435 if (isa<TensorLikeType>(it.value().getType()))
436 result.insert(it.index());
444 const AnalysisState &state) {
445 unsigned int minSize = std::min(bbArgs.size(), yieldedValues.size());
447 for (
unsigned int i = 0; i < minSize; ++i) {
448 if (!isa<TensorLikeType>(bbArgs[i].
getType()) ||
449 !isa<TensorLikeType>(yieldedValues[i].
getType()))
451 if (state.areEquivalentBufferizedValues(bbArgs[i], yieldedValues[i]))
459static FailureOr<SmallVector<Value>>
460getBuffers(RewriterBase &rewriter,
const MutableOperandRange &operands,
461 const BufferizationOptions &
options, BufferizationState &state) {
462 SmallVector<Value>
result;
463 for (OpOperand &opOperand : operands) {
464 if (isa<TensorLikeType>(opOperand.get().getType())) {
465 FailureOr<Value> resultBuffer =
466 getBuffer(rewriter, opOperand.get(),
options, state);
469 result.push_back(*resultBuffer);
471 result.push_back(opOperand.get());
480static SmallVector<Value>
484 SmallVector<Value>
result;
485 for (
const auto &it : llvm::enumerate(bbArgs)) {
486 size_t idx = it.index();
487 Value val = it.value();
488 if (tensorIndices.contains(idx)) {
490 bufferization::ToTensorOp::create(rewriter, val.
getLoc(),
491 oldBbArgs[idx].getType(), val)
512static FailureOr<BufferLikeType> computeLoopRegionIterArgBufferType(
513 Operation *loopOp, BlockArgument iterArg, Value initArg, Value yieldedValue,
514 const BufferizationOptions &
options,
const BufferizationState &state,
515 SmallVector<Value> &invocationStack) {
517 auto initArgBufferType =
518 bufferization::getBufferType(initArg,
options, state, invocationStack);
519 if (
failed(initArgBufferType))
522 if (llvm::count(invocationStack, iterArg) >= 2) {
533 return *initArgBufferType;
537 BufferLikeType yieldedValueBufferType;
538 if (
auto bufferType = dyn_cast<BufferLikeType>(yieldedValue.
getType())) {
540 yieldedValueBufferType = bufferType;
544 auto maybeBufferType = bufferization::getBufferType(yieldedValue,
options,
545 state, invocationStack);
546 if (
failed(maybeBufferType))
548 yieldedValueBufferType = *maybeBufferType;
552 if (*initArgBufferType == yieldedValueBufferType)
553 return yieldedValueBufferType;
558 if (
auto iterTensorType = dyn_cast<TensorLikeType>(iterArg.
getType())) {
559 const auto emitOpError = [&]() {
return loopOp->
emitOpError(); };
560 assert(succeeded(iterTensorType.verifyCompatibleBufferType(
561 yieldedValueBufferType, emitOpError)) &&
562 "incompatible yielded type");
563 assert(succeeded(iterTensorType.verifyCompatibleBufferType(
564 *initArgBufferType, emitOpError)) &&
565 "incompatible init_arg type");
569 auto reconciled =
options.reconcileBufferTypeMismatchFn(
570 *initArgBufferType, yieldedValueBufferType,
options);
573 "init_arg and yielded value bufferize to incompatible buffer types");
580bool mayHaveZeroIterations(scf::ForOp forOp) {
583 if (!lb.has_value() || !ub.has_value())
591 :
public BufferizableOpInterface::ExternalModel<ForOpInterface,
593 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
594 const AnalysisState &state)
const {
595 auto forOp = cast<scf::ForOp>(op);
599 if (mayHaveZeroIterations(forOp))
604 return state.isValueRead(forOp.getTiedLoopRegionIterArg(&opOperand));
607 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
608 const AnalysisState &state)
const {
613 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
614 const AnalysisState &state)
const {
615 auto forOp = cast<scf::ForOp>(op);
616 OpResult opResult = forOp.getTiedLoopResult(&opOperand);
617 BufferRelation relation = bufferRelation(op, opResult, state);
618 return {{opResult, relation,
619 relation == BufferRelation::Equivalent}};
622 BufferRelation bufferRelation(Operation *op, OpResult opResult,
623 const AnalysisState &state)
const {
626 auto forOp = cast<scf::ForOp>(op);
627 BlockArgument bbArg = forOp.getTiedLoopRegionIterArg(opResult);
628 bool equivalentYield = state.areEquivalentBufferizedValues(
629 bbArg, forOp.getTiedLoopYieldedValue(bbArg)->get());
630 return equivalentYield ? BufferRelation::Equivalent
631 : BufferRelation::Unknown;
634 bool isWritable(Operation *op, Value value,
635 const AnalysisState &state)
const {
646 resolveConflicts(Operation *op, RewriterBase &rewriter,
647 const AnalysisState &analysisState,
648 const BufferizationState &bufferizationState)
const {
649 auto bufferizableOp = cast<BufferizableOpInterface>(op);
650 if (
failed(bufferizableOp.resolveTensorOpOperandConflicts(
651 rewriter, analysisState, bufferizationState)))
654 if (analysisState.getOptions().copyBeforeWrite)
662 auto forOp = cast<scf::ForOp>(op);
663 auto yieldOp = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
664 OpBuilder::InsertionGuard g(rewriter);
672 SmallVector<Value> yieldValues;
673 for (
const auto it : llvm::enumerate(yieldOp.getResults())) {
678 if (!
indices.contains(it.index()) ||
679 doesNotAliasExternalValue(
680 it.value(), &forOp.getRegion(),
681 forOp.getRegionIterArg(it.index()),
682 static_cast<const OneShotAnalysisState &
>(analysisState))) {
683 yieldValues.push_back(it.value());
686 FailureOr<Value> alloc = allocateTensorForShapedValue(
687 rewriter, yieldOp.getLoc(), it.value(), analysisState.getOptions(),
691 yieldValues.push_back(*alloc);
695 yieldOp, [&]() { yieldOp.getResultsMutable().assign(yieldValues); });
699 FailureOr<BufferLikeType>
701 const BufferizationState &state,
702 SmallVector<Value> &invocationStack)
const {
703 auto forOp = cast<scf::ForOp>(op);
705 assert(isa<TensorLikeType>(value.
getType()) &&
"expected tensor type");
707 if (
auto opResult = dyn_cast<OpResult>(value)) {
709 BlockArgument bbArg = forOp.getTiedLoopRegionIterArg(opResult);
710 return bufferization::getBufferType(bbArg,
options, state,
715 BlockArgument bbArg = cast<BlockArgument>(value);
716 unsigned resultNum = forOp.getTiedLoopResult(bbArg).getResultNumber();
719 auto yieldOp = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
720 Value yieldedValue = yieldOp.getOperand(resultNum);
721 BlockArgument iterArg = forOp.getRegionIterArgs()[resultNum];
722 Value initArg = forOp.getInitArgs()[resultNum];
723 return computeLoopRegionIterArgBufferType(
724 op, iterArg, initArg, yieldedValue,
options, state, invocationStack);
727 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
728 const BufferizationOptions &
options,
729 BufferizationState &state)
const {
730 auto forOp = cast<scf::ForOp>(op);
731 Block *oldLoopBody = forOp.getBody();
738 FailureOr<SmallVector<Value>> maybeInitArgs =
739 getBuffers(rewriter, forOp.getInitArgsMutable(),
options, state);
740 if (
failed(maybeInitArgs))
742 SmallVector<Value> initArgs = *maybeInitArgs;
745 SmallVector<Value> castedInitArgs;
746 for (
const auto &it : llvm::enumerate(initArgs)) {
747 Value initArg = it.value();
748 Value
result = forOp->getResult(it.index());
750 if (!isa<TensorLikeType>(
result.getType())) {
751 castedInitArgs.push_back(initArg);
754 auto targetType = bufferization::getBufferType(
result,
options, state);
757 castedInitArgs.push_back(
758 castBuffer(rewriter, initArg, *targetType,
options));
762 auto newForOp = scf::ForOp::create(
763 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
764 forOp.getStep(), castedInitArgs,
nullptr,
765 forOp.getUnsignedCmp());
766 newForOp->setDiscardableAttrs(
767 forOp->getDiscardableAttrDictionary().getValue());
768 Block *loopBody = newForOp.getBody();
773 SmallVector<Value> iterArgs =
774 getBbArgReplacements(rewriter, newForOp.getRegionIterArgs(),
775 forOp.getRegionIterArgs(),
indices);
776 iterArgs.insert(iterArgs.begin(), newForOp.getInductionVar());
779 rewriter.
mergeBlocks(oldLoopBody, loopBody, iterArgs);
782 replaceOpWithBufferizedValues(rewriter, op, newForOp->getResults());
794 LogicalResult verifyAnalysis(Operation *op,
795 const AnalysisState &state)
const {
797 static_cast<const OneShotBufferizationOptions &
>(state.getOptions());
798 if (
options.allowReturnAllocsFromLoops)
801 auto forOp = cast<scf::ForOp>(op);
802 auto yieldOp = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
804 if (!isa<TensorLikeType>(opResult.
getType()))
808 if (bufferRelation(op, opResult, state) != BufferRelation::Equivalent)
809 return yieldOp->emitError()
811 <<
" is not equivalent to the corresponding iter bbArg";
820struct WhileOpInterface
821 :
public BufferizableOpInterface::ExternalModel<WhileOpInterface,
823 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
824 const AnalysisState &state)
const {
829 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
830 const AnalysisState &state)
const {
835 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
836 const AnalysisState &state)
const {
837 auto whileOp = cast<scf::WhileOp>(op);
847 OpResult opResult = whileOp->getResult(idx);
848 BufferRelation relation = bufferRelation(op, opResult, state);
849 return {{opResult, relation,
850 relation == BufferRelation::Equivalent}};
853 BufferRelation bufferRelation(Operation *op, OpResult opResult,
854 const AnalysisState &state)
const {
859 auto whileOp = cast<scf::WhileOp>(op);
862 if (resultNumber >= whileOp.getBeforeArguments().size())
863 return BufferRelation::Unknown;
865 whileOp.getBeforeArguments()[resultNumber].getType())
866 return BufferRelation::Unknown;
868 auto conditionOp = whileOp.getConditionOp();
869 BlockArgument conditionBbArg = whileOp.getBeforeArguments()[resultNumber];
870 Value conditionOperand = conditionOp.getArgs()[resultNumber];
871 bool equivCondition =
872 state.areEquivalentBufferizedValues(conditionBbArg, conditionOperand);
874 auto yieldOp = whileOp.getYieldOp();
875 BlockArgument bodyBbArg = whileOp.getAfterArguments()[resultNumber];
876 Value yieldOperand = yieldOp.getOperand(resultNumber);
878 state.areEquivalentBufferizedValues(bodyBbArg, yieldOperand);
880 return equivCondition && equivYield ? BufferRelation::Equivalent
881 : BufferRelation::Unknown;
884 bool isWritable(Operation *op, Value value,
885 const AnalysisState &state)
const {
896 resolveConflicts(Operation *op, RewriterBase &rewriter,
897 const AnalysisState &analysisState,
898 const BufferizationState &bufferizationState)
const {
899 auto bufferizableOp = cast<BufferizableOpInterface>(op);
900 if (
failed(bufferizableOp.resolveTensorOpOperandConflicts(
901 rewriter, analysisState, bufferizationState)))
904 if (analysisState.getOptions().copyBeforeWrite)
914 OpBuilder::InsertionGuard g(rewriter);
915 auto whileOp = cast<scf::WhileOp>(op);
916 auto conditionOp = whileOp.getConditionOp();
921 whileOp.getBeforeArguments(), conditionOp.getArgs(), analysisState);
923 getEquivalentBuffers(whileOp.getAfterArguments(),
924 whileOp.getYieldOp().getResults(), analysisState);
928 SmallVector<Value> beforeYieldValues;
929 for (int64_t idx = 0;
930 idx < static_cast<int64_t>(conditionOp.getArgs().size()); ++idx) {
931 Value value = conditionOp.getArgs()[idx];
932 if (!isa<TensorLikeType>(value.
getType()) ||
933 (equivalentYieldsAfter.contains(idx) &&
934 equivalentYieldsBefore.contains(idx))) {
935 beforeYieldValues.push_back(value);
938 FailureOr<Value> alloc = allocateTensorForShapedValue(
939 rewriter, conditionOp.getLoc(), value, analysisState.getOptions(),
943 beforeYieldValues.push_back(*alloc);
946 conditionOp.getArgsMutable().assign(beforeYieldValues);
952 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
953 const BufferizationOptions &
options,
954 BufferizationState &state)
const {
955 auto whileOp = cast<scf::WhileOp>(op);
961 getTensorIndices(whileOp.getAfterArguments());
964 FailureOr<SmallVector<Value>> maybeInitArgs =
965 getBuffers(rewriter, whileOp.getInitsMutable(),
options, state);
966 if (
failed(maybeInitArgs))
968 SmallVector<Value> initArgs = *maybeInitArgs;
971 SmallVector<Value> castedInitArgs;
972 for (
const auto &it : llvm::enumerate(initArgs)) {
973 Value initArg = it.value();
974 Value beforeArg = whileOp.getBeforeArguments()[it.index()];
976 if (!isa<TensorLikeType>(beforeArg.
getType())) {
977 castedInitArgs.push_back(initArg);
980 auto targetType = bufferization::getBufferType(beforeArg,
options, state);
983 castedInitArgs.push_back(
984 castBuffer(rewriter, initArg, *targetType,
options));
988 SmallVector<Type> argsTypesAfter = llvm::map_to_vector(
989 whileOp.getAfterArguments(), [&](BlockArgument bbArg) {
990 if (!isa<TensorLikeType>(bbArg.getType()))
991 return bbArg.getType();
993 return llvm::cast<Type>(
994 *bufferization::getBufferType(bbArg, options, state));
999 TypeRange argsTypesBefore(argsRangeBefore);
1000 auto newWhileOp = scf::WhileOp::create(rewriter, whileOp.getLoc(),
1001 argsTypesAfter, castedInitArgs);
1004 SmallVector<Location> bbArgLocsBefore(castedInitArgs.size(),
1006 SmallVector<Location> bbArgLocsAfter(argsTypesAfter.size(),
1008 Block *newBeforeBody = &newWhileOp.getBefore().emplaceBlock();
1009 newWhileOp.getBefore().addArguments(argsTypesBefore, bbArgLocsBefore);
1010 Block *newAfterBody = &newWhileOp.getAfter().emplaceBlock();
1011 newWhileOp.getAfter().addArguments(argsTypesAfter, bbArgLocsAfter);
1017 SmallVector<Value> newBeforeArgs =
1018 getBbArgReplacements(rewriter, newWhileOp.getBeforeArguments(),
1019 whileOp.getBeforeArguments(), indicesBefore);
1020 rewriter.
mergeBlocks(whileOp.getBeforeBody(), newBeforeBody, newBeforeArgs);
1026 SmallVector<Value> newAfterArgs =
1027 getBbArgReplacements(rewriter, newWhileOp.getAfterArguments(),
1028 whileOp.getAfterArguments(), indicesAfter);
1029 rewriter.
mergeBlocks(whileOp.getAfterBody(), newAfterBody, newAfterArgs);
1032 replaceOpWithBufferizedValues(rewriter, op, newWhileOp->getResults());
1037 FailureOr<BufferLikeType>
1039 const BufferizationState &state,
1040 SmallVector<Value> &invocationStack)
const {
1041 auto whileOp = cast<scf::WhileOp>(op);
1043 assert(isa<TensorLikeType>(value.
getType()) &&
"expected tensor type");
1046 if (
auto bbArg = dyn_cast<BlockArgument>(value)) {
1048 Value initArg = whileOp.getInits()[bbArg.
getArgNumber()];
1049 auto yieldOp = whileOp.getYieldOp();
1050 Value yieldedValue = yieldOp.getOperand(bbArg.
getArgNumber());
1051 return computeLoopRegionIterArgBufferType(
1052 op, bbArg, initArg, yieldedValue,
options, state, invocationStack);
1060 if (
auto opResult = dyn_cast<OpResult>(value)) {
1062 }
else if (cast<BlockArgument>(value).getOwner()->getParent() ==
1063 &whileOp.getAfter()) {
1064 resultNum = cast<BlockArgument>(value).getArgNumber();
1066 llvm_unreachable(
"invalid value");
1068 Value conditionYieldedVal = whileOp.getConditionOp().getArgs()[resultNum];
1069 if (!isa<TensorLikeType>(conditionYieldedVal.
getType())) {
1071 return cast<BufferLikeType>(conditionYieldedVal.
getType());
1073 return bufferization::getBufferType(conditionYieldedVal,
options, state,
1087 LogicalResult verifyAnalysis(Operation *op,
1088 const AnalysisState &state)
const {
1089 auto whileOp = cast<scf::WhileOp>(op);
1091 static_cast<const OneShotBufferizationOptions &
>(state.getOptions());
1092 if (
options.allowReturnAllocsFromLoops)
1095 auto conditionOp = whileOp.getConditionOp();
1096 for (
const auto &it : llvm::enumerate(conditionOp.getArgs())) {
1097 Block *block = conditionOp->getBlock();
1098 if (!isa<TensorLikeType>(it.value().getType()))
1101 !state.areEquivalentBufferizedValues(it.value(),
1103 return conditionOp->emitError()
1104 <<
"Condition arg #" << it.index()
1105 <<
" is not equivalent to the corresponding iter bbArg";
1108 auto yieldOp = whileOp.getYieldOp();
1109 for (
const auto &it : llvm::enumerate(yieldOp.getResults())) {
1110 Block *block = yieldOp->getBlock();
1111 if (!isa<TensorLikeType>(it.value().getType()))
1114 !state.areEquivalentBufferizedValues(it.value(),
1116 return yieldOp->emitError()
1117 <<
"Yield operand #" << it.index()
1118 <<
" is not equivalent to the corresponding iter bbArg";
1127struct YieldOpInterface
1128 :
public BufferizableOpInterface::ExternalModel<YieldOpInterface,
1130 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
1131 const AnalysisState &state)
const {
1135 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
1136 const AnalysisState &state)
const {
1140 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
1141 const AnalysisState &state)
const {
1142 if (
auto ifOp = dyn_cast<scf::IfOp>(op->
getParentOp())) {
1144 BufferRelation::Equivalent,
false}};
1148 BufferRelation::Equivalent}};
1152 bool mustBufferizeInPlace(Operation *op, OpOperand &opOperand,
1153 const AnalysisState &state)
const {
1160 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
1161 const BufferizationOptions &
options,
1162 BufferizationState &state)
const {
1163 auto yieldOp = cast<scf::YieldOp>(op);
1164 if (!isa<scf::ExecuteRegionOp, scf::IfOp, scf::IndexSwitchOp, scf::ForOp,
1165 scf::WhileOp>(yieldOp->getParentOp()))
1166 return yieldOp->emitError(
"unsupported scf::YieldOp parent");
1168 SmallVector<Value> newResults;
1169 for (
const auto &it : llvm::enumerate(yieldOp.getResults())) {
1170 Value value = it.value();
1171 if (isa<TensorLikeType>(value.
getType())) {
1172 FailureOr<Value> maybeBuffer =
1173 getBuffer(rewriter, value,
options, state);
1176 Value buffer = *maybeBuffer;
1178 if (isa<scf::ForOp, scf::IfOp, scf::IndexSwitchOp>(
1179 yieldOp->getParentOp())) {
1180 FailureOr<BufferLikeType> resultType = bufferization::getBufferType(
1181 yieldOp->getParentOp()->getResult(it.index()),
options, state);
1184 buffer = castBuffer(rewriter, buffer, *resultType,
options);
1185 }
else if (
auto whileOp =
1186 dyn_cast<scf::WhileOp>(yieldOp->getParentOp())) {
1187 FailureOr<BufferLikeType> resultType = bufferization::getBufferType(
1188 whileOp.getBeforeArguments()[it.index()],
options, state);
1191 buffer = castBuffer(rewriter, buffer, *resultType,
options);
1193 newResults.push_back(buffer);
1195 newResults.push_back(value);
1199 replaceOpWithNewBufferizedOp<scf::YieldOp>(rewriter, op, newResults);
1208struct ForallOpInterface
1209 :
public BufferizableOpInterface::ExternalModel<ForallOpInterface,
1211 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
1212 const AnalysisState &state)
const {
1220 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
1221 const AnalysisState &state)
const {
1226 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
1227 const AnalysisState &state)
const {
1228 auto forallOp = cast<ForallOp>(op);
1230 {{forallOp.getTiedOpResult(&opOperand), BufferRelation::Equivalent}}};
1233 bool isWritable(Operation *op, Value value,
1234 const AnalysisState &state)
const {
1238 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
1239 const BufferizationOptions &
options,
1240 BufferizationState &state)
const {
1241 OpBuilder::InsertionGuard guard(rewriter);
1242 auto forallOp = cast<ForallOp>(op);
1243 int64_t rank = forallOp.getRank();
1246 SmallVector<Value> buffers;
1247 for (Value out : forallOp.getOutputs()) {
1248 FailureOr<Value> buffer = getBuffer(rewriter, out,
options, state);
1251 buffers.push_back(*buffer);
1256 for (
const auto &it : llvm::zip(
1257 forallOp.getBody()->getArguments().drop_front(rank), buffers)) {
1258 BlockArgument bbArg = std::get<0>(it);
1259 Value buffer = std::get<1>(it);
1260 Value bufferAsTensor = ToTensorOp::create(rewriter, forallOp.getLoc(),
1268 ForallOp newForallOp;
1269 newForallOp = ForallOp::create(
1270 rewriter, forallOp.getLoc(), forallOp.getMixedLowerBound(),
1271 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1277 rewriter.
eraseOp(newForallOp.getBody()->getTerminator());
1280 SmallVector<Value> replacementBbArgs;
1281 replacementBbArgs.append(newForallOp.getBody()->getArguments().begin(),
1282 newForallOp.getBody()->getArguments().end());
1283 replacementBbArgs.append(forallOp.getOutputs().size(), Value());
1284 rewriter.
mergeBlocks(forallOp.getBody(), newForallOp.getBody(),
1288 replaceOpWithBufferizedValues(rewriter, op, buffers);
1293 FailureOr<BufferLikeType>
1295 const BufferizationState &state,
1296 SmallVector<Value> &invocationStack)
const {
1297 auto forallOp = cast<ForallOp>(op);
1299 if (
auto bbArg = dyn_cast<BlockArgument>(value))
1302 return bufferization::getBufferType(
1303 forallOp.getTiedOpOperand(bbArg)->get(),
options, state,
1308 return bufferization::getBufferType(
1309 forallOp.getOutputs()[cast<OpResult>(value).getResultNumber()],
options,
1310 state, invocationStack);
1314 auto forallOp = cast<ForallOp>(op);
1318 for (
auto [lb, ub, step] :
1319 llvm::zip(forallOp.getMixedLowerBound(), forallOp.getMixedUpperBound(),
1320 forallOp.getMixedStep())) {
1333 if (*lbConstant + *stepConstant < *ubConstant)
1339 bool isParallelRegion(Operation *op,
unsigned index)
const {
1345struct InParallelOpInterface
1346 :
public BufferizableOpInterface::ExternalModel<InParallelOpInterface,
1348 LogicalResult bufferize(Operation *op, RewriterBase &
b,
1349 const BufferizationOptions &
options,
1350 BufferizationState &state)
const {
1351 llvm_unreachable(
"op does not have any tensor OpOperands / OpResults");
1363 ConditionOp::attachInterface<ConditionOpInterface>(*ctx);
1364 ExecuteRegionOp::attachInterface<ExecuteRegionOpInterface>(*ctx);
1365 ForOp::attachInterface<ForOpInterface>(*ctx);
1366 IfOp::attachInterface<IfOpInterface>(*ctx);
1367 IndexSwitchOp::attachInterface<IndexSwitchOpInterface>(*ctx);
1368 ForallOp::attachInterface<ForallOpInterface>(*ctx);
1369 InParallelOp::attachInterface<InParallelOpInterface>(*ctx);
1370 WhileOp::attachInterface<WhileOpInterface>(*ctx);
1371 YieldOp::attachInterface<YieldOpInterface>(*ctx);
static bool isRepetitiveRegion(Region *region, const BufferizationOptions &options)
static llvm::ManagedStatic< PassManagerOptions > options
static RankedTensorType getBufferType(const SparseTensorType &stt, bool needTmpCOO)
static Operation * getOwnerOfValue(Value value)
unsigned getArgNumber() const
Returns the number of this argument.
Block * getOwner() const
Returns the block that owns this argument.
MutableArrayRef< BlockArgument > BlockArgListType
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
IRValueT get() const
Return the current value being used by this operand.
MLIRContext is the top-level object for a collection of MLIR operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
unsigned getResultNumber() const
Returns the number of this result.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
result_range getOpResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
bool isProperAncestor(Region *other)
Return true if this region is a proper ancestor of the other region.
bool hasOneBlock()
Return true if this region has exactly one block.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
Type getType() const
Return the type of this value.
void replaceAllUsesWith(Value newValue)
Replace all uses of 'this' value with the new value, updating anything in the IR that uses 'this' to ...
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
void applyOnAliases(Value v, function_ref< void(Value)> fun) const
Apply fun to all aliases of v.
LogicalResult bufferizeBlockSignature(Block *block, RewriterBase &rewriter, const BufferizationOptions &options, BufferizationState &state)
Bufferize the signature of block and its callers (i.e., ops that have the given block as a successor)...
void registerBufferizableOpInterfaceExternalModels(DialectRegistry ®istry)
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet