27#include "llvm/ADT/ArrayRef.h"
28#include "llvm/ADT/STLExtras.h"
29#include "llvm/ADT/SmallSet.h"
30#include "llvm/ADT/SmallVector.h"
38#define DEBUG_TYPE "shard-ops"
43#include "mlir/Dialect/Shard/IR/ShardDialect.cpp.inc"
48 static DimensionSize dynamic() {
return DimensionSize(ShapedType::kDynamic); }
49 DimensionSize(
int64_t val) : val(val) {}
50 int64_t value()
const {
return val; }
51 operator int64_t()
const {
return val; }
52 bool isDynamic()
const {
return ShapedType::isDynamic(val); }
60static DimensionSize
operator/(DimensionSize lhs, DimensionSize rhs) {
61 if (lhs.isDynamic() || rhs.isDynamic()) {
62 return DimensionSize::dynamic();
64 return lhs.value() / rhs.value();
67static DimensionSize
operator*(DimensionSize lhs, DimensionSize rhs) {
68 if (lhs.isDynamic() || rhs.isDynamic()) {
69 return DimensionSize::dynamic();
71 return lhs.value() * rhs.value();
79 auto dyn = dynamics.begin();
80 Type i64 =
b.getI64Type();
83 assert((i64 == type ||
b.getIndexType() == type) &&
84 "expected an i64 or an intex type");
85 for (
auto s : statics) {
86 if (s == ShapedType::kDynamic) {
87 values.emplace_back(*(dyn++));
89 TypedAttr val = type == i64 ?
b.getI64IntegerAttr(s) :
b.getIndexAttr(s);
90 values.emplace_back(arith::ConstantOp::create(
b, loc, type, val));
101struct ShardInlinerinterface :
public DialectInlinerInterface {
102 using DialectInlinerInterface::DialectInlinerInterface;
110 bool isLegalToInline(Operation *, Region *,
bool, IRMapping &)
const final {
120void ShardDialect::initialize() {
123#include "mlir/Dialect/Shard/IR/ShardOps.cpp.inc"
126#define GET_ATTRDEF_LIST
127#include "mlir/Dialect/Shard/IR/ShardAttributes.cpp.inc"
130#define GET_TYPEDEF_LIST
131#include "mlir/Dialect/Shard/IR/ShardTypes.cpp.inc"
133 addInterface<ShardInlinerinterface>();
139 return arith::ConstantOp::materialize(builder, value, type, loc);
149 shard::GridOp grid =
getGridOrNull(op, gridSymbol, symbolTable);
151 return op->
emitError() <<
"Undefined required grid symbol \""
158template <
typename It>
163 It next = std::next(begin);
167 for (; next != end; ++next, ++begin) {
168 if (*begin == *next) {
179 if (!
isUnique(sorted.begin(), sorted.end())) {
180 return emitError(loc) <<
"Grid axes contains duplicate elements.";
184 for (
auto axis : axes) {
185 if (axis >= rank || axis < 0) {
187 <<
"0-based grid axis index " << axis
188 <<
" is out of bounds. The referenced grid \"" << grid.getSymName()
189 <<
"\" is of rank " << rank <<
".";
196template <
typename Op>
197static FailureOr<GridOp>
210template <
typename InShape,
typename GridShape,
typename SplitAxes,
212static void shardShape(
const InShape &inShape,
const GridShape &gridShape,
213 const SplitAxes &splitAxes, OutShape &outShape,
217 if (inShape.empty()) {
218 assert(outShape.empty());
222 std::copy(llvm::adl_begin(inShape), llvm::adl_end(inShape),
223 llvm::adl_begin(outShape));
225 if (!shardedDimsOffsets.empty()) {
226 auto isDynShape = ShapedType::isDynamicShape(gridShape);
228 for (
auto [tensorAxis, innerSplitAxes] : llvm::enumerate(splitAxes)) {
229 if (!innerSplitAxes.empty()) {
230 auto sz = shardedDimsOffsets[pos];
231 bool same = !isDynShape;
236 uint64_t numShards = 0;
237 for (
auto i : innerSplitAxes.asArrayRef()) {
238 numShards += gridShape[i];
240 for (
size_t i = 1; i < numShards; ++i) {
241 if (shardedDimsOffsets[pos + i] - shardedDimsOffsets[pos + i - 1] !=
247 pos += numShards + 1;
249 outShape[tensorAxis] = same ? sz : ShapedType::kDynamic;
253 for (
auto [tensorAxis, innerSplitAxes] : llvm::enumerate(splitAxes)) {
259 if (!haloSizes.empty()) {
262 for (
auto [tensorAxis, innerSplitAxes] : llvm::enumerate(splitAxes)) {
263 if (ShapedType::isStatic(outShape[tensorAxis]) &&
264 !innerSplitAxes.empty()) {
265 if (haloSizes[haloAxis * 2] >= 0 &&
266 haloSizes[haloAxis * 2 + 1] >= 0) {
267 outShape[tensorAxis] +=
268 haloSizes[haloAxis * 2] + haloSizes[haloAxis * 2 + 1];
271 outShape[tensorAxis] = ShapedType::kDynamic;
281 using Dim = std::decay_t<
decltype(
shape.getDimSize(0))>;
286 return shape.clone(resShapeArr);
290 RankedTensorType rankedTensorType = dyn_cast<RankedTensorType>(type);
291 if (rankedTensorType && !rankedTensorType.getShape().empty()) {
301 ShardOp &newShardOp) {
304 ShardOp shardOp = dyn_cast<ShardOp>(operandOp);
305 if (shardOp && sharding == shardOp.getSharding() &&
306 !shardOp.getAnnotateForUsers()) {
309 newShardOp = shardOp;
316 ShardingOp::create(builder, operandValue.
getLoc(), sharding);
317 newShardOp = ShardOp::create(builder, operandValue.
getLoc(), operandValue,
322 newShardOp, [operandOp, operandValue](
OpOperand &use) {
323 return use.
getOwner() == operandOp && use.
get() == operandValue;
326 if (!shardOp || shardOp.getAnnotateForUsers()) {
330 auto newShardOp2 = ShardOp::create(builder, operandValue.
getLoc(), newShardOp,
331 newShardOp.getSharding(),
333 newShardOp.getResult().replaceAllUsesExcept(newShardOp2, newShardOp2);
341 for (
auto &use :
result.getUses()) {
342 uses.emplace_back(use.get(), use.getOwner());
344 for (
auto &[operandValue, operandOp] : uses) {
346 builder, newShardOp);
356 bool isBlockArg = !operandSrcOp;
358 [[maybe_unused]]
auto opType =
359 dyn_cast<mlir::RankedTensorType>(operandValue.
getType());
362 if (!isa<RankedTensorType>(operandValue.
getType()) && operandSrcOp &&
368 ShardOp shardOp = dyn_cast_or_null<ShardOp>(operandSrcOp);
370 if (shardOp && sharding == shardOp.getSharding() &&
371 shardOp.getAnnotateForUsers()) {
378 ShardingOp::create(builder, operand.
get().
getLoc(), sharding);
380 ShardOp::create(builder, operandValue.
getLoc(), operandValue, shardingOp,
384 operandValue, newShardOp, [operandOp, operandValue](
OpOperand &use) {
385 return use.
getOwner() == operandOp && use.
get() == operandValue;
388 if (isBlockArg || !shardOp || !shardOp.getAnnotateForUsers()) {
394 auto newPreceedingShardOp =
395 ShardOp::create(builder, operandValue.
getLoc(), operandValue, shardingOp,
398 newShardOp.getSrc(), newPreceedingShardOp, [&newShardOp](
OpOperand &use) {
399 return use.getOwner() == newShardOp.getOperation();
407LogicalResult GridOp::verify() {
411 return emitOpError(
"rank of grid is expected to be a positive integer");
414 if (dimSize < 0 && ShapedType::isStatic(dimSize))
415 return emitOpError(
"dimension size of a grid is expected to be "
416 "non-negative or dynamic");
436 size_t expectedResultsCount =
437 getAxes().empty() ? grid->getRank() : getAxes().size();
438 if (getResult().size() != expectedResultsCount) {
439 return emitError() <<
"Unexpected number of results " << getResult().size()
440 <<
". Expected " << expectedResultsCount <<
".";
453 build(odsBuilder, odsState,
461 assert(!axes.empty());
462 build(odsBuilder, odsState,
467void GridShapeOp::getAsmResultNames(
469 setNameFn(getResults()[0],
"grid_shape");
481 b, odsState, grid, GridAxesArrayAttr::get(
b.getContext(), splitAxes),
491 GridAxesArrayAttr::get(
b.getContext(), splitAxes),
497void ShardingOp::build(
507 b, odsState, grid, GridAxesArrayAttr::get(
b.getContext(), splitAxes),
515 build(
b, odsState, ShardingType::get(
b.getContext()), from.
getGridAttr(),
527LogicalResult ShardingOp::verify() {
528 llvm::SmallSet<GridAxis, 4> visitedAxes;
533 return emitError() <<
"grid axis is expected to be non-negative";
534 if (!visitedAxes.insert(axis).second)
535 return emitError() <<
"grid axis duplicated";
540 for (
auto subAxes : getSplitAxes().getAxes()) {
542 if (
failed(checkGridAxis(subAxesArray)))
546 if (!getStaticHaloSizes().empty() && !getStaticShardedDimsOffsets().empty()) {
547 return emitOpError(
"halo sizes and shard offsets are mutually exclusive");
550 if (!getStaticHaloSizes().empty()) {
551 auto numSplitAxes = getSplitAxes().getAxes().size();
552 for (
auto splitAxis : getSplitAxes().getAxes()) {
553 if (splitAxis.empty()) {
557 if (getStaticHaloSizes().size() != numSplitAxes * 2) {
558 return emitError() <<
"halo sizes must be specified for all split axes.";
565void ShardingOp::getAsmResultNames(
567 setNameFn(getResult(),
"sharding");
575 if (mlir::ShapedType::isDynamicShape(grid->getShape()) &&
576 !getStaticShardedDimsOffsets().empty()) {
577 return emitError() <<
"sharded dims offsets are not allowed for "
578 "device grids with dynamic shape.";
581 auto shardedDimsOffsets = getStaticShardedDimsOffsets();
582 if (!shardedDimsOffsets.empty()) {
583 auto gridShape = grid.value().getShape();
584 assert(ShapedType::isStaticShape(gridShape));
586 for (
auto [tensorAxis, innerSplitAxes] : llvm::enumerate(getSplitAxes())) {
587 if (!innerSplitAxes.empty()) {
588 int64_t numShards = 0, off = 0;
589 for (
auto i : innerSplitAxes.asArrayRef()) {
590 numShards += gridShape[i];
592 for (
int64_t i = 0; i <= numShards; ++i) {
593 if (shardedDimsOffsets.size() <= pos + i) {
594 return emitError() <<
"sharded dims offsets has wrong size.";
596 if (ShapedType::isStatic(shardedDimsOffsets[pos + i])) {
597 if (shardedDimsOffsets[pos + i] < off) {
599 <<
"sharded dims offsets must be non-decreasing.";
601 off = shardedDimsOffsets[pos + i];
604 pos += numShards + 1;
619 using OpRewritePattern<ShardingOp>::OpRewritePattern;
621 LogicalResult matchAndRewrite(ShardingOp op,
622 PatternRewriter &
b)
const override {
626 op.getDynamicShardedDimsOffsets(),
b);
633 auto staticHalos = decomposedHalos.first;
634 auto dynamicHalos = decomposedHalos.second;
636 auto staticOffs = decomposedOffs.first;
637 auto dynamicOffs = decomposedOffs.second;
639 if (dynamicHalos.empty() && !staticHalos.empty()) {
640 if (staticHalos[0] == 0 && llvm::all_equal(staticHalos)) {
651 if (dynamicOffs.empty() && !staticOffs.empty()) {
652 assert(staticOffs.size() >= 2);
653 auto diff = staticOffs[1] - staticOffs[0];
654 bool allSame = staticOffs.size() > 2;
655 for (
auto i = 2u; i < staticOffs.size(); ++i) {
656 if (staticOffs[i] - staticOffs[i - 1] != diff) {
671 b.modifyOpInPlace(op, [&]() {
672 op.setStaticHaloSizes(staticHalos);
673 op.getDynamicHaloSizesMutable().assign(dynamicHalos);
674 op.setStaticShardedDimsOffsets(staticOffs);
675 op.getDynamicShardedDimsOffsetsMutable().assign(dynamicOffs);
684 results.
add<NormalizeSharding>(context);
692 if (
getGrid() != rhs.getGrid()) {
696 auto minSize = std::min(
getSplitAxes().size(), rhs.getSplitAxes().size());
697 if (!llvm::equal(llvm::make_range(
getSplitAxes().begin(),
699 llvm::make_range(rhs.getSplitAxes().begin(),
700 rhs.getSplitAxes().begin() + minSize))) {
704 return llvm::all_of(llvm::drop_begin(
getSplitAxes(), minSize),
705 std::mem_fn(&GridAxesAttr::empty)) &&
706 llvm::all_of(llvm::drop_begin(rhs.getSplitAxes(), minSize),
707 std::mem_fn(&GridAxesAttr::empty));
715 if (rhs.getStaticShardedDimsOffsets().size() !=
718 rhs.getStaticShardedDimsOffsets())) {
721 if (rhs.getDynamicShardedDimsOffsets().size() !=
724 rhs.getDynamicShardedDimsOffsets())) {
756 os <<
"Sharding<grid=" << sharding.
getGrid() <<
", split_axes=[";
759 llvm::interleaveComma(axes.asArrayRef(), os);
764 os <<
", halo_sizes=[";
769 os <<
", sharded_dims_offsets=[";
780 auto shardingOp = rhs.getDefiningOp<ShardingOp>();
781 assert(shardingOp &&
"expected sharding op");
782 auto splitAxes = shardingOp.getSplitAxes().getAxes();
784 if (splitAxes.empty()) {
785 *
this =
Sharding(shardingOp.getGridAttr());
789 get(shardingOp.getGridAttr(), splitAxes, shardingOp.getStaticHaloSizes(),
790 shardingOp.getStaticShardedDimsOffsets(),
802 if (splitAxes.empty()) {
806 res.split_axes.resize(splitAxes.size());
807 for (
auto [i, axis] : llvm::enumerate(splitAxes)) {
811 auto clone = [](
const auto src,
auto &dst) {
812 dst.resize(src.size());
813 llvm::copy(src, dst.begin());
816 clone(staticHaloSizes, res.static_halo_sizes);
817 clone(staticShardedDimsOffsets, res.static_sharded_dims_offsets);
818 clone(dynamicHaloSizes, res.dynamic_halo_sizes);
819 clone(dynamicShardedDimsOffsets, res.dynamic_sharded_dims_offsets);
828void ShardShapeOp::getAsmResultNames(
830 setNameFn(getResult()[0],
"shard_shape");
839 build(odsBuilder, odsState, resType, dims, dimsDyn, sharding,
847void ShardOp::getAsmResultNames(
849 setNameFn(getResult(),
"sharding_annotated");
855class FoldDuplicateShardOp final :
public OpRewritePattern<ShardOp> {
857 using OpRewritePattern<ShardOp>::OpRewritePattern;
859 LogicalResult matchAndRewrite(ShardOp op, PatternRewriter &
b)
const override {
862 Value value = op.getSrc();
868 for (
auto &use : value.
getUses()) {
869 if (use.getOwner() != op.getOperation()) {
870 auto otherOp = dyn_cast<ShardOp>(use.getOwner());
872 if (!otherOp || otherOp->getBlock() != op->getBlock() ||
873 !otherOp->isBeforeInBlock(op)) {
878 Sharding currentSharding(op.getSharding());
879 Sharding otherSharding(otherOp.getSharding());
880 if (currentSharding == otherSharding) {
881 b.replaceAllUsesWith(op.getResult(), otherOp.getResult());
882 b.eraseOp(op.getOperation());
886 op, [&]() { op.getSrcMutable().assign(otherOp.getResult()); });
897void ShardOp::getCanonicalizationPatterns(mlir::RewritePatternSet &results,
898 mlir::MLIRContext *context) {
899 results.
add<FoldDuplicateShardOp>(context);
907ProcessMultiIndexOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
916 size_t expectedResultsCount =
917 getAxes().empty() ? grid->getRank() : getAxes().size();
918 if (getResult().size() != expectedResultsCount) {
919 return emitError() <<
"Unexpected number of results " << getResult().size()
920 <<
". Expected " << expectedResultsCount <<
".";
926void ProcessMultiIndexOp::build(OpBuilder &odsBuilder, OperationState &odsState,
928 build(odsBuilder, odsState,
929 SmallVector<Type>(grid.getRank(), odsBuilder.
getIndexType()),
930 grid.getSymName(), ArrayRef<GridAxis>());
933void ProcessMultiIndexOp::build(OpBuilder &odsBuilder, OperationState &odsState,
934 StringRef grid, ArrayRef<GridAxis> axes) {
935 build(odsBuilder, odsState,
936 SmallVector<Type>(axes.size(), odsBuilder.
getIndexType()), grid,
940void ProcessMultiIndexOp::getAsmResultNames(
942 setNameFn(getResults()[0],
"proc_linear_idx");
950ProcessLinearIndexOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
958void ProcessLinearIndexOp::build(OpBuilder &odsBuilder,
959 OperationState &odsState, GridOp grid) {
960 build(odsBuilder, odsState, grid.getSymName());
963void ProcessLinearIndexOp::getAsmResultNames(
965 setNameFn(getResult(),
"proc_linear_idx");
973NeighborsLinearIndicesOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
981void NeighborsLinearIndicesOp::getAsmResultNames(
983 setNameFn(getNeighborDown(),
"down_linear_idx");
984 setNameFn(getNeighborUp(),
"up_linear_idx");
993template <
typename Op>
994struct EmptyGridAxesCanonicalizationPattern : OpRewritePattern<Op> {
995 using OpRewritePattern<
Op>::OpRewritePattern;
996 LogicalResult matchAndRewrite(Op op,
997 PatternRewriter &rewriter)
const override {
998 auto gridAxes = op.getGridAxes();
999 if (!gridAxes.empty()) {
1002 if (op.getInput().getType() != op.getResult().getType()) {
1019 if (device.size() != gridAxes.size()) {
1020 return emitError(loc) <<
"In-group device \"" << deviceName
1021 <<
"\" has unexpected multi-index size "
1022 << device.size() <<
". Expected " << gridAxes.size()
1026 for (
size_t i = 0; i < device.size(); ++i) {
1027 if (ShapedType::isStatic(device[i]) &&
1028 ShapedType::isStatic(gridShape[gridAxes[i]]) &&
1029 gridShape[gridAxes[i]] <= device[i]) {
1031 <<
"Out of bounds coordinate " << i <<
" for in-group device \""
1032 << deviceName <<
"\"."
1033 <<
" Got " << device[i] <<
", but expected value in the range [0, "
1034 << (gridShape[gridAxes[i]] - 1) <<
"].";
1044 if (ShapedType::isStatic(resultDimSize) && expectedDimSize != resultDimSize) {
1045 return emitError(loc) <<
"Dimension size mismatch for result axis "
1046 << resultAxis <<
". Expected "
1047 << (ShapedType::isDynamic(expectedDimSize)
1049 : Twine(expectedDimSize))
1050 <<
", but got " << resultDimSize <<
".";
1059 auto resultRank = cast<ShapedType>(
result.getType()).getRank();
1060 if (gatherAxis < 0 || gatherAxis >= resultRank) {
1062 <<
"Gather axis " << gatherAxis <<
" is out of bounds [0, "
1063 << resultRank <<
").";
1066 ShapedType operandType = cast<ShapedType>(operand.
getType());
1067 ShapedType resultType = cast<ShapedType>(
result.getType());
1068 auto deviceGroupSize =
1070 for (
int64_t axis = 0; axis < operandType.getRank(); ++axis) {
1071 auto operandDimSize = DimensionSize(operandType.getDimSize(axis));
1072 auto resultDimSize = DimensionSize(resultType.getDimSize(axis));
1073 auto expectedResultDimSize =
1074 axis == gatherAxis ? deviceGroupSize * operandDimSize : operandDimSize;
1076 result.getLoc(), expectedResultDimSize, resultDimSize, axis))) {
1086 ShapedType operandType = cast<ShapedType>(operand.
getType());
1087 ShapedType resultType = cast<ShapedType>(
result.getType());
1088 auto operandRank = operandType.getRank();
1089 if (splitAxis < 0 || splitAxis >= operandRank) {
1091 <<
"split_axis " << splitAxis <<
" is out of bounds [0, "
1092 << operandRank <<
").";
1094 if (concatAxis < 0 || concatAxis >= operandRank) {
1096 <<
"concat_axis " << concatAxis <<
" is out of bounds [0, "
1097 << operandRank <<
").";
1099 for (
int64_t axis = 0; axis < operandType.getRank(); ++axis) {
1100 if ((axis != splitAxis && axis != concatAxis) || splitAxis == concatAxis) {
1102 result.getLoc(), operandType.getDimSize(axis),
1103 resultType.getDimSize(axis), axis))) {
1109 if (splitAxis == concatAxis) {
1113 auto deviceGroupSize =
1115 auto operandConcatDimSize = DimensionSize(operandType.getDimSize(concatAxis));
1116 auto operandSplitDimSize = DimensionSize(operandType.getDimSize(splitAxis));
1117 DimensionSize expectedResultConcatDimSize =
1118 operandConcatDimSize * deviceGroupSize;
1119 DimensionSize expectedResultSplitDimSize =
1120 operandSplitDimSize / deviceGroupSize;
1121 if (!expectedResultSplitDimSize.isDynamic() &&
1123 expectedResultSplitDimSize = DimensionSize::dynamic();
1126 result.getLoc(), expectedResultConcatDimSize.value(),
1127 resultType.getDimSize(concatAxis), concatAxis))) {
1131 result.getLoc(), expectedResultSplitDimSize.value(),
1132 resultType.getDimSize(splitAxis), splitAxis))) {
1143 ShapedType operandType = cast<ShapedType>(operand.
getType());
1144 ShapedType resultType = cast<ShapedType>(
result.getType());
1145 auto operandRank = operandType.getRank();
1146 if (tensorAxis < 0 || tensorAxis >= operandRank) {
1148 << tensorAxisAttrName <<
" " << tensorAxis
1149 <<
" is out of bounds [0, " << operandRank <<
").";
1151 for (
int64_t axis = 0; axis < operandType.getRank(); ++axis) {
1152 if (axis != tensorAxis) {
1154 result.getLoc(), operandType.getDimSize(axis),
1155 resultType.getDimSize(axis), axis))) {
1161 auto deviceGroupSize =
1163 auto operandScatterDimSize =
1164 DimensionSize(operandType.getDimSize(tensorAxis));
1165 if (!operandScatterDimSize.isDynamic() && !deviceGroupSize.isDynamic() &&
1166 int64_t(operandScatterDimSize) %
int64_t(deviceGroupSize) != 0) {
1168 <<
"Operand dimension size " <<
int64_t(operandScatterDimSize)
1169 <<
" is not divisible by collective device group size "
1170 <<
int64_t(deviceGroupSize) <<
" for tensor axis " << tensorAxis
1173 DimensionSize expectedResultTensorDimSize =
1174 operandScatterDimSize / deviceGroupSize;
1176 result.getLoc(), expectedResultTensorDimSize.value(),
1177 resultType.getDimSize(tensorAxis), tensorAxis))) {
1187 RankedTensorType operandRankedTensorType =
1188 cast<RankedTensorType>(operandType);
1189 DimensionSize operandSliceAxisSize =
1190 operandRankedTensorType.getShape()[sliceAxis];
1192 llvm::to_vector(operandRankedTensorType.getShape());
1194 resultShape[sliceAxis] =
1195 operandSliceAxisSize /
1197 return operandRankedTensorType.clone(resultShape);
1205AllGatherOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1210 auto gatherAxis = getGatherAxis().getSExtValue();
1212 gatherAxis, getGridAxes(),
1213 grid.value().getShape());
1216void AllGatherOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1217 MLIRContext *context) {
1218 patterns.
add<EmptyGridAxesCanonicalizationPattern<AllGatherOp>>(context);
1221void AllGatherOp::getAsmResultNames(
1223 setNameFn(getResult(),
"all_gather");
1231AllReduceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1235void AllReduceOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1236 MLIRContext *context) {
1237 patterns.
add<EmptyGridAxesCanonicalizationPattern<AllReduceOp>>(context);
1240void AllReduceOp::build(OpBuilder &odsBuilder, OperationState &odsState,
1241 Value input, StringRef grid,
1242 ArrayRef<GridAxis> gridAxes, ReductionKind reduction) {
1243 build(odsBuilder, odsState, input.
getType(), grid, gridAxes, input,
1247void AllReduceOp::getAsmResultNames(
1249 setNameFn(getResult(),
"all_reduce");
1256LogicalResult AllSliceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1262 getOperand(), getResult(), getSliceAxis().getSExtValue(),
"slice_axis",
1263 getGridAxes(), grid.value().getShape());
1266void AllSliceOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1267 MLIRContext *context) {
1268 patterns.
add<EmptyGridAxesCanonicalizationPattern<AllSliceOp>>(context);
1271void AllSliceOp::build(OpBuilder &odsBuilder, OperationState &odsState,
1272 Value input, GridOp grid, ArrayRef<GridAxis> gridAxes,
1273 int64_t sliceAxis) {
1275 build(odsBuilder, odsState, resultType, input, grid.getSymName(), gridAxes,
1279void AllSliceOp::build(OpBuilder &odsBuilder, OperationState &odsState,
1280 Type resultType, Value input, StringRef grid,
1281 ArrayRef<GridAxis> gridAxes, int64_t sliceAxis) {
1282 build(odsBuilder, odsState, resultType, grid, gridAxes, input,
1283 APInt(
sizeof(sliceAxis) * CHAR_BIT, sliceAxis));
1286void AllSliceOp::getAsmResultNames(
1288 setNameFn(getResult(),
"all_slice");
1295LogicalResult AllToAllOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1302 getOperand(), getResult(), getSplitAxis().getSExtValue(),
1303 getConcatAxis().getSExtValue(), getGridAxes(), grid.value().getShape());
1306void AllToAllOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1307 MLIRContext *context) {
1308 patterns.
add<EmptyGridAxesCanonicalizationPattern<AllToAllOp>>(context);
1311void AllToAllOp::getAsmResultNames(
1313 setNameFn(getResult(),
"all_to_all");
1321BroadcastOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1327 getRootDynamic(), getGridAxes(),
1328 grid.value().getShape()))) {
1335void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1336 MLIRContext *context) {
1337 patterns.
add<EmptyGridAxesCanonicalizationPattern<BroadcastOp>>(context);
1340void BroadcastOp::getAsmResultNames(
1342 setNameFn(getResult(),
"broadcast");
1349LogicalResult GatherOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1355 getRootDynamic(), getGridAxes(),
1356 grid.value().getShape()))) {
1360 auto gatherAxis = getGatherAxis().getSExtValue();
1363 grid.value().getShape());
1366void GatherOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1367 MLIRContext *context) {
1368 patterns.
add<EmptyGridAxesCanonicalizationPattern<GatherOp>>(context);
1371void GatherOp::getAsmResultNames(
1373 setNameFn(getResult(),
"gather");
1380LogicalResult RecvOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1387 getSource().value(), getSourceDynamic(),
1388 getGridAxes(), grid.value().getShape()))) {
1394void RecvOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1395 MLIRContext *context) {
1396 patterns.
add<EmptyGridAxesCanonicalizationPattern<RecvOp>>(context);
1399void RecvOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1400 setNameFn(getResult(),
"recv");
1407LogicalResult ReduceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1413 getRootDynamic(), getGridAxes(),
1414 grid.value().getShape()))) {
1421void ReduceOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1422 MLIRContext *context) {
1423 patterns.
add<EmptyGridAxesCanonicalizationPattern<ReduceOp>>(context);
1426void ReduceOp::getAsmResultNames(
1428 setNameFn(getResult(),
"reduce");
1436ReduceScatterOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1443 getOperand(), getResult(), getScatterDim().getSExtValue(),
"scatter_dim",
1444 getGridAxes(), grid.value().getShape());
1447void ReduceScatterOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1448 MLIRContext *context) {
1449 patterns.
add<EmptyGridAxesCanonicalizationPattern<ReduceScatterOp>>(context);
1452void ReduceScatterOp::getAsmResultNames(
1454 setNameFn(getResult(),
"reduce_scatter");
1461LogicalResult ScatterOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1467 getRootDynamic(), getGridAxes(),
1468 grid.value().getShape()))) {
1472 auto scatterDim = getScatterDim().getSExtValue();
1474 getInput(), getResult(), scatterDim,
"scatter_dim", getGridAxes(),
1475 grid.value().getShape());
1478void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1479 MLIRContext *context) {
1480 patterns.
add<EmptyGridAxesCanonicalizationPattern<ScatterOp>>(context);
1483void ScatterOp::getAsmResultNames(
1485 setNameFn(getResult(),
"scatter");
1492LogicalResult SendOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1498 getDestination(), getDestinationDynamic(),
1499 getGridAxes(), grid.value().getShape()))) {
1505void SendOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1506 MLIRContext *context) {
1507 patterns.
add<EmptyGridAxesCanonicalizationPattern<SendOp>>(context);
1510void SendOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1511 setNameFn(getResult(),
"send");
1518LogicalResult ShiftOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1524 auto gridAxes = getGridAxes();
1525 auto shiftAxis = getShiftAxis().getZExtValue();
1526 if (!llvm::is_contained(gridAxes, shiftAxis)) {
1527 return emitError() <<
"Invalid shift axis " << shiftAxis
1528 <<
". It must be one of the grouping grid axes.";
1534void ShiftOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1535 MLIRContext *context) {
1540void ShiftOp::getAsmResultNames(
1542 setNameFn(getResult(),
"shift");
1550UpdateHaloOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1563#define GET_OP_CLASSES
1564#include "mlir/Dialect/Shard/IR/ShardOps.cpp.inc"
1566#define GET_ATTRDEF_CLASSES
1567#include "mlir/Dialect/Shard/IR/ShardAttributes.cpp.inc"
1569#define GET_TYPEDEF_CLASSES
1570#include "mlir/Dialect/Shard/IR/ShardTypes.cpp.inc"
1572#include "mlir/Dialect/Shard/IR/ShardEnums.cpp.inc"
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static LogicalResult verifyGatherOperandAndResultShape(Value operand, Value result, int64_t gatherAxis, ArrayRef< GridAxis > gridAxes, ArrayRef< int64_t > gridShape)
static LogicalResult verifyDimensionCompatibility(Location loc, int64_t expectedDimSize, int64_t resultDimSize, int64_t resultAxis)
static FailureOr< GridOp > getGridAndVerify(Operation *op, FlatSymbolRefAttr gridSymbol, SymbolTableCollection &symbolTable)
static LogicalResult verifyGridAxes(Location loc, ArrayRef< GridAxis > axes, GridOp grid)
static FailureOr< GridOp > getGridAndVerifyAxes(Op op, SymbolTableCollection &symbolTable)
static LogicalResult verifyAllToAllOperandAndResultShape(Value operand, Value result, int64_t splitAxis, int64_t concatAxis, ArrayRef< GridAxis > gridAxes, ArrayRef< int64_t > gridShape)
static void shardShape(const InShape &inShape, const GridShape &gridShape, const SplitAxes &splitAxes, OutShape &outShape, ArrayRef< int64_t > shardedDimsOffsets={}, ArrayRef< int64_t > haloSizes={})
static LogicalResult verifyScatterOrSliceOperandAndResultShape(Value operand, Value result, int64_t tensorAxis, StringRef tensorAxisAttrName, ArrayRef< GridAxis > gridAxes, ArrayRef< int64_t > gridShape)
static LogicalResult verifyInGroupDevice(Location loc, StringRef deviceName, ArrayRef< int64_t > device, Operation::operand_range deviceDynamic, ArrayRef< GridAxis > gridAxes, ArrayRef< int64_t > gridShape)
static RankedTensorType sliceResultType(Type operandType, GridOp grid, ArrayRef< GridAxis > gridAxes, int64_t sliceAxis)
static bool isUnique(It begin, It end)
static void maybeInsertTargetShardingAnnotationImpl(Sharding sharding, Value &operandValue, Operation *operandOp, OpBuilder &builder, ShardOp &newShardOp)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Attributes are known-constant values of operations.
MLIRContext * getContext() const
A symbol reference with a reference path containing a single element.
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
StringRef getValue() const
Returns the name of the held symbol reference.
This is a utility class for mapping one set of IR entities to another.
IRValueT get() const
Return the current value being used by this operand.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
This class represents an operand of an operation.
This is a value defined by a result of an operation.
Location getLoc()
The source location the operation was defined or derived from.
This class provides the API for a sub-set of ops that are known to be constant-like.
This provides public APIs that all operations should have.
Operation * getOperation()
Inherit getOperation from OpState.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
OperandRange operand_range
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual bool replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
This class represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
bool replaceUsesWithIf(Value newValue, function_ref< bool(OpOperand &)> shouldReplace)
Replace all uses of 'this' value with 'newValue' if the given callback returns true.
bool hasOneUse() const
Returns true if this value has exactly one use.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int16_t > content)
Operation * getOwner() const
Return the owner of this operand.
ArrayRef< Value > getDynamicShardedDimsOffsets() const
bool operator!=(Value rhs) const
bool equalShardSizes(const Sharding &rhs) const
Sharding(::mlir::FlatSymbolRefAttr grid_=nullptr)
static Sharding get(::mlir::FlatSymbolRefAttr grid_, ArrayRef< GridAxesAttr > split_axes_, ArrayRef< int64_t > static_halo_sizes_={}, ArrayRef< int64_t > static_sharded_dims_offsets_={}, ArrayRef< Value > dynamic_halo_sizes_={}, ArrayRef< Value > dynamic_sharded_dims_offsets_={})
bool equalSplitAxes(const Sharding &rhs) const
ArrayRef< int64_t > getStaticHaloSizes() const
::mlir::FlatSymbolRefAttr getGridAttr() const
::llvm::StringRef getGrid() const
bool equalHaloAndShardSizes(const Sharding &rhs) const
bool operator==(Value rhs) const
ArrayRef< Value > getDynamicHaloSizes() const
ArrayRef< int64_t > getStaticShardedDimsOffsets() const
ArrayRef< GridAxesAttr > getSplitAxes() const
bool equalHaloSizes(const Sharding &rhs) const
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const Sharding &sharding)
ShapedType shardShapedType(ShapedType shape, GridOp grid, Sharding sharding)
void maybeInsertTargetShardingAnnotation(Sharding sharding, OpResult result, OpBuilder &builder)
DenseI16ArrayAttr GridAxesAttr
shard::GridOp getGridOrNull(Operation *op, FlatSymbolRefAttr gridSymbol, SymbolTableCollection &symbolTableCollection)
bool isFullReplication(Sharding sharding)
void maybeInsertSourceShardingAnnotation(Sharding sharding, OpOperand &operand, OpBuilder &builder)
int64_t shardDimension(int64_t dimSize, int64_t shardCount)
Type shardType(Type type, GridOp grid, Sharding sharding)
SmallVector< Value > getMixedAsValues(OpBuilder b, const Location &loc, llvm::ArrayRef< int64_t > statics, ValueRange dynamics, Type type=Type())
Converts a vector of OpFoldResults (ints) into vector of Values of the provided type.
int64_t collectiveProcessGroupSize(GridAxesRange &&gridAxes, GridShapeRange &&gridShape)
Include the generated interface declarations.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
constexpr NonFloatComplex< T > operator/(const NonFloatComplex< T > &x, const NonFloatComplex< T > &y)
AffineExpr operator*(int64_t val, AffineExpr expr)
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
llvm::function_ref< Fn > function_ref
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
LogicalResult foldDynamicIndexList(SmallVectorImpl< OpFoldResult > &ofrs, bool onlyNonNegative=false, bool onlyNonZero=false)
Returns "success" when any of the elements in ofrs is a constant value.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.