diff --git a/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td b/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td --- a/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td +++ b/mlir/include/mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.td @@ -1527,7 +1527,8 @@ let arguments = (ins TransformHandleTypeInterface:$target, Variadic:$dynamic_sizes, DefaultValuedOptionalAttr:$static_sizes, - DefaultValuedOptionalAttr:$interchange); + DefaultValuedOptionalAttr:$interchange, + DefaultValuedOptionalAttr:$scalable); let results = (outs TransformHandleTypeInterface:$tiled_linalg_op, Variadic:$loops); let builders = [ diff --git a/mlir/include/mlir/Interfaces/ViewLikeInterface.h b/mlir/include/mlir/Interfaces/ViewLikeInterface.h --- a/mlir/include/mlir/Interfaces/ViewLikeInterface.h +++ b/mlir/include/mlir/Interfaces/ViewLikeInterface.h @@ -75,15 +75,16 @@ ParseResult parseDynamicIndexList( OpAsmParser &parser, SmallVectorImpl &values, - DenseI64ArrayAttr &integers, SmallVectorImpl *valueTypes = nullptr, + DenseI64ArrayAttr &integers, bool *scalable = nullptr, + SmallVectorImpl *valueTypes = nullptr, AsmParser::Delimiter delimiter = AsmParser::Delimiter::Square); inline ParseResult parseDynamicIndexList( OpAsmParser &parser, SmallVectorImpl &values, DenseI64ArrayAttr &integers, SmallVectorImpl &valueTypes, AsmParser::Delimiter delimiter = AsmParser::Delimiter::Square) { - return parseDynamicIndexList(parser, values, integers, &valueTypes, - delimiter); + return parseDynamicIndexList(parser, values, integers, + /*scalable=*/nullptr, &valueTypes, delimiter); } /// Verify that a the `values` has as many elements as the number of entries in diff --git a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp --- a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp +++ b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp @@ -2254,6 +2254,7 @@ SmallVector tiled; SmallVector, 4> loops; loops.resize(getLoops().size()); + bool scalable = getScalable(); for (auto [i, op] : llvm::enumerate(targets)) { auto tilingInterface = dyn_cast(op); auto dpsInterface = dyn_cast(op); @@ -2274,8 +2275,16 @@ unsigned dynamicIdx = 0; for (OpFoldResult ofr : getMixedSizes()) { if (auto attr = ofr.dyn_cast()) { - sizes.push_back(b.create( - getLoc(), cast(attr).getInt())); + if (scalable) { + auto val = b.create( + getLoc(), attr.cast().getInt()); + Value vscale = + b.create(getLoc(), b.getIndexType()); + sizes.push_back(b.create(getLoc(), val, vscale)); + } else { + sizes.push_back(b.create( + getLoc(), cast(attr).getInt())); + } continue; } ArrayRef dynamicSizes = dynamicSizeProducers[dynamicIdx]; @@ -2370,8 +2379,9 @@ DenseI64ArrayAttr staticSizes; FunctionType functionalType; llvm::SMLoc operandLoc; + bool scalable = false; if (parser.parseOperand(target) || parser.getCurrentLocation(&operandLoc) || - parseDynamicIndexList(parser, dynamicSizes, staticSizes) || + parseDynamicIndexList(parser, dynamicSizes, staticSizes, &scalable) || parseOptionalInterchange(parser, result) || parser.parseColonType(functionalType)) return ParseResult::failure(); @@ -2394,6 +2404,9 @@ return failure(); } + auto scalableAttr = parser.getBuilder().getBoolAttr(scalable); + result.addAttribute(getScalableAttrName(result.name), scalableAttr); + result.addAttribute(getStaticSizesAttrName(result.name), staticSizes); result.addTypes(functionalType.getResults()); return success(); diff --git a/mlir/lib/Dialect/SCF/IR/SCF.cpp b/mlir/lib/Dialect/SCF/IR/SCF.cpp --- a/mlir/lib/Dialect/SCF/IR/SCF.cpp +++ b/mlir/lib/Dialect/SCF/IR/SCF.cpp @@ -1261,9 +1261,9 @@ dynamicSteps; if (succeeded(parser.parseOptionalKeyword("in"))) { // Parse upper bounds. - if (parseDynamicIndexList(parser, dynamicUbs, staticUbs, - /*valueTypes=*/nullptr, - OpAsmParser::Delimiter::Paren) || + if (parseDynamicIndexList( + parser, dynamicUbs, staticUbs, /*scalable=*/nullptr, + /*valueTypes=*/nullptr, OpAsmParser::Delimiter::Paren) || parser.resolveOperands(dynamicUbs, indexType, result.operands)) return failure(); @@ -1273,26 +1273,26 @@ } else { // Parse lower bounds. if (parser.parseEqual() || - parseDynamicIndexList(parser, dynamicLbs, staticLbs, - /*valueTypes=*/nullptr, - OpAsmParser::Delimiter::Paren) || + parseDynamicIndexList( + parser, dynamicLbs, staticLbs, /*scalable=*/nullptr, + /*valueTypes=*/nullptr, OpAsmParser::Delimiter::Paren) || parser.resolveOperands(dynamicLbs, indexType, result.operands)) return failure(); // Parse upper bounds. if (parser.parseKeyword("to") || - parseDynamicIndexList(parser, dynamicUbs, staticUbs, - /*valueTypes=*/nullptr, - OpAsmParser::Delimiter::Paren) || + parseDynamicIndexList( + parser, dynamicUbs, staticUbs, /*scalable=*/nullptr, + /*valueTypes=*/nullptr, OpAsmParser::Delimiter::Paren) || parser.resolveOperands(dynamicUbs, indexType, result.operands)) return failure(); // Parse step values. if (parser.parseKeyword("step") || - parseDynamicIndexList(parser, dynamicSteps, staticSteps, - /*valueTypes=*/nullptr, - OpAsmParser::Delimiter::Paren) || + parseDynamicIndexList( + parser, dynamicSteps, staticSteps, /*scalable=*/nullptr, + /*valueTypes=*/nullptr, OpAsmParser::Delimiter::Paren) || parser.resolveOperands(dynamicSteps, indexType, result.operands)) return failure(); } diff --git a/mlir/lib/Dialect/Transform/Utils/Utils.cpp b/mlir/lib/Dialect/Transform/Utils/Utils.cpp --- a/mlir/lib/Dialect/Transform/Utils/Utils.cpp +++ b/mlir/lib/Dialect/Transform/Utils/Utils.cpp @@ -42,5 +42,6 @@ return success(); } - return parseDynamicIndexList(parser, values, integers, &valueTypes); + return parseDynamicIndexList(parser, values, integers, /*scalable=*/nullptr, + &valueTypes); } diff --git a/mlir/lib/Interfaces/ViewLikeInterface.cpp b/mlir/lib/Interfaces/ViewLikeInterface.cpp --- a/mlir/lib/Interfaces/ViewLikeInterface.cpp +++ b/mlir/lib/Interfaces/ViewLikeInterface.cpp @@ -128,13 +128,17 @@ ParseResult mlir::parseDynamicIndexList( OpAsmParser &parser, SmallVectorImpl &values, - DenseI64ArrayAttr &integers, SmallVectorImpl *valueTypes, - AsmParser::Delimiter delimiter) { + DenseI64ArrayAttr &integers, bool *returnScalableVal, + SmallVectorImpl *valueTypes, AsmParser::Delimiter delimiter) { SmallVector integerVals; + bool isScalable = false; auto parseIntegerOrValue = [&]() { OpAsmParser::UnresolvedOperand operand; auto res = parser.parseOptionalOperand(operand); + + if (returnScalableVal && parser.parseOptionalLSquare().succeeded()) + isScalable = true; if (res.has_value() && succeeded(res.value())) { values.push_back(operand); integerVals.push_back(ShapedType::kDynamic); @@ -146,6 +150,8 @@ return failure(); integerVals.push_back(integer); } + if (isScalable && parser.parseOptionalRSquare().failed()) + return failure(); return success(); }; if (parser.parseCommaSeparatedList(delimiter, parseIntegerOrValue, @@ -153,6 +159,8 @@ return parser.emitError(parser.getNameLoc()) << "expected SSA value or integer"; integers = parser.getBuilder().getDenseI64ArrayAttr(integerVals); + if (returnScalableVal) + *returnScalableVal = isScalable; return success(); } diff --git a/mlir/test/Dialect/Linalg/transform-op-tile.mlir b/mlir/test/Dialect/Linalg/transform-op-tile.mlir --- a/mlir/test/Dialect/Linalg/transform-op-tile.mlir +++ b/mlir/test/Dialect/Linalg/transform-op-tile.mlir @@ -149,3 +149,60 @@ transform.structured.tile_to_forall_op %0 tile_sizes[1, 1] : (!transform.any_op) -> (!transform.any_op, !transform.any_op) } + +// ----- + +#map = affine_map<(d0) -> (d0)> + +module { + func.func @scalable_tile(%arg0: tensor, %arg1: tensor, %arg2: tensor, %arg3: f32) -> tensor { + %0 = linalg.generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel"]} ins(%arg0, %arg1 : tensor, tensor) outs(%arg2 : tensor) { + ^bb0(%in_1: f32, %in_2: f32, %out: f32): + %1 = arith.addf %in_1, %in_2 : f32 + %2 = arith.mulf %arg3, %1 : f32 + linalg.yield %2 : f32 + } -> tensor + return %0 : tensor + } +} + +// CHECK-LABEL: func.func @scalable_tile( +// CHECK-SAME: %[[VAL_0:.*]]: tensor, %[[VAL_1:.*]]: tensor, %[[VAL_2:.*]]: tensor, +// CHECK-SAME: %[[VAL_3:.*]]: f32) -> tensor { +// CHECK: %[[VAL_4:.*]] = arith.constant 0 : index +// CHECK: %[[VAL_5:.*]] = tensor.dim %[[VAL_0]], %[[VAL_4]] : tensor +// CHECK: %[[VAL_6:.*]] = arith.constant 0 : index +// CHECK: %[[VAL_7:.*]] = tensor.dim %[[VAL_1]], %[[VAL_6]] : tensor +// CHECK: %[[VAL_8:.*]] = arith.constant 0 : index +// CHECK: %[[VAL_9:.*]] = tensor.dim %[[VAL_2]], %[[VAL_8]] : tensor +// CHECK: %[[VAL_10:.*]] = arith.constant 4 : index +// CHECK: %[[VAL_11:.*]] = vector.vscale +// CHECK: %[[VAL_12:.*]] = arith.muli %[[VAL_10]], %[[VAL_11]] : index +// CHECK: %[[VAL_13:.*]] = arith.constant 0 : index +// CHECK: %[[VAL_14:.*]] = scf.for %[[VAL_15:.*]] = %[[VAL_13]] to %[[VAL_5]] step %[[VAL_12]] iter_args(%[[VAL_16:.*]] = %[[VAL_2]]) -> (tensor) { +// CHECK: %[[VAL_17:.*]] = affine.min #map(%[[VAL_15]]){{\[}}%[[VAL_12]], %[[VAL_5]]] +// CHECK: %[[VAL_18:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_19:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_20:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_21:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_22:.*]] = tensor.extract_slice %[[VAL_0]]{{\[}}%[[VAL_15]]] {{\[}}%[[VAL_17]]] [1] : tensor to tensor +// CHECK: %[[VAL_23:.*]] = tensor.extract_slice %[[VAL_1]]{{\[}}%[[VAL_15]]] {{\[}}%[[VAL_17]]] [1] : tensor to tensor +// CHECK: %[[VAL_24:.*]] = tensor.extract_slice %[[VAL_16]]{{\[}}%[[VAL_15]]] {{\[}}%[[VAL_17]]] [1] : tensor to tensor +// CHECK: %[[VAL_25:.*]] = linalg.generic {indexing_maps = [#map2, #map2, #map2], iterator_types = ["parallel"]} ins(%[[VAL_22]], %[[VAL_23]] : tensor, tensor) outs(%[[VAL_24]] : tensor) { +// CHECK: ^bb0(%[[VAL_26:.*]]: f32, %[[VAL_27:.*]]: f32, %[[VAL_28:.*]]: f32): +// CHECK: %[[VAL_29:.*]] = arith.addf %[[VAL_26]], %[[VAL_27]] : f32 +// CHECK: %[[VAL_30:.*]] = arith.mulf %[[VAL_3]], %[[VAL_29]] : f32 +// CHECK: linalg.yield %[[VAL_30]] : f32 +// CHECK: } -> tensor +// CHECK: %[[VAL_31:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_32:.*]] = affine.apply #map1(%[[VAL_17]]) +// CHECK: %[[VAL_33:.*]] = tensor.insert_slice %[[VAL_34:.*]] into %[[VAL_16]]{{\[}}%[[VAL_15]]] {{\[}}%[[VAL_17]]] [1] : tensor into tensor +// CHECK: scf.yield %[[VAL_33]] : tensor +// CHECK: } +// CHECK: return %[[VAL_35:.*]] : tensor + +transform.sequence failures(propagate) { + ^bb0(%arg1: !pdl.operation): + %0 = transform.structured.match ops{["linalg.generic"]} in %arg1 : (!pdl.operation) -> !pdl.operation + %1, %loop = transform.structured.tile %0 [[4]] : (!pdl.operation) -> (!pdl.operation, !pdl.operation) +}