1 //===- LinalgOps.cpp - Implementation of the linalg operations ------------===// 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 implements the Linalg operations. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "mlir/Dialect/Linalg/IR/Linalg.h" 14 15 #include "mlir/Dialect/Arithmetic/Utils/Utils.h" 16 #include "mlir/Dialect/SCF/SCF.h" 17 #include "mlir/Dialect/SparseTensor/IR/SparseTensor.h" 18 #include "mlir/Dialect/Utils/ReshapeOpsUtils.h" 19 #include "mlir/Dialect/Utils/StaticValueUtils.h" 20 #include "mlir/IR/AffineExprVisitor.h" 21 #include "mlir/IR/Matchers.h" 22 #include "mlir/IR/OpImplementation.h" 23 #include "mlir/IR/PatternMatch.h" 24 #include "mlir/Interfaces/InferTypeOpInterface.h" 25 #include "mlir/Parser/Parser.h" 26 27 #include "llvm/ADT/DenseMap.h" 28 #include "llvm/ADT/SetVector.h" 29 #include "llvm/ADT/SmallSet.h" 30 #include "llvm/ADT/StringSet.h" 31 #include "llvm/ADT/TypeSwitch.h" 32 #include "llvm/Support/FormatVariadic.h" 33 #include "llvm/Support/MathExtras.h" 34 #include "llvm/Support/raw_ostream.h" 35 36 using namespace mlir; 37 using namespace mlir::linalg; 38 39 /// Forward declarations. 40 41 /// Generic entry point to create the block for the region of a LinalgOp. 42 /// This is used by both named structured ops created by ods-gen and by manually 43 /// defined C++ ops. 44 /// This is used by both builders and parsers. 45 /// This function creates the block in the region with arguments corresponding 46 /// to the elemental types of `inputTypes` and `outputTypes`. The latter are 47 /// asserted to be of ShapedType. 48 template <typename NamedStructuredOpType> 49 static void fillStructuredOpRegion( 50 OpBuilder &opBuilder, Region ®ion, TypeRange inputTypes, 51 TypeRange outputTypes, ArrayRef<NamedAttribute> attrs, 52 llvm::function_ref<void(unsigned, unsigned)> errorHandler = nullptr); 53 54 /// Generic entry point to create both the region and the block of a LinalgOp. 55 template <typename NamedStructuredOpType> 56 static void 57 createAndFillStructuredOpRegion(OpBuilder &opBuilder, OperationState &result, 58 TypeRange inputTypes, TypeRange outputTypes); 59 60 /// Common parsing and printing used for both named structured ops created by 61 /// ods-gen and by manually defined C++ ops. Does not handle regions. 62 static ParseResult 63 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result, 64 SmallVectorImpl<Type> &inputTypes, 65 SmallVectorImpl<Type> &outputTypes); 66 template <typename NamedStructuredOpType> 67 static void printCommonStructuredOpParts(OpAsmPrinter &p, 68 NamedStructuredOpType op); 69 70 /// Specific parsing and printing for named structured ops created by ods-gen. 71 template <typename NamedStructuredOpType> 72 static ParseResult 73 parseNamedStructuredOpRegion(OpAsmParser &parser, Region ®ion, 74 TypeRange inputTypes, TypeRange outputTypes, 75 ArrayRef<NamedAttribute> attrs); 76 77 static ParseResult 78 parseNamedStructuredOpResults(OpAsmParser &parser, 79 SmallVectorImpl<Type> &resultTypes); 80 81 template <typename NamedStructuredOpType> 82 static ParseResult parseNamedStructuredOp(OpAsmParser &parser, 83 OperationState &result); 84 85 static void printNamedStructuredOpResults(OpAsmPrinter &p, 86 TypeRange resultTypes); 87 88 template <typename NamedStructuredOpType> 89 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op); 90 91 /// This is a common class used for patterns of the form 92 /// ``` 93 /// someop(memrefcast(%src)) -> someop(%src) 94 /// ``` 95 /// It folds the source of the memref.cast into the root operation directly. 96 static LogicalResult foldMemRefCast(Operation *op) { 97 bool folded = false; 98 for (OpOperand &operand : op->getOpOperands()) { 99 auto castOp = operand.get().getDefiningOp<memref::CastOp>(); 100 if (castOp && memref::CastOp::canFoldIntoConsumerOp(castOp)) { 101 operand.set(castOp.getOperand()); 102 folded = true; 103 } 104 } 105 return success(folded); 106 } 107 108 //===----------------------------------------------------------------------===// 109 // Region builder helper. 110 // TODO: Move this to a utility library. 111 // The public methods on this class are referenced directly from generated code. 112 // Helper build the unary, binary, and type conversion functions defined by the 113 // DSL. See mlir-linalg-ods-yaml-gen.cpp for the code that uses this class. 114 // 115 // Implementations of the math functions must be polymorphic over numeric types, 116 // internally performing necessary casts. If the function application makes no 117 // sense, then the only recourse is to assert and return nullptr. This can be 118 // extended later if it becomes possible to fail construction of the region. The 119 // invariant should be enforced at a higher level. 120 // 121 // TODO: These helpers are currently type polymorphic over the class of integer 122 // and floating point types, but they will not internally cast within bit 123 // widths of a class (mixed precision such as i8->i32) or across classes 124 // (i.e. mixed float and integer). Many such combinations are ambiguous or need 125 // to be handled with care and work is being considered to extend the op 126 // language to make such cases explicit. In the mean-time, violating this will 127 // fail verification, which is deemed acceptable. 128 //===----------------------------------------------------------------------===// 129 130 namespace { 131 132 class RegionBuilderHelper { 133 public: 134 RegionBuilderHelper(MLIRContext *context, Block &block) 135 : context(context), block(block) {} 136 137 // Build the unary functions defined by OpDSL. 138 Value buildUnaryFn(UnaryFn unaryFn, Value arg) { 139 if (!isFloatingPoint(arg)) 140 llvm_unreachable("unsupported non numeric type"); 141 OpBuilder builder = getBuilder(); 142 switch (unaryFn) { 143 case UnaryFn::exp: 144 return builder.create<math::ExpOp>(arg.getLoc(), arg); 145 case UnaryFn::log: 146 return builder.create<math::LogOp>(arg.getLoc(), arg); 147 } 148 llvm_unreachable("unsupported unary function"); 149 } 150 151 // Build the binary functions defined by OpDSL. 152 Value buildBinaryFn(BinaryFn binaryFn, Value arg0, Value arg1) { 153 bool allFloatingPoint = isFloatingPoint(arg0) && isFloatingPoint(arg1); 154 bool allInteger = isInteger(arg0) && isInteger(arg1); 155 if (!allFloatingPoint && !allInteger) 156 llvm_unreachable("unsupported non numeric type"); 157 OpBuilder builder = getBuilder(); 158 switch (binaryFn) { 159 case BinaryFn::add: 160 if (allFloatingPoint) 161 return builder.create<arith::AddFOp>(arg0.getLoc(), arg0, arg1); 162 return builder.create<arith::AddIOp>(arg0.getLoc(), arg0, arg1); 163 case BinaryFn::sub: 164 if (allFloatingPoint) 165 return builder.create<arith::SubFOp>(arg0.getLoc(), arg0, arg1); 166 return builder.create<arith::SubIOp>(arg0.getLoc(), arg0, arg1); 167 case BinaryFn::mul: 168 if (allFloatingPoint) 169 return builder.create<arith::MulFOp>(arg0.getLoc(), arg0, arg1); 170 return builder.create<arith::MulIOp>(arg0.getLoc(), arg0, arg1); 171 case BinaryFn::max_signed: 172 if (allFloatingPoint) 173 return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1); 174 return builder.create<arith::MaxSIOp>(arg0.getLoc(), arg0, arg1); 175 case BinaryFn::min_signed: 176 if (allFloatingPoint) 177 return builder.create<arith::MinFOp>(arg0.getLoc(), arg0, arg1); 178 return builder.create<arith::MinSIOp>(arg0.getLoc(), arg0, arg1); 179 case BinaryFn::max_unsigned: 180 if (allFloatingPoint) 181 return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1); 182 return builder.create<arith::MaxUIOp>(arg0.getLoc(), arg0, arg1); 183 case BinaryFn::min_unsigned: 184 if (allFloatingPoint) 185 return builder.create<arith::MinFOp>(arg0.getLoc(), arg0, arg1); 186 return builder.create<arith::MinUIOp>(arg0.getLoc(), arg0, arg1); 187 } 188 llvm_unreachable("unsupported binary function"); 189 } 190 191 // Build the type functions defined by OpDSL. 192 Value buildTypeFn(TypeFn typeFn, Type toType, Value operand) { 193 switch (typeFn) { 194 case TypeFn::cast_signed: 195 return cast(toType, operand, false); 196 case TypeFn::cast_unsigned: 197 return cast(toType, operand, true); 198 } 199 llvm_unreachable("unsupported type conversion function"); 200 } 201 202 void yieldOutputs(ValueRange values) { 203 OpBuilder builder = getBuilder(); 204 Location loc = builder.getUnknownLoc(); 205 builder.create<YieldOp>(loc, values); 206 } 207 208 Value constant(const std::string &value) { 209 OpBuilder builder = getBuilder(); 210 Location loc = builder.getUnknownLoc(); 211 Attribute valueAttr = parseAttribute(value, builder.getContext()); 212 return builder.create<arith::ConstantOp>(loc, valueAttr.getType(), 213 valueAttr); 214 } 215 216 Value index(int64_t dim) { 217 OpBuilder builder = getBuilder(); 218 return builder.create<IndexOp>(builder.getUnknownLoc(), dim); 219 } 220 221 Type getIntegerType(unsigned width) { 222 return IntegerType::get(context, width); 223 } 224 225 Type getFloat32Type() { return Float32Type::get(context); } 226 Type getFloat64Type() { return Float64Type::get(context); } 227 228 private: 229 // Generates operations to cast the given operand to a specified type. 230 // If the cast cannot be performed, a warning will be issued and the 231 // operand returned as-is (which will presumably yield a verification 232 // issue downstream). 233 Value cast(Type toType, Value operand, bool isUnsignedCast) { 234 OpBuilder builder = getBuilder(); 235 auto loc = operand.getLoc(); 236 237 if (operand.getType() == toType) 238 return operand; 239 if (auto toIntType = toType.dyn_cast<IntegerType>()) { 240 // If operand is floating point, cast directly to the int type. 241 if (operand.getType().isa<FloatType>()) { 242 if (isUnsignedCast) 243 return builder.create<arith::FPToUIOp>(loc, toType, operand); 244 return builder.create<arith::FPToSIOp>(loc, toType, operand); 245 } 246 // Cast index operands directly to the int type. 247 if (operand.getType().isIndex()) 248 return builder.create<arith::IndexCastOp>(loc, toType, operand); 249 if (auto fromIntType = operand.getType().dyn_cast<IntegerType>()) { 250 // Either extend or truncate. 251 if (toIntType.getWidth() > fromIntType.getWidth()) { 252 if (isUnsignedCast) 253 return builder.create<arith::ExtUIOp>(loc, toType, operand); 254 return builder.create<arith::ExtSIOp>(loc, toType, operand); 255 } 256 if (toIntType.getWidth() < fromIntType.getWidth()) 257 return builder.create<arith::TruncIOp>(loc, toType, operand); 258 } 259 } else if (auto toFloatType = toType.dyn_cast<FloatType>()) { 260 // If operand is integer, cast directly to the float type. 261 // Note that it is unclear how to cast from BF16<->FP16. 262 if (operand.getType().isa<IntegerType>()) { 263 if (isUnsignedCast) 264 return builder.create<arith::UIToFPOp>(loc, toFloatType, operand); 265 return builder.create<arith::SIToFPOp>(loc, toFloatType, operand); 266 } 267 if (auto fromFloatType = operand.getType().dyn_cast<FloatType>()) { 268 if (toFloatType.getWidth() > fromFloatType.getWidth()) 269 return builder.create<arith::ExtFOp>(loc, toFloatType, operand); 270 if (toFloatType.getWidth() < fromFloatType.getWidth()) 271 return builder.create<arith::TruncFOp>(loc, toFloatType, operand); 272 } 273 } 274 275 emitWarning(operand.getLoc()) << "could not cast operand of type " 276 << operand.getType() << " to " << toType; 277 return operand; 278 } 279 280 bool isFloatingPoint(Value value) { return value.getType().isa<FloatType>(); } 281 bool isInteger(Value value) { return value.getType().isa<IntegerType>(); } 282 283 OpBuilder getBuilder() { 284 OpBuilder builder(context); 285 builder.setInsertionPointToEnd(&block); 286 return builder; 287 } 288 289 MLIRContext *context; 290 Block █ 291 }; 292 293 } // namespace 294 295 //===----------------------------------------------------------------------===// 296 // FillOp 297 //===----------------------------------------------------------------------===// 298 void FillOp::regionBuilder(ImplicitLocOpBuilder &b, Block &block, 299 ArrayRef<NamedAttribute> attrs) { 300 assert(block.getNumArguments() == 2 && "FillOp regionBuilder expects 2 args"); 301 b.create<linalg::YieldOp>(block.getArgument(0)); 302 } 303 304 void FillOp::build(OpBuilder &builder, OperationState &result, Value value, 305 Value output) { 306 build(builder, result, output.getType().dyn_cast<RankedTensorType>(), value, 307 output); 308 fillStructuredOpRegion<FillOp>( 309 builder, *result.regions.front(), TypeRange{value.getType()}, 310 TypeRange{output.getType()}, result.attributes.getAttrs(), {}); 311 } 312 313 ParseResult parseFillOpRegion(OpAsmParser &parser, Region &r, Type valueType, 314 Type outputType) { 315 OpBuilder opBuilder(parser.getContext()); 316 fillStructuredOpRegion<FillOp>(opBuilder, r, TypeRange{valueType}, 317 TypeRange{outputType}, {}); 318 return success(); 319 } 320 321 /// FillOp region is elided when printing. 322 void printFillOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Type) {} 323 324 LogicalResult FillOp::verify() { 325 OpOperand *output = getOutputOperand(0); 326 Type fillType = value().getType(); 327 if (getElementTypeOrSelf(output->get()) != fillType) 328 return emitOpError("expects fill type to match view elemental type"); 329 return success(); 330 } 331 332 void FillOp::getEffects( 333 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>> 334 &effects) { 335 if (output().getType().isa<MemRefType>()) 336 effects.emplace_back(MemoryEffects::Write::get(), output(), 337 SideEffects::DefaultResource::get()); 338 } 339 340 namespace { 341 342 /// Fold linalg.fill -> tensor.expand/collapse_shape chain. 343 /// 344 /// For such op chains, we can create new linalg.fill ops with the result 345 /// type of the tensor.expand/collapse_shape op. 346 template <typename TensorReshapeOp> 347 struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> { 348 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern; 349 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp, 350 PatternRewriter &rewriter) const override { 351 auto oldFill = reshapeOp.src().template getDefiningOp<FillOp>(); 352 if (!oldFill) 353 return failure(); 354 355 Location loc = oldFill.getLoc(); 356 auto newInit = rewriter.create<TensorReshapeOp>( 357 loc, reshapeOp.getResultType(), oldFill.output(), 358 reshapeOp.reassociation()); 359 rewriter.replaceOpWithNewOp<FillOp>(reshapeOp, oldFill.value(), newInit); 360 361 return success(); 362 } 363 }; 364 365 /// Fold tensor.pad(linalg.fill) into linalg.fill if the padding value and the 366 /// filling value are the same. 367 struct FoldFillWithPad final : public OpRewritePattern<tensor::PadOp> { 368 using OpRewritePattern::OpRewritePattern; 369 370 LogicalResult matchAndRewrite(tensor::PadOp padOp, 371 PatternRewriter &rewriter) const override { 372 auto fillOp = padOp.source().getDefiningOp<linalg::FillOp>(); 373 if (!fillOp) 374 return failure(); 375 376 // We can only fold if the padding value is the same as the original 377 // filling value. 378 Value padValue = padOp.getConstantPaddingValue(); 379 if (!padValue || fillOp.value() != padValue) 380 return failure(); 381 382 ReifiedRankedShapedTypeDims reifiedShape; 383 ReifyRankedShapedTypeOpInterface interface = 384 cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation()); 385 if (failed(interface.reifyResultShapes(rewriter, reifiedShape))) 386 return rewriter.notifyMatchFailure( 387 padOp, "failed to reify tensor.pad op result shape"); 388 389 auto oldResultType = padOp.getResultType(); 390 SmallVector<int64_t, 4> staticShape(oldResultType.getRank(), 391 ShapedType::kDynamicSize); 392 auto newInitOp = rewriter.create<InitTensorOp>( 393 padOp.getLoc(), reifiedShape.front(), staticShape, 394 oldResultType.getElementType()); 395 auto newFillOp = 396 rewriter.create<FillOp>(fillOp.getLoc(), padValue, newInitOp); 397 rewriter.replaceOpWithNewOp<tensor::CastOp>(padOp, oldResultType, 398 newFillOp.result()); 399 400 return success(); 401 } 402 }; 403 404 /// Fold tensor.insert_slice(tensor.pad(<input>), linalg.fill) into 405 /// tensor.insert_slice(<input>, linalg.fill) if the padding value and the 406 /// filling value are the same. 407 struct FoldInsertPadIntoFill : public OpRewritePattern<tensor::InsertSliceOp> { 408 using OpRewritePattern::OpRewritePattern; 409 410 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp, 411 PatternRewriter &rewriter) const override { 412 auto srcPadOp = insertOp.source().getDefiningOp<tensor::PadOp>(); 413 if (!srcPadOp) 414 return failure(); 415 416 if (insertOp.getType().getRank() != insertOp.getSourceType().getRank()) 417 return failure(); 418 419 // Walk back the tensor.insert_slice chain and find the first destination 420 // value at the start of the chain. 421 Value firstDest = insertOp.dest(); 422 while (auto prevOp = firstDest.getDefiningOp<tensor::InsertSliceOp>()) { 423 if (prevOp.getType().getRank() != prevOp.getSourceType().getRank()) 424 return failure(); 425 426 // Make sure the range of values accessed are disjoint. Without this, we 427 // cannot fold tensor.pad away. 428 bool disjoint = false; 429 for (int i = 0, e = prevOp.getType().getRank(); i < e; ++i) { 430 // If the dimension has dynamic offset/size, we cannot guarantee 431 // disjoint. So just skip it. 432 if (insertOp.isDynamicOffset(i) || insertOp.isDynamicSize(i) || 433 insertOp.isDynamicStride(i) || prevOp.isDynamicOffset(i) || 434 prevOp.isDynamicSize(i) || prevOp.isDynamicStride(i)) 435 continue; 436 437 // Get the range start and end, inclusively for both. 438 int64_t prevStart = prevOp.getStaticOffset(i); 439 int64_t prevEnd = prevStart + (prevOp.getStaticSize(i) - 1) * 440 prevOp.getStaticStride(i); 441 int64_t nextStart = insertOp.getStaticOffset(i); 442 int64_t nextEnd = nextStart + (insertOp.getStaticSize(i) - 1) * 443 insertOp.getStaticStride(i); 444 if (prevEnd < nextStart || nextEnd < prevStart) { 445 disjoint = true; 446 break; 447 } 448 } 449 450 if (!disjoint) 451 break; 452 firstDest = prevOp.dest(); 453 } 454 455 // Check whether the first destination is a fill op. For overlapped cases, 456 // this also cannot be true. 457 auto dstFillOp = firstDest.getDefiningOp<linalg::FillOp>(); 458 if (!dstFillOp) 459 return failure(); 460 461 // We can only fold if the padding value is the same as the original 462 // filling value. 463 Value padValue = srcPadOp.getConstantPaddingValue(); 464 if (!padValue || dstFillOp.value() != padValue) 465 return failure(); 466 467 SmallVector<OpFoldResult> lowPads = srcPadOp.getMixedLowPad(); 468 SmallVector<OpFoldResult> oldOffsets = insertOp.getMixedOffsets(); 469 470 Location loc = insertOp.getLoc(); 471 MLIRContext *context = getContext(); 472 473 AffineExpr sym0, sym1; 474 bindSymbols(context, sym0, sym1); 475 auto addMap = AffineMap::get(0, 2, {sym0 + sym1}, context); 476 477 // Calculate the new offsets for the insert. It should be the old offsets 478 // plus low padding sizes. 479 SmallVector<OpFoldResult, 4> newOffsets; 480 for (const auto &p : llvm::zip(lowPads, oldOffsets)) { 481 Value padValue = getValueOrCreateConstantIndexOp( 482 rewriter, srcPadOp.getLoc(), std::get<0>(p)); 483 Value offsetValue = getValueOrCreateConstantIndexOp( 484 rewriter, insertOp.getLoc(), std::get<1>(p)); 485 newOffsets.push_back( 486 applyMapToValues(rewriter, loc, addMap, {offsetValue, padValue})[0]); 487 } 488 489 SmallVector<OpFoldResult, 4> newSizes; 490 for (int i = 0, e = srcPadOp.getSourceType().getRank(); i < e; ++i) { 491 newSizes.push_back( 492 rewriter.create<tensor::DimOp>(loc, srcPadOp.source(), i).result()); 493 } 494 495 rewriter.replaceOpWithNewOp<tensor::InsertSliceOp>( 496 insertOp, srcPadOp.source(), insertOp.dest(), newOffsets, newSizes, 497 insertOp.getMixedStrides()); 498 return success(); 499 } 500 }; 501 502 } // namespace 503 504 void FillOp::getCanonicalizationPatterns(RewritePatternSet &results, 505 MLIRContext *context) { 506 results 507 .add<FoldFillWithPad, FoldFillWithTensorReshape<tensor::CollapseShapeOp>, 508 FoldFillWithTensorReshape<tensor::ExpandShapeOp>, 509 FoldInsertPadIntoFill>(context); 510 } 511 512 //===----------------------------------------------------------------------===// 513 // GenericOps 514 //===----------------------------------------------------------------------===// 515 void GenericOp::build( 516 OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes, 517 ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps, 518 ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall, 519 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild, 520 ArrayRef<NamedAttribute> attributes) { 521 build(builder, result, resultTensorTypes, inputs, outputs, 522 builder.getAffineMapArrayAttr(indexingMaps), 523 builder.getStrArrayAttr(iteratorTypes), 524 doc.empty() ? StringAttr() : builder.getStringAttr(doc), 525 libraryCall.empty() ? StringAttr() 526 : builder.getStringAttr(libraryCall)); 527 result.addAttributes(attributes); 528 if (!bodyBuild) 529 return; 530 531 SmallVector<Type, 4> blockArgTypes; 532 SmallVector<Location, 4> blockArgLocs; 533 for (ValueRange container : {inputs, outputs}) { 534 for (Value v : container) { 535 blockArgTypes.push_back(getElementTypeOrSelf(v)); 536 blockArgLocs.push_back(v.getLoc()); 537 } 538 } 539 540 OpBuilder::InsertionGuard guard(builder); 541 auto ®ion = *result.regions.front(); 542 Block *bodyBlock = 543 builder.createBlock(®ion, region.end(), blockArgTypes, blockArgLocs); 544 bodyBuild(builder, result.location, bodyBlock->getArguments()); 545 } 546 547 void GenericOp::build( 548 OpBuilder &builder, OperationState &result, ValueRange inputs, 549 ValueRange outputs, ArrayRef<AffineMap> indexingMaps, 550 ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall, 551 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild, 552 ArrayRef<NamedAttribute> attributes) { 553 build(builder, result, TypeRange{}, inputs, outputs, indexingMaps, 554 iteratorTypes, doc, libraryCall, bodyBuild, attributes); 555 } 556 557 void GenericOp::build( 558 OpBuilder &builder, OperationState &result, ValueRange inputs, 559 ValueRange outputs, ArrayRef<AffineMap> indexingMaps, 560 ArrayRef<StringRef> iteratorTypes, 561 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild, 562 ArrayRef<NamedAttribute> attributes) { 563 build(builder, result, inputs, outputs, indexingMaps, iteratorTypes, 564 /*doc=*/"", 565 /*libraryCall=*/"", bodyBuild, attributes); 566 } 567 568 void GenericOp::build( 569 OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes, 570 ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps, 571 ArrayRef<StringRef> iteratorTypes, 572 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild, 573 ArrayRef<NamedAttribute> attributes) { 574 build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps, 575 iteratorTypes, 576 /*doc=*/"", 577 /*libraryCall=*/"", bodyBuild, attributes); 578 } 579 580 void GenericOp::print(OpAsmPrinter &p) { 581 p << " "; 582 583 // Print extra attributes. 584 auto genericAttrNames = linalgTraitAttrNames(); 585 586 llvm::StringSet<> genericAttrNamesSet; 587 genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end()); 588 SmallVector<NamedAttribute, 8> genericAttrs; 589 for (auto attr : (*this)->getAttrs()) 590 if (genericAttrNamesSet.count(attr.getName().strref()) > 0) 591 genericAttrs.push_back(attr); 592 if (!genericAttrs.empty()) { 593 auto genericDictAttr = DictionaryAttr::get(getContext(), genericAttrs); 594 p << genericDictAttr; 595 } 596 597 // Printing is shared with named ops, except for the region and attributes 598 printCommonStructuredOpParts(p, *this); 599 600 genericAttrNames.push_back("operand_segment_sizes"); 601 genericAttrNamesSet.insert(genericAttrNames.back()); 602 603 bool hasExtraAttrs = false; 604 for (NamedAttribute n : (*this)->getAttrs()) { 605 if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref()))) 606 break; 607 } 608 if (hasExtraAttrs) { 609 p << " attrs = "; 610 p.printOptionalAttrDict((*this)->getAttrs(), 611 /*elidedAttrs=*/genericAttrNames); 612 } 613 614 // Print region. 615 if (!region().empty()) { 616 p << ' '; 617 p.printRegion(region()); 618 } 619 620 // Print results. 621 printNamedStructuredOpResults(p, result_tensors().getTypes()); 622 } 623 624 ParseResult GenericOp::parse(OpAsmParser &parser, OperationState &result) { 625 DictionaryAttr dictAttr; 626 // Parse the core linalg traits that must check into a dictAttr. 627 // The name is unimportant as we will overwrite result.attributes. 628 // The core linalg traits must contain the information necessary to pass the 629 // verifier. 630 if (parser.parseAttribute(dictAttr, "_", result.attributes)) 631 return failure(); 632 result.attributes.assign(dictAttr.getValue().begin(), 633 dictAttr.getValue().end()); 634 635 // Parsing is shared with named ops, except for the region. 636 SmallVector<Type, 1> inputTypes, outputTypes; 637 if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes)) 638 return failure(); 639 640 // Optional attributes may be added. 641 if (succeeded(parser.parseOptionalKeyword("attrs"))) 642 if (failed(parser.parseEqual()) || 643 failed(parser.parseOptionalAttrDict(result.attributes))) 644 return failure(); 645 646 SmallVector<OpAsmParser::OperandType, 8> regionOperands; 647 std::unique_ptr<Region> region = std::make_unique<Region>(); 648 SmallVector<Type, 8> operandTypes, regionTypes; 649 if (parser.parseRegion(*region, regionOperands, regionTypes)) 650 return failure(); 651 result.addRegion(std::move(region)); 652 653 // Generic ops may specify that a subset of its outputs are tensors. Such 654 // outputs are specified in the result type. 655 // TODO: may need to move output parsing before region parsing. 656 // Need to wait for declarative assembly resolution to decide. 657 SmallVector<Type, 1> outputTensorsTypes; 658 if (parseNamedStructuredOpResults(parser, outputTensorsTypes)) 659 return failure(); 660 result.addTypes(outputTensorsTypes); 661 662 return success(); 663 } 664 665 static void getGenericEffectsImpl( 666 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>> 667 &effects, 668 ValueRange results, ValueRange inputBuffers, ValueRange outputs) { 669 for (Value value : results) { 670 effects.emplace_back(MemoryEffects::Allocate::get(), value, 671 SideEffects::DefaultResource::get()); 672 } 673 for (Value value : inputBuffers) { 674 effects.emplace_back(MemoryEffects::Read::get(), value, 675 SideEffects::DefaultResource::get()); 676 } 677 for (Value value : outputs) { 678 effects.emplace_back(MemoryEffects::Read::get(), value, 679 SideEffects::DefaultResource::get()); 680 effects.emplace_back(MemoryEffects::Write::get(), value, 681 SideEffects::DefaultResource::get()); 682 } 683 } 684 685 void GenericOp::getEffects( 686 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>> 687 &effects) { 688 SmallVector<Value> inputBuffers = getInputBufferOperands(); 689 SmallVector<Value> outputBuffers = getOutputBufferOperands(); 690 getGenericEffectsImpl(effects, getOperation()->getResults(), inputBuffers, 691 outputBuffers); 692 } 693 694 template <typename GenericOpType> 695 static LogicalResult verifyGenericOp(GenericOpType op) { 696 return success(); 697 } 698 699 LogicalResult GenericOp::verify() { return verifyGenericOp(*this); } 700 701 namespace { 702 // Deduplicate redundant args of a linalg generic op. 703 // An arg is redundant if it has the same Value and indexing map as another. 704 struct DeduplicateGenericOpInputs : public OpRewritePattern<GenericOp> { 705 using OpRewritePattern<GenericOp>::OpRewritePattern; 706 707 LogicalResult matchAndRewrite(GenericOp genericOp, 708 PatternRewriter &rewriter) const override { 709 // Associate each input to an equivalent "canonical" input that has the same 710 // Value and indexing map. 711 // 712 // In the non-duplicate case, input `i` will have canonical input `i`. But 713 // in the case of duplicated inputs, the canonical input could be some other 714 // input `< i`. That is, a later input will have some earlier input as its 715 // canonical input. 716 llvm::SmallDenseMap<std::pair<Value, AffineMap>, unsigned> canonicalInput; 717 // For later remapping tasks like deduplicating payload block arguments, 718 // having a simple "inputIndex -> canonicalInputIndex" integer mapping is 719 // convenient. 720 SmallVector<unsigned> canonicalInputIndices; 721 for (OpOperand *opOperand : genericOp.getInputOperands()) { 722 AffineMap indexingMap = genericOp.getTiedIndexingMap(opOperand); 723 // STL-like maps have a convenient behavior for our use case here. In the 724 // case of duplicate keys, the insertion is rejected, and the returned 725 // iterator gives access to the value already in the map. 726 auto pair = canonicalInput.insert( 727 {{opOperand->get(), indexingMap}, opOperand->getOperandNumber()}); 728 canonicalInputIndices.push_back(pair.first->second); 729 } 730 731 // If there are no duplicate args, then bail out. 732 if (canonicalInput.size() == genericOp.getNumInputs()) 733 return failure(); 734 735 // The operands for the newly canonicalized op. 736 SmallVector<Value> newInputOperands; 737 for (OpOperand *opOperand : genericOp.getInputOperands()) 738 if (canonicalInputIndices[opOperand->getOperandNumber()] == 739 opOperand->getOperandNumber()) 740 newInputOperands.push_back(opOperand->get()); 741 742 // Repair the indexing maps by filtering out the ones that have been 743 // eliminated. 744 SmallVector<AffineMap> newIndexingMaps; 745 for (OpOperand *opOperand : genericOp.getInputOperands()) 746 if (canonicalInputIndices[opOperand->getOperandNumber()] == 747 opOperand->getOperandNumber()) 748 newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand)); 749 for (OpOperand *opOperand : genericOp.getOutputOperands()) 750 newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand)); 751 752 // Clone the old op with new operands. 753 SmallVector<Value> outputOperands = genericOp.getOutputOperands(); 754 auto newOp = rewriter.create<GenericOp>( 755 genericOp.getLoc(), genericOp->getResultTypes(), newInputOperands, 756 outputOperands, rewriter.getAffineMapArrayAttr(newIndexingMaps), 757 genericOp.iterator_types(), genericOp.docAttr(), 758 genericOp.library_callAttr()); 759 760 // Copy over unknown attributes. They might be load bearing for some flow. 761 ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames(); 762 for (NamedAttribute kv : genericOp->getAttrs()) { 763 if (!llvm::is_contained(odsAttrs, kv.getName().getValue())) { 764 newOp->setAttr(kv.getName(), kv.getValue()); 765 } 766 } 767 768 rewriter.inlineRegionBefore(genericOp.region(), newOp.region(), 769 newOp.region().begin()); 770 771 // Repair the payload entry block by RAUW'ing redundant arguments and 772 // erasing them. 773 Block &payload = newOp.region().front(); 774 SmallVector<OpOperand *> inputOperands = genericOp.getInputOperands(); 775 for (OpOperand *opOperand : llvm::reverse(inputOperands)) { 776 // Iterate in reverse, so that we erase later args first, preventing the 777 // argument list from shifting unexpectedly and invalidating all our 778 // indices. 779 unsigned operandNumber = opOperand->getOperandNumber(); 780 if (canonicalInputIndices[operandNumber] == operandNumber) 781 continue; 782 payload.getArgument(operandNumber) 783 .replaceAllUsesWith( 784 payload.getArgument(canonicalInputIndices[operandNumber])); 785 payload.eraseArgument(operandNumber); 786 } 787 788 rewriter.replaceOp(genericOp, newOp->getResults()); 789 return success(); 790 } 791 }; 792 793 /// Remove generic operations (on tensors) that are just copying 794 /// the values from inputs to the results. Requirements are 795 /// 1) All iterator types are parallel 796 /// 2) The body contains just a yield operation with the yielded values being 797 /// the arguments corresponding to the operands. 798 struct EraseIdentityGenericOp : public OpRewritePattern<GenericOp> { 799 using OpRewritePattern<GenericOp>::OpRewritePattern; 800 801 LogicalResult matchAndRewrite(GenericOp genericOp, 802 PatternRewriter &rewriter) const override { 803 // Check all indexing maps are identity. 804 if (llvm::any_of(genericOp.getIndexingMaps(), 805 [](AffineMap map) { return !map.isIdentity(); })) 806 return failure(); 807 808 // Check that the body of the linalg operation is just a linalg.yield 809 // operation. 810 Block &body = genericOp.region().front(); 811 if (!llvm::hasSingleElement(body)) 812 return failure(); 813 auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator()); 814 if (!yieldOp) 815 return failure(); 816 817 // In the buffer case, we need to check exact buffer equality. 818 if (genericOp.hasBufferSemantics()) { 819 if (genericOp.getNumInputs() == 1 && genericOp.getNumOutputs() == 1 && 820 genericOp.getInputOperand(0)->get() == 821 genericOp.getOutputOperand(0)->get()) { 822 rewriter.eraseOp(genericOp); 823 return success(); 824 } 825 return failure(); 826 } 827 828 // Get the argument number of the returned values. That is the operand 829 // number to use for replacing uses of this operation. 830 SmallVector<Value> returnedArgs; 831 for (const auto &yieldVal : llvm::enumerate(yieldOp.values())) { 832 auto yieldArg = yieldVal.value().dyn_cast<BlockArgument>(); 833 if (!yieldArg || yieldArg.getOwner() != &body) 834 return failure(); 835 unsigned argumentNumber = yieldArg.getArgNumber(); 836 Value returnedArg = genericOp->getOperand(argumentNumber); 837 Type resultType = genericOp->getResult(yieldVal.index()).getType(); 838 // The input can have a different type than the result, e.g. a dynamic 839 // input dimension can be turned into a static output dimension. 840 Type returnType = returnedArg.getType(); 841 if (returnType != resultType) { 842 // Distinguish between sparse conversion or dense tensor casting. 843 // TODO: unify the two ops? 844 if (sparse_tensor::getSparseTensorEncoding(returnType) || 845 sparse_tensor::getSparseTensorEncoding(resultType)) 846 returnedArg = rewriter.create<sparse_tensor::ConvertOp>( 847 genericOp.getLoc(), resultType, returnedArg); 848 else 849 returnedArg = rewriter.create<tensor::CastOp>( 850 genericOp.getLoc(), resultType, returnedArg); 851 } 852 returnedArgs.push_back(returnedArg); 853 } 854 855 if (returnedArgs.size() != genericOp->getNumResults()) 856 return failure(); 857 rewriter.replaceOp(genericOp, returnedArgs); 858 return success(); 859 } 860 }; 861 862 /// For each of the operand in `operands` this function maps the static sizes of 863 /// dimensions to their affine dim expressions. 864 static void populateMap(GenericOp genericOp, ArrayRef<OpOperand *> operands, 865 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) { 866 for (OpOperand *opOperand : operands) { 867 if (genericOp.isScalar(opOperand)) 868 continue; 869 Value src = opOperand->get(); 870 auto sourceType = src.getType().cast<RankedTensorType>(); 871 auto sourceMap = genericOp.getTiedIndexingMap(opOperand); 872 873 // Get the `sourceShape` of the `sourceType`. If the operand is a result of 874 // `tensor.cast` operation and source of the cast operation has a static 875 // shape, then assign it to the `sourceShape`. 876 auto *parentOp = src.getDefiningOp(); 877 ArrayRef<int64_t> sourceShape = sourceType.getShape(); 878 if (parentOp) { 879 if (auto castOp = dyn_cast<tensor::CastOp>(parentOp)) { 880 Value castSource = castOp.source(); 881 auto castSourceType = castSource.getType().cast<RankedTensorType>(); 882 if (castSourceType.hasStaticShape()) 883 sourceShape = castSourceType.getShape(); 884 } 885 } 886 887 // If the source shape's dimension has a static shape, map the affine dim 888 // expression to the known static size. 889 for (unsigned i = 0; i < sourceShape.size(); i++) { 890 if (sourceType.isDynamicDim(i)) 891 continue; 892 if (auto affineDimExpr = sourceMap.getResult(i).dyn_cast<AffineDimExpr>()) 893 affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]); 894 } 895 } 896 } 897 898 /// Creates new operand w.r.t 'opOperand' of `genericOp` with static sizes 899 /// mapped in `affineExprToSize`. New operands are created in `newOperands` and 900 /// their result types is stored in `resultTypes`. If `opOperand` requires no 901 /// change then `changeNeeded` is false and same operand is added in the 902 /// `newOperands` list. 903 static void createNewOperandWithStaticSizes( 904 Location loc, PatternRewriter &rewriter, OpOperand *opOperand, 905 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, GenericOp genericOp, 906 SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes, 907 bool &changeNeeded) { 908 Value src = opOperand->get(); 909 newOperands.push_back(src); 910 if (genericOp.isScalar(opOperand)) 911 return; 912 auto sourceType = src.getType().cast<RankedTensorType>(); 913 Type resultType = sourceType; 914 if (sourceType.hasStaticShape() && genericOp.isOutputTensor(opOperand)) { 915 resultTypes.push_back(resultType); 916 return; 917 } 918 ArrayRef<int64_t> sourceShape = sourceType.getShape(); 919 AffineMap sourceMap = genericOp.getTiedIndexingMap(opOperand); 920 SmallVector<int64_t> newShape; 921 // If operand is updated with new shape, `newOperandNeeded` will be 922 // true. 923 bool newOperandNeeded = false; 924 for (unsigned i = 0; i < sourceShape.size(); i++) { 925 int64_t dimShape = sourceShape[i]; 926 AffineExpr dimExpr = sourceMap.getResult(i); 927 if (affineExprToSize.find(dimExpr) == affineExprToSize.end() || 928 !sourceType.isDynamicDim(i)) { 929 newShape.push_back(dimShape); 930 continue; 931 } 932 // Dimension has a dynamic shape and corresponding affine dim 933 // expression is present in the map. So assign the size for the 934 // given affine dim expression to the dimension. 935 newShape.push_back(affineExprToSize[dimExpr]); 936 newOperandNeeded = true; 937 } 938 resultType = RankedTensorType::get(newShape, sourceType.getElementType()); 939 if (newOperandNeeded) { 940 changeNeeded = true; 941 // Get the new operand value given its size and element type by 942 // casting it. 943 Value newOperand = rewriter.create<tensor::CastOp>(loc, resultType, src); 944 unsigned index = opOperand->getOperandNumber(); 945 newOperands[index] = newOperand; 946 } 947 if (genericOp.isOutputTensor(opOperand)) 948 resultTypes.push_back(resultType); 949 } 950 951 /// Static shapes for the operands can be inferred if any one of the operands 952 /// have a static shape. This can be done by referring to the affine dim 953 /// expressions for the operand. 954 struct InferStaticShapeOfOperands : public OpRewritePattern<GenericOp> { 955 using OpRewritePattern<GenericOp>::OpRewritePattern; 956 957 LogicalResult matchAndRewrite(GenericOp genericOp, 958 PatternRewriter &rewriter) const override { 959 if (!genericOp.hasTensorSemantics()) 960 return failure(); 961 962 // Maps must be projected permutations. 963 if (llvm::any_of(genericOp.getIndexingMaps(), [](AffineMap map) { 964 return !map.isProjectedPermutation(); 965 })) 966 return failure(); 967 968 // Maps affine dim expressions to the static size of that dimension. 969 llvm::DenseMap<AffineExpr, int64_t> affineExprToSize; 970 Location loc = genericOp.getLoc(); 971 972 // For each of the affine dim expression, check if the size is known. If 973 // known add that in the map. 974 populateMap(genericOp, genericOp.getInputAndOutputOperands(), 975 affineExprToSize); 976 977 SmallVector<Value> newOperands; 978 SmallVector<Type> resultTypes; 979 980 // `changeNeeded` is `false` if the operands of `genericOp` require no 981 // change in their types. 982 bool changeNeeded = false; 983 newOperands.reserve(genericOp.getNumInputsAndOutputs()); 984 resultTypes.reserve(genericOp.getNumOutputs()); 985 986 // Iterate over all the operands and update the static sizes. 987 for (OpOperand *opOperand : genericOp.getInputAndOutputOperands()) { 988 createNewOperandWithStaticSizes(loc, rewriter, opOperand, 989 affineExprToSize, genericOp, newOperands, 990 resultTypes, changeNeeded); 991 } 992 993 // If the generic op has all the required static information, no 994 // canonicalization needed. 995 if (!changeNeeded) 996 return failure(); 997 998 // Clone op. 999 Operation *newOp = 1000 cast<linalg::LinalgOp>(genericOp.getOperation()) 1001 .clone(rewriter, genericOp->getLoc(), resultTypes, newOperands); 1002 SmallVector<Value> replacements; 1003 replacements.reserve(newOp->getNumResults()); 1004 for (auto it : llvm::zip(genericOp->getResults(), newOp->getResults())) { 1005 Value newResult = std::get<1>(it); 1006 Value oldResult = std::get<0>(it); 1007 Type newType = newResult.getType(); 1008 Type oldType = oldResult.getType(); 1009 replacements.push_back( 1010 (newType != oldType) 1011 ? rewriter.create<tensor::CastOp>(loc, oldType, newResult) 1012 : newResult); 1013 } 1014 rewriter.replaceOp(genericOp, replacements); 1015 return success(); 1016 } 1017 }; 1018 } // namespace 1019 1020 void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results, 1021 MLIRContext *context) { 1022 results.add<DeduplicateGenericOpInputs, EraseIdentityGenericOp, 1023 InferStaticShapeOfOperands>(context); 1024 } 1025 1026 //===----------------------------------------------------------------------===// 1027 // InitTensorOp 1028 //===----------------------------------------------------------------------===// 1029 1030 void InitTensorOp::build(OpBuilder &b, OperationState &result, 1031 ArrayRef<OpFoldResult> sizes, Type elementType, 1032 ArrayRef<NamedAttribute> attrs) { 1033 SmallVector<Value, 4> dynamicSizes; 1034 SmallVector<int64_t, 4> staticSizes; 1035 dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes, 1036 ShapedType::kDynamicSize); 1037 auto resultType = RankedTensorType ::get(staticSizes, elementType); 1038 build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes)); 1039 result.addAttributes(attrs); 1040 } 1041 1042 LogicalResult InitTensorOp::verify() { 1043 RankedTensorType resultType = getType(); 1044 SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range( 1045 static_sizes().cast<ArrayAttr>(), 1046 [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); })); 1047 1048 if (failed(verifyListOfOperandsOrIntegers( 1049 *this, "sizes", resultType.getRank(), static_sizes(), sizes(), 1050 ShapedType::isDynamic))) 1051 return failure(); 1052 1053 if (static_sizes().size() != static_cast<unsigned>(resultType.getRank())) 1054 return emitError("expected ") << resultType.getRank() << " sizes values"; 1055 1056 Type expectedType = InitTensorOp::inferResultType( 1057 staticSizes, resultType.getElementType(), resultType.getEncoding()); 1058 if (resultType != expectedType) { 1059 return emitError("specified type ") 1060 << resultType << " does not match the inferred type " 1061 << expectedType; 1062 } 1063 return success(); 1064 } 1065 1066 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes, 1067 Type elementType, Attribute encoding) { 1068 return RankedTensorType::get(staticSizes, elementType, encoding); 1069 } 1070 1071 namespace { 1072 /// Change the type of the result of a `linalg.init_tensor` by making the result 1073 /// type statically sized along dimension that in the original operation where 1074 /// defined as dynamic, but the size was defined using a `constant` op. For 1075 /// example 1076 /// 1077 /// %c5 = arith.constant 5: index 1078 /// %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32> 1079 /// 1080 /// to 1081 /// 1082 /// %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32> 1083 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> { 1084 using OpRewritePattern<InitTensorOp>::OpRewritePattern; 1085 1086 LogicalResult matchAndRewrite(InitTensorOp op, 1087 PatternRewriter &rewriter) const override { 1088 SmallVector<Value, 4> dynamicSizes; 1089 SmallVector<int64_t, 4> staticSizes; 1090 for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) { 1091 // If the size is already static, nothing to do. 1092 if (!op.isDynamicSize(i)) { 1093 staticSizes.push_back(op.getStaticSize(i)); 1094 continue; 1095 } 1096 1097 // If the size is dynamic but defined using a `constant` op, get the 1098 // constant value to find the static size to use. 1099 unsigned operandNum = op.getIndexOfDynamicSize(i); 1100 Value sizeOperand = op.getOperand(operandNum); 1101 if (auto constantIndexOp = 1102 sizeOperand.getDefiningOp<arith::ConstantIndexOp>()) { 1103 staticSizes.push_back(constantIndexOp.value()); 1104 continue; 1105 } 1106 1107 // Fallback case. Keep the size dynamic. 1108 dynamicSizes.push_back(sizeOperand); 1109 staticSizes.push_back(ShapedType::kDynamicSize); 1110 } 1111 RankedTensorType newType = 1112 RankedTensorType::get(staticSizes, op.getType().getElementType()); 1113 if (newType == op.getType()) 1114 return failure(); 1115 auto newOp = 1116 rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes, 1117 rewriter.getI64ArrayAttr(staticSizes)); 1118 rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp); 1119 return success(); 1120 } 1121 }; 1122 } // namespace 1123 1124 namespace { 1125 /// Since `init_tensor` operation creates a tensor needed only for its shape, a 1126 /// slice of this is also needed only for its shape. The result can be 1127 /// replaced by a new init_tensor operation of the same size as the extract 1128 /// slice op. 1129 struct FoldInitTensorWithExtractSliceOp 1130 : public OpRewritePattern<tensor::ExtractSliceOp> { 1131 using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern; 1132 1133 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp, 1134 PatternRewriter &rewriter) const override { 1135 if (!sliceOp.source().getDefiningOp<linalg::InitTensorOp>()) 1136 return failure(); 1137 // ExtractSliceOp may be rank-reducing; its dynamic sizes must be preserved 1138 // as well as its result type. 1139 rewriter.replaceOpWithNewOp<linalg::InitTensorOp>( 1140 sliceOp, sliceOp.sizes(), 1141 sliceOp.result().getType().cast<RankedTensorType>().getShape(), 1142 sliceOp.getSourceType().getElementType()); 1143 return success(); 1144 } 1145 }; 1146 1147 template <typename TensorReshapeOp> 1148 struct FoldInitTensorWithTensorReshapeOp 1149 : public OpRewritePattern<TensorReshapeOp> { 1150 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern; 1151 1152 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp, 1153 PatternRewriter &rewriter) const override { 1154 if (!reshapeOp.src().template getDefiningOp<InitTensorOp>()) 1155 return failure(); 1156 Location loc = reshapeOp.getLoc(); 1157 ReifiedRankedShapedTypeDims resultShapes; 1158 ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface = 1159 cast<ReifyRankedShapedTypeOpInterface>(reshapeOp.getOperation()); 1160 if (failed(reifyShapedTypeInterface.reifyResultShapes(rewriter, 1161 resultShapes)) || 1162 !llvm::hasSingleElement(resultShapes)) 1163 return failure(); 1164 Value initTensor = rewriter.create<InitTensorOp>( 1165 loc, getAsOpFoldResult(resultShapes[0]), 1166 reshapeOp.getResultType().getElementType()); 1167 if (initTensor.getType() != reshapeOp.getResultType()) { 1168 rewriter.replaceOpWithNewOp<tensor::CastOp>( 1169 reshapeOp, reshapeOp.getResultType(), initTensor); 1170 } else { 1171 rewriter.replaceOp(reshapeOp, initTensor); 1172 } 1173 return success(); 1174 } 1175 }; 1176 1177 struct FoldInitTensorWithDimOp : public OpRewritePattern<tensor::DimOp> { 1178 using OpRewritePattern<tensor::DimOp>::OpRewritePattern; 1179 1180 LogicalResult matchAndRewrite(tensor::DimOp dimOp, 1181 PatternRewriter &rewriter) const override { 1182 Optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex(); 1183 auto initTensorOp = dimOp.source().getDefiningOp<linalg::InitTensorOp>(); 1184 if (!initTensorOp || !maybeConstantIndex) 1185 return failure(); 1186 if (!initTensorOp.isDynamicSize(*maybeConstantIndex)) 1187 return failure(); 1188 rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(*maybeConstantIndex)); 1189 return success(); 1190 } 1191 }; 1192 } // namespace 1193 1194 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results, 1195 MLIRContext *context) { 1196 results.add<FoldInitTensorWithDimOp, FoldInitTensorWithExtractSliceOp, 1197 FoldInitTensorWithTensorReshapeOp<tensor::ExpandShapeOp>, 1198 FoldInitTensorWithTensorReshapeOp<tensor::CollapseShapeOp>, 1199 ReplaceStaticShapeDims>(context); 1200 } 1201 1202 LogicalResult InitTensorOp::reifyResultShapes( 1203 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) { 1204 auto shapes = llvm::to_vector<4>(llvm::map_range( 1205 llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value { 1206 if (isDynamicSize(dim)) 1207 return getDynamicSize(dim); 1208 return builder.create<arith::ConstantIndexOp>(getLoc(), 1209 getStaticSize(dim)); 1210 })); 1211 reifiedReturnShapes.emplace_back(std::move(shapes)); 1212 return success(); 1213 } 1214 1215 //===----------------------------------------------------------------------===// 1216 // YieldOp 1217 //===----------------------------------------------------------------------===// 1218 1219 void linalg::YieldOp::print(OpAsmPrinter &p) { 1220 if (getNumOperands() > 0) 1221 p << ' ' << getOperands(); 1222 p.printOptionalAttrDict((*this)->getAttrs()); 1223 if (getNumOperands() > 0) 1224 p << " : " << getOperandTypes(); 1225 } 1226 1227 ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &result) { 1228 SmallVector<OpAsmParser::OperandType, 2> opInfo; 1229 SmallVector<Type, 2> types; 1230 SMLoc loc = parser.getCurrentLocation(); 1231 return failure(parser.parseOperandList(opInfo) || 1232 parser.parseOptionalAttrDict(result.attributes) || 1233 (!opInfo.empty() && parser.parseColonTypeList(types)) || 1234 parser.resolveOperands(opInfo, types, loc, result.operands)); 1235 } 1236 1237 // Check the operand number and types must match the element types of the 1238 // LinalgOp interface's shaped operands. 1239 static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp) { 1240 if (op.getNumOperands() != linalgOp.getNumOutputs()) 1241 return op.emitOpError("expected number of yield values (") 1242 << linalgOp.getNumOutputs() 1243 << ") to match the number of operands of the enclosing " 1244 << "LinalgOp (" << op.getNumOperands() << ")"; 1245 1246 for (OpOperand &opOperand : op->getOpOperands()) { 1247 OpOperand *outputOperand = 1248 linalgOp.getOutputOperand(opOperand.getOperandNumber()); 1249 Type elementType = getElementTypeOrSelf(outputOperand->get().getType()); 1250 if (opOperand.get().getType() != elementType) 1251 return op.emitOpError("type of yield operand ") 1252 << (opOperand.getOperandNumber() + 1) << " (" 1253 << opOperand.get().getType() << ") doesn't match " 1254 << "the element type of the enclosing linalg.generic op (" 1255 << elementType << ")"; 1256 } 1257 return success(); 1258 } 1259 1260 LogicalResult linalg::YieldOp::verify() { 1261 auto *parentOp = (*this)->getParentOp(); 1262 if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty()) 1263 return emitOpError("expected single non-empty parent region"); 1264 1265 if (auto linalgOp = dyn_cast<LinalgOp>(parentOp)) 1266 return verifyYield(*this, cast<LinalgOp>(parentOp)); 1267 1268 return emitOpError("expected parent op with LinalgOp interface"); 1269 } 1270 1271 //===----------------------------------------------------------------------===// 1272 // IndexOp 1273 //===----------------------------------------------------------------------===// 1274 1275 LogicalResult IndexOp::verify() { 1276 auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp()); 1277 if (!linalgOp) 1278 return emitOpError("expected parent op with LinalgOp interface"); 1279 if (linalgOp.getNumLoops() <= dim()) 1280 return emitOpError("expected dim (") 1281 << dim() << ") to be lower than the number of loops (" 1282 << linalgOp.getNumLoops() << ") of the enclosing LinalgOp"; 1283 return success(); 1284 } 1285 1286 /////// Operations corresponding to library calls defined with Tablegen //////// 1287 1288 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc" 1289 1290 #define GET_OP_CLASSES 1291 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc" 1292 1293 #define GET_OP_CLASSES 1294 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc" 1295 1296 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`. 1297 /// Assumes `op` is a LinalgOp. 1298 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName, 1299 SmallVectorImpl<unsigned> &res) { 1300 if (!cast<LinalgOp>(op).iterator_types()) 1301 return; 1302 1303 unsigned dim = 0; 1304 for (auto tn : 1305 cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) { 1306 if (tn == iteratorTypeName) 1307 res.push_back(dim); 1308 ++dim; 1309 } 1310 } 1311 1312 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap, 1313 unsigned rank, 1314 MLIRContext *context) { 1315 if (maybeMap) 1316 return maybeMap.getValue(); 1317 if (rank == 0) 1318 return AffineMap::get(context); 1319 return AffineMap::getMultiDimIdentityMap(rank, context); 1320 } 1321 1322 SmallVector<AffineExpr, 4> 1323 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx, 1324 MLIRContext *context) { 1325 SmallVector<AffineExpr, 4> res; 1326 res.reserve(num); 1327 for (unsigned i = 0; i < num; ++i) 1328 res.push_back(getAffineDimExpr(startIdx++, context)); 1329 return res; 1330 } 1331 1332 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a, 1333 ArrayRef<AffineExpr> b) { 1334 auto rangeA = llvm::make_range(a.begin(), a.end()); 1335 auto rangeB = llvm::make_range(b.begin(), b.end()); 1336 auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB); 1337 return llvm::to_vector<4>(concatRanges); 1338 } 1339 1340 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) { 1341 if (auto memref = t.dyn_cast<MemRefType>()) { 1342 ss << "view"; 1343 for (auto size : memref.getShape()) 1344 if (size < 0) 1345 ss << "sx"; 1346 else 1347 ss << size << "x"; 1348 appendMangledType(ss, memref.getElementType()); 1349 } else if (auto vec = t.dyn_cast<VectorType>()) { 1350 ss << "vector"; 1351 llvm::interleave( 1352 vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; }); 1353 appendMangledType(ss, vec.getElementType()); 1354 } else if (t.isSignlessIntOrIndexOrFloat()) { 1355 ss << t; 1356 } else { 1357 llvm_unreachable("Invalid type for linalg library name mangling"); 1358 } 1359 } 1360 1361 std::string mlir::linalg::generateLibraryCallName(Operation *op) { 1362 assert(isa<LinalgOp>(op)); 1363 std::string name(op->getName().getStringRef().str()); 1364 name.reserve(128); 1365 std::replace(name.begin(), name.end(), '.', '_'); 1366 llvm::raw_string_ostream ss(name); 1367 ss << "_"; 1368 auto types = op->getOperandTypes(); 1369 llvm::interleave( 1370 types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); }, 1371 [&]() { ss << "_"; }); 1372 return ss.str(); 1373 } 1374 1375 //===----------------------------------------------------------------------===// 1376 // Support for named Linalg ops defined in ods-gen. 1377 //===----------------------------------------------------------------------===// 1378 1379 /// Generic entry point to create the block for the region of a LinalgOp. 1380 /// This is used by both named structured ops created by ods-gen and by manually 1381 /// defined C++ ops. 1382 /// This is used by both builders and parsers. 1383 /// This function creates the block in the region with arguments corresponding 1384 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted 1385 /// to be ShapedType. 1386 template <typename NamedStructuredOpType> 1387 static void fillStructuredOpRegion( 1388 OpBuilder &opBuilder, Region ®ion, TypeRange inputTypes, 1389 TypeRange outputTypes, ArrayRef<NamedAttribute> attrs, 1390 llvm::function_ref<void(unsigned, unsigned)> errorHandler) { 1391 assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); })); 1392 1393 // TODO: atm all operands go through getElementTypeOrSelf, 1394 // reconsider when we have evidence we need to. 1395 SmallVector<Type, 8> argTypes; 1396 SmallVector<Location, 8> argLocs; 1397 for (auto containers : {inputTypes, outputTypes}) { 1398 for (auto t : containers) { 1399 argTypes.push_back(getElementTypeOrSelf(t)); 1400 1401 // TODO: Pass in a proper location here. 1402 argLocs.push_back(opBuilder.getUnknownLoc()); 1403 } 1404 } 1405 1406 // RAII. 1407 OpBuilder::InsertionGuard guard(opBuilder); 1408 Block *body = 1409 opBuilder.createBlock(®ion, /*insertPt=*/{}, argTypes, argLocs); 1410 unsigned actual = body->getNumArguments(); 1411 unsigned expected = NamedStructuredOpType::getNumRegionArgs(); 1412 if (expected != actual) { 1413 if (errorHandler) 1414 errorHandler(expected, actual); 1415 return; 1416 } 1417 1418 opBuilder.setInsertionPointToStart(body); 1419 ImplicitLocOpBuilder b(opBuilder.getUnknownLoc(), opBuilder); 1420 NamedStructuredOpType::regionBuilder(b, *body, attrs); 1421 1422 // indexing_maps is an auto-generated method. 1423 1424 // iterator_types is an auto-generated method. 1425 } 1426 1427 /// Generic entry point to create both the region and the block of a LinalgOp. 1428 template <typename NamedStructuredOpType> 1429 void createAndFillStructuredOpRegion(OpBuilder &opBuilder, 1430 OperationState &result, 1431 TypeRange inputTypes, 1432 TypeRange outputTypes) { 1433 Region ®ion = *result.addRegion(); 1434 fillStructuredOpRegion<NamedStructuredOpType>( 1435 opBuilder, region, inputTypes, outputTypes, result.attributes.getAttrs(), 1436 [&](unsigned expected, unsigned actual) { 1437 assert(expected != actual && "incorrect number of arguments"); 1438 }); 1439 } 1440 1441 /// Common parsing used for both named structured ops created by ods-gen and by 1442 /// manually defined C++ ops. Does not handle regions. 1443 static ParseResult 1444 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result, 1445 SmallVectorImpl<Type> &inputTypes, 1446 SmallVectorImpl<Type> &outputTypes) { 1447 SMLoc inputsOperandsLoc, outputsOperandsLoc; 1448 SmallVector<OpAsmParser::OperandType, 4> inputsOperands, outputsOperands; 1449 1450 parser.parseOptionalAttrDict(result.attributes); 1451 1452 if (succeeded(parser.parseOptionalKeyword("ins"))) { 1453 if (parser.parseLParen()) 1454 return failure(); 1455 1456 inputsOperandsLoc = parser.getCurrentLocation(); 1457 if (parser.parseOperandList(inputsOperands) || 1458 parser.parseColonTypeList(inputTypes) || parser.parseRParen()) 1459 return failure(); 1460 } 1461 1462 if (succeeded(parser.parseOptionalKeyword("outs"))) { 1463 outputsOperandsLoc = parser.getCurrentLocation(); 1464 if (parser.parseLParen() || parser.parseOperandList(outputsOperands) || 1465 parser.parseColonTypeList(outputTypes) || parser.parseRParen()) 1466 return failure(); 1467 } 1468 1469 if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc, 1470 result.operands) || 1471 parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc, 1472 result.operands)) 1473 return failure(); 1474 1475 result.addAttribute("operand_segment_sizes", 1476 parser.getBuilder().getI32VectorAttr( 1477 {static_cast<int32_t>(inputsOperands.size()), 1478 static_cast<int32_t>(outputsOperands.size())})); 1479 return success(); 1480 } 1481 1482 template <typename NamedStructuredOpType> 1483 static void printCommonStructuredOpParts(OpAsmPrinter &p, 1484 NamedStructuredOpType op) { 1485 if (!op.inputs().empty()) 1486 p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")"; 1487 if (!op.outputs().empty()) 1488 p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")"; 1489 } 1490 1491 //===----------------------------------------------------------------------===// 1492 // Specific parsing and printing for named structured ops created by ods-gen. 1493 //===----------------------------------------------------------------------===// 1494 1495 template <typename NamedStructuredOpType> 1496 static ParseResult 1497 parseNamedStructuredOpRegion(OpAsmParser &parser, Region ®ion, 1498 TypeRange inputTypes, TypeRange outputTypes, 1499 ArrayRef<NamedAttribute> attrs) { 1500 ParseResult res = success(); 1501 OpBuilder opBuilder(parser.getContext()); 1502 // Resolve `captures` into `capturedValues` at parse time so we can build the 1503 // region with captures. 1504 SmallVector<Value> capturedValues; 1505 fillStructuredOpRegion<NamedStructuredOpType>( 1506 opBuilder, region, inputTypes, outputTypes, attrs, 1507 [&](unsigned expected, unsigned actual) { 1508 res = parser.emitError( 1509 parser.getCurrentLocation(), 1510 llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated " 1511 "region expects {0} args, got {1}", 1512 expected, actual)); 1513 region.front().dump(); 1514 }); 1515 return res; 1516 } 1517 1518 static ParseResult 1519 parseNamedStructuredOpResults(OpAsmParser &parser, 1520 SmallVectorImpl<Type> &resultTypes) { 1521 if (parser.parseOptionalArrowTypeList(resultTypes)) 1522 return failure(); 1523 return success(); 1524 } 1525 1526 template <typename NamedStructuredOpType> 1527 static ParseResult parseNamedStructuredOp(OpAsmParser &parser, 1528 OperationState &result) { 1529 // TODO: Enable when ods-gen supports captures. 1530 SmallVector<Type, 1> inputTypes, outputTypes; 1531 if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes)) 1532 return failure(); 1533 1534 // TODO: consider merging results parsing into region parsing. 1535 // Need to wait for declarative assembly resolution to decide. 1536 SmallVector<Type, 1> outputTensorsTypes; 1537 if (parseNamedStructuredOpResults(parser, outputTensorsTypes)) 1538 return failure(); 1539 result.addTypes(outputTensorsTypes); 1540 1541 std::unique_ptr<Region> region = std::make_unique<Region>(); 1542 if (parseNamedStructuredOpRegion<NamedStructuredOpType>( 1543 parser, *region, inputTypes, outputTypes, 1544 result.attributes.getAttrs())) 1545 return failure(); 1546 result.addRegion(std::move(region)); 1547 1548 return success(); 1549 } 1550 1551 static void printNamedStructuredOpResults(OpAsmPrinter &p, 1552 TypeRange resultTypes) { 1553 if (resultTypes.empty()) 1554 return; 1555 p.printOptionalArrowTypeList(resultTypes); 1556 } 1557 1558 template <typename NamedStructuredOpType> 1559 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) { 1560 p.printOptionalAttrDict( 1561 op->getAttrs(), 1562 /*elidedAttrs=*/{"operand_segment_sizes", 1563 // See generated code in mlir-linalg-yaml-gen.cpp 1564 "linalg.memoized_indexing_maps"}); 1565 1566 // Printing is shared with generic ops, except for the region and 1567 // attributes. 1568 printCommonStructuredOpParts(p, op); 1569 1570 // Results printing. 1571 printNamedStructuredOpResults(p, op.result_tensors().getTypes()); 1572 1573 // Region is elided. 1574 } 1575 1576 template <typename NamedStructuredOpType> 1577 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) { 1578 return verifyGenericOp<NamedStructuredOpType>(op); 1579 } 1580 1581 //===----------------------------------------------------------------------===// 1582 // Canonicalizers and Folders. 1583 //===----------------------------------------------------------------------===// 1584 1585 namespace { 1586 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> { 1587 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern; 1588 1589 LogicalResult matchAndRewrite(LinalgOp op, 1590 PatternRewriter &rewriter) const override { 1591 for (OpOperand *opOperand : op.getInputAndOutputOperands()) { 1592 // Linalg "inputs" may be either tensor or memref type. 1593 // tensor<0xelt_type> is a convention that may not always mean 1594 // "0 iterations". Only erase in cases we see memref<...x0x...>. 1595 auto mt = opOperand->get().getType().dyn_cast<MemRefType>(); 1596 if (!mt) 1597 continue; 1598 if (llvm::is_contained(op.getShape(opOperand), 0)) { 1599 rewriter.eraseOp(op); 1600 return success(); 1601 } 1602 } 1603 return failure(); 1604 } 1605 }; 1606 1607 struct FoldTensorCastOp : public OpInterfaceRewritePattern<LinalgOp> { 1608 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern; 1609 1610 LogicalResult matchAndRewrite(LinalgOp op, 1611 PatternRewriter &rewriter) const override { 1612 // If no operand comes from a tensor::CastOp and can be folded then fail. 1613 bool hasTensorCastOperand = 1614 llvm::any_of(op.getInputAndOutputOperands(), [&](OpOperand *opOperand) { 1615 if (opOperand->get().isa<BlockArgument>()) 1616 return false; 1617 auto castOp = opOperand->get().getDefiningOp<tensor::CastOp>(); 1618 return castOp && canFoldIntoConsumerOp(castOp); 1619 }); 1620 if (!hasTensorCastOperand) 1621 return failure(); 1622 1623 SmallVector<Type, 4> newResultTypes; 1624 newResultTypes.reserve(op->getNumResults()); 1625 SmallVector<Value, 4> newOperands; 1626 newOperands.reserve(op->getNumOperands()); 1627 // Inputs may fold. 1628 for (OpOperand *opOperand : op.getInputOperands()) { 1629 auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>(); 1630 newOperands.push_back(canFoldIntoConsumerOp(tensorCastOp) 1631 ? tensorCastOp.source() 1632 : opOperand->get()); 1633 } 1634 // Init tensors may fold, in which case the resultType must also change. 1635 for (OpOperand *opOperand : op.getOutputOperands()) { 1636 auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>(); 1637 bool fold = canFoldIntoConsumerOp(tensorCastOp); 1638 newOperands.push_back(fold ? tensorCastOp.getOperand() 1639 : opOperand->get()); 1640 newResultTypes.push_back(newOperands.back().getType()); 1641 } 1642 // Clone op. 1643 Operation *newOp = 1644 op.clone(rewriter, op->getLoc(), newResultTypes, newOperands); 1645 SmallVector<Value, 4> replacements; 1646 replacements.reserve(newOp->getNumResults()); 1647 for (auto result : llvm::zip(op->getResults(), newOp->getResults())) { 1648 Value oldResult = std::get<0>(result); 1649 Value newResult = std::get<1>(result); 1650 if (newResult.getType() != oldResult.getType()) { 1651 replacements.push_back(rewriter.create<tensor::CastOp>( 1652 op->getLoc(), oldResult.getType(), newResult)); 1653 } else { 1654 replacements.push_back(newResult); 1655 } 1656 } 1657 rewriter.replaceOp(op, replacements); 1658 1659 return success(); 1660 } 1661 }; 1662 1663 } // namespace 1664 1665 #define LINALGOP_FOLDERS(XXX) \ 1666 LogicalResult XXX::fold(ArrayRef<Attribute>, \ 1667 SmallVectorImpl<OpFoldResult> &) { \ 1668 return foldMemRefCast(*this); \ 1669 } 1670 1671 LINALGOP_FOLDERS(FillOp) 1672 LINALGOP_FOLDERS(GenericOp) 1673 1674 // All named ops canonicalizers and folders are auto-generated in the 1675 // .cpp.inc. 1676 1677 //===----------------------------------------------------------------------===// 1678 // LinalgDialect 1679 //===----------------------------------------------------------------------===// 1680 1681 void LinalgDialect::getCanonicalizationPatterns( 1682 RewritePatternSet &results) const { 1683 results.add<EraseDeadLinalgOp, FoldTensorCastOp>(getContext()); 1684 } 1685 1686 Operation *LinalgDialect::materializeConstant(OpBuilder &builder, 1687 Attribute value, Type type, 1688 Location loc) { 1689 return builder.create<arith::ConstantOp>(loc, type, value); 1690 } 1691