1 //===- ComprehensiveBufferize.h - Linalg bufferization pass -----*- 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_LINALG_TRANSFORMS_COMPREHENSIVE_BUFFERIZE_H 10 #define MLIR_DIALECT_LINALG_TRANSFORMS_COMPREHENSIVE_BUFFERIZE_H 11 12 #include "mlir/Dialect/Tensor/IR/Tensor.h" 13 #include "mlir/IR/Value.h" 14 #include "llvm/ADT/EquivalenceClasses.h" 15 #include "llvm/ADT/SetOperations.h" 16 17 namespace mlir { 18 19 class DominanceInfo; 20 class FuncOp; 21 class GlobalCreator; 22 23 namespace linalg { 24 25 /// The BufferizationAliasInfo class maintains a list of buffer aliases and 26 /// equivalence classes to support bufferization. 27 /// ExtractSliceOps have special behavior, they act as a level of indirection 28 /// for bufferization. They don't create reads or writes themselves and analysis 29 /// needs to look through their uses. 30 /// ExtractSliceOp + InsertSliceOp have special joint behavior: they may 31 /// bufferize to the same buffer (i.e. subview), which is what introduces the 32 /// need for bufferization classes. 33 /// Some of these functionalities could be refactored in a Bufferizer class that 34 /// uses BufferizationAliasInfo. 35 class BufferizationAliasInfo { 36 public: 37 /// Specify fine-grain relationship between buffers to enable more analysis. 38 enum class BufferRelation { 39 None, 40 // TODO: ResultContainsOperand, 41 // TODO: OperandContainsResult, 42 Equivalent 43 }; 44 45 explicit BufferizationAliasInfo(Operation *rootOp); 46 47 /// Add a new entry for `v` in the `aliasInfo` and `equivalentInfo`. In the 48 /// beginning the alias and equivalence sets only contain `v` itself. 49 void createAliasInfoEntry(Value v); 50 51 /// Insert an info entry for `newValue` and merge its alias set with that of 52 /// `alias`. 53 void insertNewBufferAlias(Value newValue, Value alias); 54 55 /// Insert an info entry for `newValue` and merge its alias set with that of 56 /// `alias`. Additionally, merge their equivalence classes. 57 void insertNewBufferEquivalence(Value newValue, Value alias); 58 59 /// Return true if, under current bufferization decisions, the buffer of 60 /// `value` is not writable. 61 bool aliasesNonWritableBuffer(Value value) const; 62 63 /// Return true if the buffer to which `operand` would bufferize is equivalent 64 /// to some buffer write. 65 bool aliasesInPlaceWrite(Value v) const; 66 67 /// Set the inPlace bufferization spec to true. 68 /// Merge result's and operand's aliasing sets and iterate to a fixed point. 69 void bufferizeInPlace(OpResult result, OpOperand &operand); 70 71 /// Set the inPlace bufferization spec to false. 72 void bufferizeOutOfPlace(OpResult result); 73 74 /// Return true if it is possible to find an inplace write W among `usesWrite` 75 /// and a read R among `usesRead`, such that W and R interfere. 76 /// Such a (W, R) pair is an interference to the inplace bufferization of 77 /// opResult when: 78 /// 1. R is not known properly dominate W (i.e. the effects of the write may 79 /// be visible from R). 80 /// 2. one cannot find an intermediate clobbering write `C` to W, such that 81 /// C interleaved between W and R (i.e. W -> C -> R where -> denotes 82 /// dominance). 83 bool wouldCreateReadAfterWriteInterference( 84 Operation *opToBufferize, DenseSet<OpOperand *> &usesRead, 85 DenseSet<OpOperand *> &usesWrite, const DominanceInfo &domInfo) const; 86 87 /// Return true if bufferizing `opResult` inplace would create a write to a 88 /// non-writable buffer. 89 bool wouldCreateWriteToNonWritableBuffer(OpResult opResult) const; 90 91 /// Assume that result bufferizes in-place with one of the operation's 92 /// operands. Return true if it is possible to find an inplace write W (resp. 93 /// a read R) among the uses of `aliasInfo[result]`, and a read R (resp. an 94 /// inplace write W) among the uses of 95 /// `aliasInfo[getAliasingOpOperand(result)]`, such that W and R interfere. 96 /// Interference detection is needed to determine which cases may bufferize 97 /// inplace without interferences. Such cases comprise: 98 /// 99 /// ``` 100 /// %0 = op_to_bufferize(%1) 101 /// read(%1) 102 /// 103 /// %0 = op_to_bufferize(%1) 104 /// write(%0) 105 /// read(%1) 106 /// 107 /// %0 = op_to_bufferize(%1) 108 /// write(%1) 109 /// read(%0) 110 /// ``` 111 bool 112 wouldCreateReadAfterWriteInterference(OpResult result, 113 const DominanceInfo &domInfo) const; 114 115 /// Return true if `v1` and `v2` bufferize to equivalent buffers. areEquivalentBufferizedValues(Value v1,Value v2)116 bool areEquivalentBufferizedValues(Value v1, Value v2) const { 117 return equivalentInfo.getLeaderValue(v1) == 118 equivalentInfo.getLeaderValue(v2); 119 } 120 121 /// Return true if the source of an `insertSliceOp` bufferizes to an 122 /// equivalent ExtractSliceOp. 123 bool isSourceEquivalentToAMatchingInplaceExtractSliceOp( 124 tensor::InsertSliceOp insertSliceOp) const; 125 126 /// Apply `fun` to all the members of the equivalence class of `v`. 127 void applyOnEquivalenceClass(Value v, function_ref<void(Value)> fun) const; 128 129 /// Return true if the value is known to bufferize to writable memory. 130 bool bufferizesToWritableMemory(Value v) const; 131 132 /// Specify that the value is known to bufferize to writable memory. 133 void setBufferizesToWritableMemory(Value v); 134 135 /// Print to `os`. 136 void printAliases(raw_ostream &os) const; 137 void printEquivalences(raw_ostream &os) const; 138 139 /// Print to `errs()`. 140 void dumpAliases() const; 141 void dumpEquivalences() const; 142 143 private: 144 /// llvm::EquivalenceClasses wants comparable elements because it uses 145 /// std::set as the underlying impl. 146 /// ValueWrapper wraps Value and uses pointer comparison on the defining op. 147 /// This is a poor man's comparison but it's not like UnionFind needs ordering 148 /// anyway .. 149 struct ValueWrapper { ValueWrapperValueWrapper150 ValueWrapper(Value val) : v(val) {} ValueValueWrapper151 operator Value() const { return v; } 152 bool operator<(const ValueWrapper &wrap) const { 153 return v.getImpl() < wrap.v.getImpl(); 154 } 155 bool operator==(const ValueWrapper &wrap) const { return v == wrap.v; } 156 Value v; 157 }; 158 159 using EquivalenceClassRangeType = llvm::iterator_range< 160 llvm::EquivalenceClasses<ValueWrapper>::member_iterator>; 161 /// Check that aliasInfo for `v` exists and return a reference to it. 162 EquivalenceClassRangeType getAliases(Value v) const; 163 164 /// Return true if the (ExtractSliceOp, InsertSliceOp) pair match (i.e. 165 /// equivalent operand / result and same offset/sizes/strides specification). 166 /// 167 /// This is one particular type of relationship between ops on tensors that 168 /// reduce to an equivalence on buffers. This should be generalized and 169 /// exposed as interfaces on the proper types. 170 bool areEquivalentExtractSliceOps(tensor::ExtractSliceOp st, 171 tensor::InsertSliceOp sti) const; 172 173 /// Return true if there is a `candidateOp` that would write to memory after 174 /// bufferization and such that: 175 /// 1. The written buffer is equivalent to either `aliasingRead` or 176 /// `aliasingWrite` under the inPlace bufferization decisions taken 177 /// so far. 178 /// 2. `aliasingWrite` properly dominates `candidateOp`. 179 /// 3. `candidateOp` properly dominates `aliasingReadOp`. 180 // TODO: richer clobbering analysis with container-containee relationship 181 // instead of equivalence. 182 bool existsInterleavedValueClobber(OpOperand &aliasingRead, 183 OpOperand &aliasingWrite, 184 const DominanceInfo &domInfo) const; 185 186 /// Return true if there is a write that: 187 /// 1. Properly dominates aliasingReadOp. 188 /// 2. Is properly dominated by aliasingWriteOp. 189 /// 3. Clobbers the write that would be interfering with the read. 190 /// 191 /// Case discussion: 192 /// ================ 193 /// Case 1: opOperand is produced by opToBufferize, 194 /// Case 2: opResult is produced by opToBufferize, 195 /// Common case: 196 /// - aliasingReadOp is a read to an alias of opOperand. 197 /// - aliasingWriteOp is an inplace write to an alias of opResult. 198 /// - aliasingWriteOp dominates aliasingReadOp. 199 /// 200 /// ``` 201 /// // Either case 1: 202 /// %opOperand = opToBufferize(%opResult) 203 /// aliasingWriteOp(%aliasingWrite = alias(%opResult)) // inplace 204 /// aliasingReadOp( %aliasingRead = alias(%opOperand)) 205 /// ``` 206 /// 207 /// ``` 208 /// // Or case 2: 209 /// %opResult = opToBufferize(%opOperand) 210 /// aliasingWriteOp(%aliasingWrite = alias(%opResult)) // inplace 211 /// aliasingReadOp( %aliasingRead = alias(%opOperand)) 212 /// ``` 213 /// 214 /// Capture possible cases where `aliasingWriteOp(alias(%opResult))` has no 215 /// visible effect on `aliasingReadOp(alias(%opOperand))`. 216 bool isClobberedWriteBeforeRead(Operation *opToBufferize, 217 OpOperand &aliasingRead, 218 OpOperand &aliasingWrite, 219 const DominanceInfo &domInfo) const; 220 221 /// Set of tensors that are known to bufferize to writable memory. 222 llvm::DenseSet<Value> bufferizeToWritableMemory; 223 224 /// Auxiliary structure to store all the values a given value aliases with. 225 /// These are the conservative cases that can further decompose into 226 /// "equivalent" buffer relationships. 227 llvm::EquivalenceClasses<ValueWrapper> aliasInfo; 228 229 /// Auxiliary structure to store all the equivalent buffer classes. 230 llvm::EquivalenceClasses<ValueWrapper> equivalentInfo; 231 }; 232 233 /// Analyze the `ops` to determine which OpResults are inplaceable: 234 LogicalResult inPlaceAnalysis(SmallVector<Operation *> &ops, 235 BufferizationAliasInfo &aliasInfo, 236 const DominanceInfo &domInfo); 237 238 /// Bufferize one particular op. 239 /// `bufferizedFunctionTypes` (resp. `globalCreator`) are expected to be 240 /// non-null if `op` is a CallOpInterface (resp. GlobalCreator). 241 LogicalResult 242 bufferizeOp(Operation *op, BlockAndValueMapping &bvm, 243 BufferizationAliasInfo &aliasInfo, 244 DenseMap<FuncOp, FunctionType> *bufferizedFunctionTypes = nullptr, 245 GlobalCreator *globalCreator = nullptr); 246 247 } // namespace linalg 248 } // namespace mlir 249 250 #endif // define MLIR_DIALECT_LINALG_TRANSFORMS_COMPREHENSIVE_BUFFERIZE_H 251