MLIR 23.0.0git
UnstructuredControlFlow.h
Go to the documentation of this file.
1//===- UnstructuredControlFlow.h - Op Interface Helpers ---------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef MLIR_DIALECT_BUFFERIZATION_IR_UNSTRUCTUREDCONTROLFLOW_H_
10#define MLIR_DIALECT_BUFFERIZATION_IR_UNSTRUCTUREDCONTROLFLOW_H_
11
14
15//===----------------------------------------------------------------------===//
16// Helpers for Unstructured Control Flow
17//===----------------------------------------------------------------------===//
18
19namespace mlir {
20namespace bufferization {
21
22namespace detail {
23/// Return a list of operands that are forwarded to the given block argument.
24/// I.e., find all predecessors of the block argument's owner and gather the
25/// operands that are equivalent to the block argument.
27} // namespace detail
28
29/// A template that provides a default implementation of `getAliasingOpOperands`
30/// for ops that support unstructured control flow within their regions.
31template <typename ConcreteModel, typename ConcreteOp>
33 : public BufferizableOpInterface::ExternalModel<ConcreteModel, ConcreteOp> {
34
35 FailureOr<BufferLikeType>
37 const BufferizationState &state,
38 SmallVector<Value> &invocationStack) const {
39 // Note: The user may want to override this function for OpResults in
40 // case the bufferized result type is different from the bufferized type of
41 // the aliasing OpOperand (if any).
42 if (isa<OpResult>(value))
43 return bufferization::detail::defaultGetBufferType(value, options, state,
44 invocationStack);
45
46 // Compute the buffer type of the block argument by computing the bufferized
47 // operand types of all forwarded values. If these are all the same type,
48 // take that type. Otherwise, take only the memory space and fall back to a
49 // buffer type with a fully dynamic layout map.
50 BufferLikeType bufferType;
51 for (OpOperand *opOperand :
52 detail::getCallerOpOperands(cast<BlockArgument>(value))) {
53
54 // If the forwarded operand is already on the invocation stack, we ran
55 // into a loop and this operand cannot be used to compute the bufferized
56 // type.
57 if (llvm::is_contained(invocationStack, opOperand->get()))
58 continue;
59
60 // Compute the bufferized type of the forwarded operand.
61 BufferLikeType callerType;
62 if (auto bufferType =
63 dyn_cast<BufferLikeType>(opOperand->get().getType())) {
64 // The operand was already bufferized. Take its type directly.
65 callerType = bufferType;
66 } else {
67 FailureOr<BufferLikeType> maybeCallerType =
68 bufferization::getBufferType(opOperand->get(), options, state,
69 invocationStack);
70 if (failed(maybeCallerType))
71 return failure();
72 callerType = *maybeCallerType;
73 }
74
75 if (!bufferType) {
76 // This is the first buffer type that we computed.
77 bufferType = callerType;
78 continue;
79 }
80
81 if (bufferType == callerType)
82 continue;
83
84 // If the computed buffer type does not match the computed buffer type of
85 // the earlier forwarded operands, fall back to a reconciled buffer type.
86#ifndef NDEBUG
87 if (auto tensorType = dyn_cast<TensorLikeType>(value.getType())) {
88 const auto emitOpError = [&]() { return op->emitOpError(); };
89 assert(succeeded(tensorType.verifyCompatibleBufferType(bufferType,
90 emitOpError)) &&
91 "incompatible buffer type");
92 assert(succeeded(tensorType.verifyCompatibleBufferType(callerType,
93 emitOpError)) &&
94 "incompatible caller type");
95 }
96#endif // NDEBUG
97
98 auto reconciled = options.reconcileBufferTypeMismatchFn(
99 bufferType, callerType, options);
100 if (failed(reconciled)) {
101 return op->emitError("incoming operands of block argument have "
102 "incompatible buffer types");
103 }
104
105 bufferType = *reconciled;
106 }
107
108 if (!bufferType)
109 return op->emitOpError("could not infer buffer type of block argument");
110
111 return bufferType;
112 }
113
114protected:
115 /// Assuming that `bbArg` is a block argument of a block that belongs to the
116 /// given `op`, return all OpOperands of users of this block that are
117 /// aliasing with the given block argument.
118 AliasingOpOperandList
120 const AnalysisState &state) const {
121 assert(bbArg.getOwner()->getParentOp() == op && "invalid bbArg");
122
123 // Gather aliasing OpOperands of all operations (callers) that link to
124 // this block.
125 AliasingOpOperandList result;
126 for (OpOperand *opOperand : detail::getCallerOpOperands(bbArg))
127 result.addAlias(
128 {opOperand, BufferRelation::Equivalent, /*isDefinite=*/false});
129
130 return result;
131 }
132};
133
134/// A template that provides a default implementation of `getAliasingValues`
135/// for ops that implement the `BranchOpInterface`.
136template <typename ConcreteModel, typename ConcreteOp>
138 : public BufferizableOpInterface::ExternalModel<ConcreteModel, ConcreteOp> {
139 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
140 const AnalysisState &state) const {
141 AliasingValueList result;
142 auto branchOp = cast<BranchOpInterface>(op);
143 auto operandNumber = opOperand.getOperandNumber();
144
145 // Gather aliasing block arguments of blocks to which this op may branch to.
146 for (const auto &it : llvm::enumerate(op->getSuccessors())) {
147 Block *block = it.value();
148 SuccessorOperands operands = branchOp.getSuccessorOperands(it.index());
149 assert(operands.getProducedOperandCount() == 0 &&
150 "produced operands not supported");
151 if (operands.getForwardedOperands().empty())
152 continue;
153 // The first and last operands that are forwarded to this successor.
154 int64_t firstOperandIndex =
156 int64_t lastOperandIndex =
157 firstOperandIndex + operands.getForwardedOperands().size();
158 bool matchingDestination = operandNumber >= firstOperandIndex &&
159 operandNumber < lastOperandIndex;
160 // A branch op may have multiple successors. Find the ones that correspond
161 // to this OpOperand. (There is usually only one.)
162 if (!matchingDestination)
163 continue;
164 // Compute the matching block argument of the destination block.
165 BlockArgument bbArg =
166 block->getArgument(operandNumber - firstOperandIndex);
167 result.addAlias(
168 {bbArg, BufferRelation::Equivalent, /*isDefinite=*/false});
169 }
170
171 return result;
172 }
173};
174
175} // namespace bufferization
176} // namespace mlir
177
178#endif // MLIR_DIALECT_BUFFERIZATION_IR_UNSTRUCTUREDCONTROLFLOW_H_
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 llvm::ManagedStatic< PassManagerOptions > options
Base class for generic analysis states.
This class represents an argument of a Block.
Definition Value.h:306
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:315
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:153
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
SuccessorRange getSuccessors()
Definition Operation.h:728
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This class models how operands are forwarded to block arguments in control flow.
unsigned getProducedOperandCount() const
Returns the amount of operands that are produced internally by the operation.
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
SmallVector< OpOperand * > getCallerOpOperands(BlockArgument bbArg)
Return a list of operands that are forwarded to the given block argument.
Include the generated interface declarations.
A template that provides a default implementation of getAliasingValues for ops that implement the Bra...
AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
A template that provides a default implementation of getAliasingOpOperands for ops that support unstr...
AliasingOpOperandList getAliasingBranchOpOperands(Operation *op, BlockArgument bbArg, const AnalysisState &state) const
Assuming that bbArg is a block argument of a block that belongs to the given op, return all OpOperand...
FailureOr< BufferLikeType > getBufferType(Operation *op, Value value, const BufferizationOptions &options, const BufferizationState &state, SmallVector< Value > &invocationStack) const