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())) {
560 assert(succeeded(iterTensorType.verifyCompatibleBufferType(
562 "incompatible yielded type");
563 assert(succeeded(iterTensorType.verifyCompatibleBufferType(
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->setAttrs(forOp->getAttrs());
767 Block *loopBody = newForOp.getBody();
772 SmallVector<Value> iterArgs =
773 getBbArgReplacements(rewriter, newForOp.getRegionIterArgs(),
774 forOp.getRegionIterArgs(),
indices);
775 iterArgs.insert(iterArgs.begin(), newForOp.getInductionVar());
778 rewriter.
mergeBlocks(oldLoopBody, loopBody, iterArgs);
781 replaceOpWithBufferizedValues(rewriter, op, newForOp->getResults());
793 LogicalResult verifyAnalysis(Operation *op,
794 const AnalysisState &state)
const {
796 static_cast<const OneShotBufferizationOptions &
>(state.getOptions());
797 if (
options.allowReturnAllocsFromLoops)
800 auto forOp = cast<scf::ForOp>(op);
801 auto yieldOp = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
803 if (!isa<TensorLikeType>(opResult.
getType()))
807 if (bufferRelation(op, opResult, state) != BufferRelation::Equivalent)
808 return yieldOp->emitError()
810 <<
" is not equivalent to the corresponding iter bbArg";
819struct WhileOpInterface
820 :
public BufferizableOpInterface::ExternalModel<WhileOpInterface,
822 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
823 const AnalysisState &state)
const {
828 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
829 const AnalysisState &state)
const {
834 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
835 const AnalysisState &state)
const {
836 auto whileOp = cast<scf::WhileOp>(op);
846 OpResult opResult = whileOp->getResult(idx);
847 BufferRelation relation = bufferRelation(op, opResult, state);
848 return {{opResult, relation,
849 relation == BufferRelation::Equivalent}};
852 BufferRelation bufferRelation(Operation *op, OpResult opResult,
853 const AnalysisState &state)
const {
858 auto whileOp = cast<scf::WhileOp>(op);
861 if (resultNumber >= whileOp.getBeforeArguments().size())
862 return BufferRelation::Unknown;
864 whileOp.getBeforeArguments()[resultNumber].getType())
865 return BufferRelation::Unknown;
867 auto conditionOp = whileOp.getConditionOp();
868 BlockArgument conditionBbArg = whileOp.getBeforeArguments()[resultNumber];
869 Value conditionOperand = conditionOp.getArgs()[resultNumber];
870 bool equivCondition =
871 state.areEquivalentBufferizedValues(conditionBbArg, conditionOperand);
873 auto yieldOp = whileOp.getYieldOp();
874 BlockArgument bodyBbArg = whileOp.getAfterArguments()[resultNumber];
875 Value yieldOperand = yieldOp.getOperand(resultNumber);
877 state.areEquivalentBufferizedValues(bodyBbArg, yieldOperand);
879 return equivCondition && equivYield ? BufferRelation::Equivalent
880 : BufferRelation::Unknown;
883 bool isWritable(Operation *op, Value value,
884 const AnalysisState &state)
const {
895 resolveConflicts(Operation *op, RewriterBase &rewriter,
896 const AnalysisState &analysisState,
897 const BufferizationState &bufferizationState)
const {
898 auto bufferizableOp = cast<BufferizableOpInterface>(op);
899 if (
failed(bufferizableOp.resolveTensorOpOperandConflicts(
900 rewriter, analysisState, bufferizationState)))
903 if (analysisState.getOptions().copyBeforeWrite)
913 OpBuilder::InsertionGuard g(rewriter);
914 auto whileOp = cast<scf::WhileOp>(op);
915 auto conditionOp = whileOp.getConditionOp();
920 whileOp.getBeforeArguments(), conditionOp.getArgs(), analysisState);
922 getEquivalentBuffers(whileOp.getAfterArguments(),
923 whileOp.getYieldOp().getResults(), analysisState);
927 SmallVector<Value> beforeYieldValues;
928 for (int64_t idx = 0;
929 idx < static_cast<int64_t>(conditionOp.getArgs().size()); ++idx) {
930 Value value = conditionOp.getArgs()[idx];
931 if (!isa<TensorLikeType>(value.
getType()) ||
932 (equivalentYieldsAfter.contains(idx) &&
933 equivalentYieldsBefore.contains(idx))) {
934 beforeYieldValues.push_back(value);
937 FailureOr<Value> alloc = allocateTensorForShapedValue(
938 rewriter, conditionOp.getLoc(), value, analysisState.getOptions(),
942 beforeYieldValues.push_back(*alloc);
945 conditionOp.getArgsMutable().assign(beforeYieldValues);
951 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
952 const BufferizationOptions &
options,
953 BufferizationState &state)
const {
954 auto whileOp = cast<scf::WhileOp>(op);
960 getTensorIndices(whileOp.getAfterArguments());
963 FailureOr<SmallVector<Value>> maybeInitArgs =
964 getBuffers(rewriter, whileOp.getInitsMutable(),
options, state);
965 if (
failed(maybeInitArgs))
967 SmallVector<Value> initArgs = *maybeInitArgs;
970 SmallVector<Value> castedInitArgs;
971 for (
const auto &it : llvm::enumerate(initArgs)) {
972 Value initArg = it.value();
973 Value beforeArg = whileOp.getBeforeArguments()[it.index()];
975 if (!isa<TensorLikeType>(beforeArg.
getType())) {
976 castedInitArgs.push_back(initArg);
979 auto targetType = bufferization::getBufferType(beforeArg,
options, state);
982 castedInitArgs.push_back(
983 castBuffer(rewriter, initArg, *targetType,
options));
987 SmallVector<Type> argsTypesAfter = llvm::map_to_vector(
988 whileOp.getAfterArguments(), [&](BlockArgument bbArg) {
989 if (!isa<TensorLikeType>(bbArg.getType()))
990 return bbArg.getType();
992 return llvm::cast<Type>(
993 *bufferization::getBufferType(bbArg, options, state));
998 TypeRange argsTypesBefore(argsRangeBefore);
999 auto newWhileOp = scf::WhileOp::create(rewriter, whileOp.getLoc(),
1000 argsTypesAfter, castedInitArgs);
1003 SmallVector<Location> bbArgLocsBefore(castedInitArgs.size(),
1005 SmallVector<Location> bbArgLocsAfter(argsTypesAfter.size(),
1007 Block *newBeforeBody = &newWhileOp.getBefore().emplaceBlock();
1008 newWhileOp.getBefore().addArguments(argsTypesBefore, bbArgLocsBefore);
1009 Block *newAfterBody = &newWhileOp.getAfter().emplaceBlock();
1010 newWhileOp.getAfter().addArguments(argsTypesAfter, bbArgLocsAfter);
1016 SmallVector<Value> newBeforeArgs =
1017 getBbArgReplacements(rewriter, newWhileOp.getBeforeArguments(),
1018 whileOp.getBeforeArguments(), indicesBefore);
1019 rewriter.
mergeBlocks(whileOp.getBeforeBody(), newBeforeBody, newBeforeArgs);
1025 SmallVector<Value> newAfterArgs =
1026 getBbArgReplacements(rewriter, newWhileOp.getAfterArguments(),
1027 whileOp.getAfterArguments(), indicesAfter);
1028 rewriter.
mergeBlocks(whileOp.getAfterBody(), newAfterBody, newAfterArgs);
1031 replaceOpWithBufferizedValues(rewriter, op, newWhileOp->getResults());
1036 FailureOr<BufferLikeType>
1038 const BufferizationState &state,
1039 SmallVector<Value> &invocationStack)
const {
1040 auto whileOp = cast<scf::WhileOp>(op);
1042 assert(isa<TensorLikeType>(value.
getType()) &&
"expected tensor type");
1045 if (
auto bbArg = dyn_cast<BlockArgument>(value)) {
1047 Value initArg = whileOp.getInits()[bbArg.
getArgNumber()];
1048 auto yieldOp = whileOp.getYieldOp();
1049 Value yieldedValue = yieldOp.getOperand(bbArg.
getArgNumber());
1050 return computeLoopRegionIterArgBufferType(
1051 op, bbArg, initArg, yieldedValue,
options, state, invocationStack);
1059 if (
auto opResult = dyn_cast<OpResult>(value)) {
1061 }
else if (cast<BlockArgument>(value).getOwner()->getParent() ==
1062 &whileOp.getAfter()) {
1063 resultNum = cast<BlockArgument>(value).getArgNumber();
1065 llvm_unreachable(
"invalid value");
1067 Value conditionYieldedVal = whileOp.getConditionOp().getArgs()[resultNum];
1068 if (!isa<TensorLikeType>(conditionYieldedVal.
getType())) {
1070 return cast<BufferLikeType>(conditionYieldedVal.
getType());
1072 return bufferization::getBufferType(conditionYieldedVal,
options, state,
1086 LogicalResult verifyAnalysis(Operation *op,
1087 const AnalysisState &state)
const {
1088 auto whileOp = cast<scf::WhileOp>(op);
1090 static_cast<const OneShotBufferizationOptions &
>(state.getOptions());
1091 if (
options.allowReturnAllocsFromLoops)
1094 auto conditionOp = whileOp.getConditionOp();
1095 for (
const auto &it : llvm::enumerate(conditionOp.getArgs())) {
1096 Block *block = conditionOp->getBlock();
1097 if (!isa<TensorLikeType>(it.value().getType()))
1100 !state.areEquivalentBufferizedValues(it.value(),
1102 return conditionOp->emitError()
1103 <<
"Condition arg #" << it.index()
1104 <<
" is not equivalent to the corresponding iter bbArg";
1107 auto yieldOp = whileOp.getYieldOp();
1108 for (
const auto &it : llvm::enumerate(yieldOp.getResults())) {
1109 Block *block = yieldOp->getBlock();
1110 if (!isa<TensorLikeType>(it.value().getType()))
1113 !state.areEquivalentBufferizedValues(it.value(),
1115 return yieldOp->emitError()
1116 <<
"Yield operand #" << it.index()
1117 <<
" is not equivalent to the corresponding iter bbArg";
1126struct YieldOpInterface
1127 :
public BufferizableOpInterface::ExternalModel<YieldOpInterface,
1129 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
1130 const AnalysisState &state)
const {
1134 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
1135 const AnalysisState &state)
const {
1139 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
1140 const AnalysisState &state)
const {
1141 if (
auto ifOp = dyn_cast<scf::IfOp>(op->
getParentOp())) {
1143 BufferRelation::Equivalent,
false}};
1147 BufferRelation::Equivalent}};
1151 bool mustBufferizeInPlace(Operation *op, OpOperand &opOperand,
1152 const AnalysisState &state)
const {
1159 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
1160 const BufferizationOptions &
options,
1161 BufferizationState &state)
const {
1162 auto yieldOp = cast<scf::YieldOp>(op);
1163 if (!isa<scf::ExecuteRegionOp, scf::IfOp, scf::IndexSwitchOp, scf::ForOp,
1164 scf::WhileOp>(yieldOp->getParentOp()))
1165 return yieldOp->emitError(
"unsupported scf::YieldOp parent");
1167 SmallVector<Value> newResults;
1168 for (
const auto &it : llvm::enumerate(yieldOp.getResults())) {
1169 Value value = it.value();
1170 if (isa<TensorLikeType>(value.
getType())) {
1171 FailureOr<Value> maybeBuffer =
1172 getBuffer(rewriter, value,
options, state);
1175 Value buffer = *maybeBuffer;
1177 if (isa<scf::ForOp, scf::IfOp, scf::IndexSwitchOp>(
1178 yieldOp->getParentOp())) {
1179 FailureOr<BufferLikeType> resultType = bufferization::getBufferType(
1180 yieldOp->getParentOp()->getResult(it.index()),
options, state);
1183 buffer = castBuffer(rewriter, buffer, *resultType,
options);
1184 }
else if (
auto whileOp =
1185 dyn_cast<scf::WhileOp>(yieldOp->getParentOp())) {
1186 FailureOr<BufferLikeType> resultType = bufferization::getBufferType(
1187 whileOp.getBeforeArguments()[it.index()],
options, state);
1190 buffer = castBuffer(rewriter, buffer, *resultType,
options);
1192 newResults.push_back(buffer);
1194 newResults.push_back(value);
1198 replaceOpWithNewBufferizedOp<scf::YieldOp>(rewriter, op, newResults);
1207struct ForallOpInterface
1208 :
public BufferizableOpInterface::ExternalModel<ForallOpInterface,
1210 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
1211 const AnalysisState &state)
const {
1219 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
1220 const AnalysisState &state)
const {
1225 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
1226 const AnalysisState &state)
const {
1227 auto forallOp = cast<ForallOp>(op);
1229 {{forallOp.getTiedOpResult(&opOperand), BufferRelation::Equivalent}}};
1232 bool isWritable(Operation *op, Value value,
1233 const AnalysisState &state)
const {
1237 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
1238 const BufferizationOptions &
options,
1239 BufferizationState &state)
const {
1240 OpBuilder::InsertionGuard guard(rewriter);
1241 auto forallOp = cast<ForallOp>(op);
1242 int64_t rank = forallOp.getRank();
1245 SmallVector<Value> buffers;
1246 for (Value out : forallOp.getOutputs()) {
1247 FailureOr<Value> buffer = getBuffer(rewriter, out,
options, state);
1250 buffers.push_back(*buffer);
1255 for (
const auto &it : llvm::zip(
1256 forallOp.getBody()->getArguments().drop_front(rank), buffers)) {
1257 BlockArgument bbArg = std::get<0>(it);
1258 Value buffer = std::get<1>(it);
1259 Value bufferAsTensor = ToTensorOp::create(rewriter, forallOp.getLoc(),
1267 ForallOp newForallOp;
1268 newForallOp = ForallOp::create(
1269 rewriter, forallOp.getLoc(), forallOp.getMixedLowerBound(),
1270 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1276 rewriter.
eraseOp(newForallOp.getBody()->getTerminator());
1279 SmallVector<Value> replacementBbArgs;
1280 replacementBbArgs.append(newForallOp.getBody()->getArguments().begin(),
1281 newForallOp.getBody()->getArguments().end());
1282 replacementBbArgs.append(forallOp.getOutputs().size(), Value());
1283 rewriter.
mergeBlocks(forallOp.getBody(), newForallOp.getBody(),
1287 replaceOpWithBufferizedValues(rewriter, op, buffers);
1292 FailureOr<BufferLikeType>
1294 const BufferizationState &state,
1295 SmallVector<Value> &invocationStack)
const {
1296 auto forallOp = cast<ForallOp>(op);
1298 if (
auto bbArg = dyn_cast<BlockArgument>(value))
1301 return bufferization::getBufferType(
1302 forallOp.getTiedOpOperand(bbArg)->get(),
options, state,
1307 return bufferization::getBufferType(
1308 forallOp.getOutputs()[cast<OpResult>(value).getResultNumber()],
options,
1309 state, invocationStack);
1313 auto forallOp = cast<ForallOp>(op);
1317 for (
auto [lb, ub, step] :
1318 llvm::zip(forallOp.getMixedLowerBound(), forallOp.getMixedUpperBound(),
1319 forallOp.getMixedStep())) {
1332 if (*lbConstant + *stepConstant < *ubConstant)
1338 bool isParallelRegion(Operation *op,
unsigned index)
const {
1344struct InParallelOpInterface
1345 :
public BufferizableOpInterface::ExternalModel<InParallelOpInterface,
1347 LogicalResult bufferize(Operation *op, RewriterBase &
b,
1348 const BufferizationOptions &
options,
1349 BufferizationState &state)
const {
1350 llvm_unreachable(
"op does not have any tensor OpOperands / OpResults");
1362 ConditionOp::attachInterface<ConditionOpInterface>(*ctx);
1363 ExecuteRegionOp::attachInterface<ExecuteRegionOpInterface>(*ctx);
1364 ForOp::attachInterface<ForOpInterface>(*ctx);
1365 IfOp::attachInterface<IfOpInterface>(*ctx);
1366 IndexSwitchOp::attachInterface<IndexSwitchOpInterface>(*ctx);
1367 ForallOp::attachInterface<ForallOpInterface>(*ctx);
1368 InParallelOp::attachInterface<InParallelOpInterface>(*ctx);
1369 WhileOp::attachInterface<WhileOpInterface>(*ctx);
1370 YieldOp::attachInterface<YieldOpInterface>(*ctx);
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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