1 //===- AffineExprVisitor.h - MLIR AffineExpr Visitor Class ------*- 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 // This file defines the AffineExpr visitor class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_IR_AFFINEEXPRVISITOR_H 14 #define MLIR_IR_AFFINEEXPRVISITOR_H 15 16 #include "mlir/IR/AffineExpr.h" 17 #include "llvm/ADT/ArrayRef.h" 18 19 namespace mlir { 20 21 /// Base class for AffineExpr visitors/walkers. 22 /// 23 /// AffineExpr visitors are used when you want to perform different actions 24 /// for different kinds of AffineExprs without having to use lots of casts 25 /// and a big switch instruction. 26 /// 27 /// To define your own visitor, inherit from this class, specifying your 28 /// new type for the 'SubClass' template parameter, and "override" visitXXX 29 /// functions in your class. This class is defined in terms of statically 30 /// resolved overloading, not virtual functions. 31 /// 32 /// For example, here is a visitor that counts the number of for AffineDimExprs 33 /// in an AffineExpr. 34 /// 35 /// /// Declare the class. Note that we derive from AffineExprVisitor 36 /// /// instantiated with our new subclasses_ type. 37 /// 38 /// struct DimExprCounter : public AffineExprVisitor<DimExprCounter> { 39 /// unsigned numDimExprs; 40 /// DimExprCounter() : numDimExprs(0) {} 41 /// void visitDimExpr(AffineDimExpr expr) { ++numDimExprs; } 42 /// }; 43 /// 44 /// And this class would be used like this: 45 /// DimExprCounter dec; 46 /// dec.visit(affineExpr); 47 /// numDimExprs = dec.numDimExprs; 48 /// 49 /// AffineExprVisitor provides visit methods for the following binary affine 50 /// op expressions: 51 /// AffineBinaryAddOpExpr, AffineBinaryMulOpExpr, 52 /// AffineBinaryModOpExpr, AffineBinaryFloorDivOpExpr, 53 /// AffineBinaryCeilDivOpExpr. Note that default implementations of these 54 /// methods will call the general AffineBinaryOpExpr method. 55 /// 56 /// In addition, visit methods are provided for the following affine 57 // expressions: AffineConstantExpr, AffineDimExpr, and 58 // AffineSymbolExpr. 59 /// 60 /// Note that if you don't implement visitXXX for some affine expression type, 61 /// the visitXXX method for Instruction superclass will be invoked. 62 /// 63 /// Note that this class is specifically designed as a template to avoid 64 /// virtual function call overhead. Defining and using a AffineExprVisitor is 65 /// just as efficient as having your own switch instruction over the instruction 66 /// opcode. 67 68 template <typename SubClass, typename RetTy = void> 69 class AffineExprVisitor { 70 //===--------------------------------------------------------------------===// 71 // Interface code - This is the public interface of the AffineExprVisitor 72 // that you use to visit affine expressions... 73 public: 74 // Function to walk an AffineExpr (in post order). walkPostOrder(AffineExpr expr)75 RetTy walkPostOrder(AffineExpr expr) { 76 static_assert(std::is_base_of<AffineExprVisitor, SubClass>::value, 77 "Must instantiate with a derived type of AffineExprVisitor"); 78 switch (expr.getKind()) { 79 case AffineExprKind::Add: { 80 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 81 walkOperandsPostOrder(binOpExpr); 82 return static_cast<SubClass *>(this)->visitAddExpr(binOpExpr); 83 } 84 case AffineExprKind::Mul: { 85 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 86 walkOperandsPostOrder(binOpExpr); 87 return static_cast<SubClass *>(this)->visitMulExpr(binOpExpr); 88 } 89 case AffineExprKind::Mod: { 90 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 91 walkOperandsPostOrder(binOpExpr); 92 return static_cast<SubClass *>(this)->visitModExpr(binOpExpr); 93 } 94 case AffineExprKind::FloorDiv: { 95 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 96 walkOperandsPostOrder(binOpExpr); 97 return static_cast<SubClass *>(this)->visitFloorDivExpr(binOpExpr); 98 } 99 case AffineExprKind::CeilDiv: { 100 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 101 walkOperandsPostOrder(binOpExpr); 102 return static_cast<SubClass *>(this)->visitCeilDivExpr(binOpExpr); 103 } 104 case AffineExprKind::Constant: 105 return static_cast<SubClass *>(this)->visitConstantExpr( 106 expr.cast<AffineConstantExpr>()); 107 case AffineExprKind::DimId: 108 return static_cast<SubClass *>(this)->visitDimExpr( 109 expr.cast<AffineDimExpr>()); 110 case AffineExprKind::SymbolId: 111 return static_cast<SubClass *>(this)->visitSymbolExpr( 112 expr.cast<AffineSymbolExpr>()); 113 } 114 } 115 116 // Function to visit an AffineExpr. visit(AffineExpr expr)117 RetTy visit(AffineExpr expr) { 118 static_assert(std::is_base_of<AffineExprVisitor, SubClass>::value, 119 "Must instantiate with a derived type of AffineExprVisitor"); 120 switch (expr.getKind()) { 121 case AffineExprKind::Add: { 122 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 123 return static_cast<SubClass *>(this)->visitAddExpr(binOpExpr); 124 } 125 case AffineExprKind::Mul: { 126 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 127 return static_cast<SubClass *>(this)->visitMulExpr(binOpExpr); 128 } 129 case AffineExprKind::Mod: { 130 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 131 return static_cast<SubClass *>(this)->visitModExpr(binOpExpr); 132 } 133 case AffineExprKind::FloorDiv: { 134 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 135 return static_cast<SubClass *>(this)->visitFloorDivExpr(binOpExpr); 136 } 137 case AffineExprKind::CeilDiv: { 138 auto binOpExpr = expr.cast<AffineBinaryOpExpr>(); 139 return static_cast<SubClass *>(this)->visitCeilDivExpr(binOpExpr); 140 } 141 case AffineExprKind::Constant: 142 return static_cast<SubClass *>(this)->visitConstantExpr( 143 expr.cast<AffineConstantExpr>()); 144 case AffineExprKind::DimId: 145 return static_cast<SubClass *>(this)->visitDimExpr( 146 expr.cast<AffineDimExpr>()); 147 case AffineExprKind::SymbolId: 148 return static_cast<SubClass *>(this)->visitSymbolExpr( 149 expr.cast<AffineSymbolExpr>()); 150 } 151 llvm_unreachable("Unknown AffineExpr"); 152 } 153 154 //===--------------------------------------------------------------------===// 155 // Visitation functions... these functions provide default fallbacks in case 156 // the user does not specify what to do for a particular instruction type. 157 // The default behavior is to generalize the instruction type to its subtype 158 // and try visiting the subtype. All of this should be inlined perfectly, 159 // because there are no virtual functions to get in the way. 160 // 161 162 // Default visit methods. Note that the default op-specific binary op visit 163 // methods call the general visitAffineBinaryOpExpr visit method. visitAffineBinaryOpExpr(AffineBinaryOpExpr expr)164 RetTy visitAffineBinaryOpExpr(AffineBinaryOpExpr expr) { return RetTy(); } visitAddExpr(AffineBinaryOpExpr expr)165 RetTy visitAddExpr(AffineBinaryOpExpr expr) { 166 return static_cast<SubClass *>(this)->visitAffineBinaryOpExpr(expr); 167 } visitMulExpr(AffineBinaryOpExpr expr)168 RetTy visitMulExpr(AffineBinaryOpExpr expr) { 169 return static_cast<SubClass *>(this)->visitAffineBinaryOpExpr(expr); 170 } visitModExpr(AffineBinaryOpExpr expr)171 RetTy visitModExpr(AffineBinaryOpExpr expr) { 172 return static_cast<SubClass *>(this)->visitAffineBinaryOpExpr(expr); 173 } visitFloorDivExpr(AffineBinaryOpExpr expr)174 RetTy visitFloorDivExpr(AffineBinaryOpExpr expr) { 175 return static_cast<SubClass *>(this)->visitAffineBinaryOpExpr(expr); 176 } visitCeilDivExpr(AffineBinaryOpExpr expr)177 RetTy visitCeilDivExpr(AffineBinaryOpExpr expr) { 178 return static_cast<SubClass *>(this)->visitAffineBinaryOpExpr(expr); 179 } visitConstantExpr(AffineConstantExpr expr)180 RetTy visitConstantExpr(AffineConstantExpr expr) { return RetTy(); } visitDimExpr(AffineDimExpr expr)181 RetTy visitDimExpr(AffineDimExpr expr) { return RetTy(); } visitSymbolExpr(AffineSymbolExpr expr)182 RetTy visitSymbolExpr(AffineSymbolExpr expr) { return RetTy(); } 183 184 private: 185 // Walk the operands - each operand is itself walked in post order. walkOperandsPostOrder(AffineBinaryOpExpr expr)186 RetTy walkOperandsPostOrder(AffineBinaryOpExpr expr) { 187 walkPostOrder(expr.getLHS()); 188 walkPostOrder(expr.getRHS()); 189 } 190 }; 191 192 // This class is used to flatten a pure affine expression (AffineExpr, 193 // which is in a tree form) into a sum of products (w.r.t constants) when 194 // possible, and in that process simplifying the expression. For a modulo, 195 // floordiv, or a ceildiv expression, an additional identifier, called a local 196 // identifier, is introduced to rewrite the expression as a sum of product 197 // affine expression. Each local identifier is always and by construction a 198 // floordiv of a pure add/mul affine function of dimensional, symbolic, and 199 // other local identifiers, in a non-mutually recursive way. Hence, every local 200 // identifier can ultimately always be recovered as an affine function of 201 // dimensional and symbolic identifiers (involving floordiv's); note however 202 // that by AffineExpr construction, some floordiv combinations are converted to 203 // mod's. The result of the flattening is a flattened expression and a set of 204 // constraints involving just the local variables. 205 // 206 // d2 + (d0 + d1) floordiv 4 is flattened to d2 + q where 'q' is the local 207 // variable introduced, with localVarCst containing 4*q <= d0 + d1 <= 4*q + 3. 208 // 209 // The simplification performed includes the accumulation of contributions for 210 // each dimensional and symbolic identifier together, the simplification of 211 // floordiv/ceildiv/mod expressions and other simplifications that in turn 212 // happen as a result. A simplification that this flattening naturally performs 213 // is of simplifying the numerator and denominator of floordiv/ceildiv, and 214 // folding a modulo expression to a zero, if possible. Three examples are below: 215 // 216 // (d0 + 3 * d1) + d0) - 2 * d1) - d0 simplified to d0 + d1 217 // (d0 - d0 mod 4 + 4) mod 4 simplified to 0 218 // (3*d0 + 2*d1 + d0) floordiv 2 + d1 simplified to 2*d0 + 2*d1 219 // 220 // The way the flattening works for the second example is as follows: d0 % 4 is 221 // replaced by d0 - 4*q with q being introduced: the expression then simplifies 222 // to: (d0 - (d0 - 4q) + 4) = 4q + 4, modulo of which w.r.t 4 simplifies to 223 // zero. Note that an affine expression may not always be expressible purely as 224 // a sum of products involving just the original dimensional and symbolic 225 // identifiers due to the presence of modulo/floordiv/ceildiv expressions that 226 // may not be eliminated after simplification; in such cases, the final 227 // expression can be reconstructed by replacing the local identifiers with their 228 // corresponding explicit form stored in 'localExprs' (note that each of the 229 // explicit forms itself would have been simplified). 230 // 231 // The expression walk method here performs a linear time post order walk that 232 // performs the above simplifications through visit methods, with partial 233 // results being stored in 'operandExprStack'. When a parent expr is visited, 234 // the flattened expressions corresponding to its two operands would already be 235 // on the stack - the parent expression looks at the two flattened expressions 236 // and combines the two. It pops off the operand expressions and pushes the 237 // combined result (although this is done in-place on its LHS operand expr). 238 // When the walk is completed, the flattened form of the top-level expression 239 // would be left on the stack. 240 // 241 // A flattener can be repeatedly used for multiple affine expressions that bind 242 // to the same operands, for example, for all result expressions of an 243 // AffineMap or AffineValueMap. In such cases, using it for multiple expressions 244 // is more efficient than creating a new flattener for each expression since 245 // common identical div and mod expressions appearing across different 246 // expressions are mapped to the same local identifier (same column position in 247 // 'localVarCst'). 248 class SimpleAffineExprFlattener 249 : public AffineExprVisitor<SimpleAffineExprFlattener> { 250 public: 251 // Flattend expression layout: [dims, symbols, locals, constant] 252 // Stack that holds the LHS and RHS operands while visiting a binary op expr. 253 // In future, consider adding a prepass to determine how big the SmallVector's 254 // will be, and linearize this to std::vector<int64_t> to prevent 255 // SmallVector moves on re-allocation. 256 std::vector<SmallVector<int64_t, 8>> operandExprStack; 257 258 unsigned numDims; 259 unsigned numSymbols; 260 261 // Number of newly introduced identifiers to flatten mod/floordiv/ceildiv's. 262 unsigned numLocals; 263 264 // AffineExpr's corresponding to the floordiv/ceildiv/mod expressions for 265 // which new identifiers were introduced; if the latter do not get canceled 266 // out, these expressions can be readily used to reconstruct the AffineExpr 267 // (tree) form. Note that these expressions themselves would have been 268 // simplified (recursively) by this pass. Eg. d0 + (d0 + 2*d1 + d0) ceildiv 4 269 // will be simplified to d0 + q, where q = (d0 + d1) ceildiv 2. (d0 + d1) 270 // ceildiv 2 would be the local expression stored for q. 271 SmallVector<AffineExpr, 4> localExprs; 272 273 SimpleAffineExprFlattener(unsigned numDims, unsigned numSymbols); 274 275 virtual ~SimpleAffineExprFlattener() = default; 276 277 // Visitor method overrides. 278 void visitMulExpr(AffineBinaryOpExpr expr); 279 void visitAddExpr(AffineBinaryOpExpr expr); 280 void visitDimExpr(AffineDimExpr expr); 281 void visitSymbolExpr(AffineSymbolExpr expr); 282 void visitConstantExpr(AffineConstantExpr expr); 283 void visitCeilDivExpr(AffineBinaryOpExpr expr); 284 void visitFloorDivExpr(AffineBinaryOpExpr expr); 285 286 // 287 // t = expr mod c <=> t = expr - c*q and c*q <= expr <= c*q + c - 1 288 // 289 // A mod expression "expr mod c" is thus flattened by introducing a new local 290 // variable q (= expr floordiv c), such that expr mod c is replaced with 291 // 'expr - c * q' and c * q <= expr <= c * q + c - 1 are added to localVarCst. 292 void visitModExpr(AffineBinaryOpExpr expr); 293 294 protected: 295 // Add a local identifier (needed to flatten a mod, floordiv, ceildiv expr). 296 // The local identifier added is always a floordiv of a pure add/mul affine 297 // function of other identifiers, coefficients of which are specified in 298 // dividend and with respect to a positive constant divisor. localExpr is the 299 // simplified tree expression (AffineExpr) corresponding to the quantifier. 300 virtual void addLocalFloorDivId(ArrayRef<int64_t> dividend, int64_t divisor, 301 AffineExpr localExpr); 302 303 /// Add a local identifier (needed to flatten a mod, floordiv, ceildiv, mul 304 /// expr) when the rhs is a symbolic expression. The local identifier added 305 /// may be a floordiv, ceildiv, mul or mod of a pure affine/semi-affine 306 /// function of other identifiers, coefficients of which are specified in the 307 /// lhs of the mod, floordiv, ceildiv or mul expression and with respect to a 308 /// symbolic rhs expression. `localExpr` is the simplified tree expression 309 /// (AffineExpr) corresponding to the quantifier. 310 virtual void addLocalIdSemiAffine(AffineExpr localExpr); 311 312 private: 313 /// Adds `expr`, which may be mod, ceildiv, floordiv or mod expression 314 /// representing the affine expression corresponding to the quantifier 315 /// introduced as the local variable corresponding to `expr`. If the 316 /// quantifier is already present, we put the coefficient in the proper index 317 /// of `result`, otherwise we add a new local variable and put the coefficient 318 /// there. 319 void addLocalVariableSemiAffine(AffineExpr expr, 320 SmallVectorImpl<int64_t> &result, 321 unsigned long resultSize); 322 323 // t = expr floordiv c <=> t = q, c * q <= expr <= c * q + c - 1 324 // A floordiv is thus flattened by introducing a new local variable q, and 325 // replacing that expression with 'q' while adding the constraints 326 // c * q <= expr <= c * q + c - 1 to localVarCst (done by 327 // FlatAffineConstraints::addLocalFloorDiv). 328 // 329 // A ceildiv is similarly flattened: 330 // t = expr ceildiv c <=> t = (expr + c - 1) floordiv c 331 void visitDivExpr(AffineBinaryOpExpr expr, bool isCeil); 332 333 int findLocalId(AffineExpr localExpr); 334 getNumCols()335 inline unsigned getNumCols() const { 336 return numDims + numSymbols + numLocals + 1; 337 } getConstantIndex()338 inline unsigned getConstantIndex() const { return getNumCols() - 1; } getLocalVarStartIndex()339 inline unsigned getLocalVarStartIndex() const { return numDims + numSymbols; } getSymbolStartIndex()340 inline unsigned getSymbolStartIndex() const { return numDims; } getDimStartIndex()341 inline unsigned getDimStartIndex() const { return 0; } 342 }; 343 344 } // namespace mlir 345 346 #endif // MLIR_IR_AFFINEEXPRVISITOR_H 347