//===- ShardOps.h - Shard Dialect Operations --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef MLIR_DIALECT_SHARD_IR_SHARDOPS_H #define MLIR_DIALECT_SHARD_IR_SHARDOPS_H #include "mlir/Bytecode/BytecodeOpInterface.h" #include "mlir/Dialect/Utils/StructuredOpsUtils.h" #include "mlir/IR/BuiltinTypeInterfaces.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/OpDefinition.h" #include "mlir/IR/PatternMatch.h" #include "mlir/IR/SymbolTable.h" #include "mlir/Interfaces/DestinationStyleOpInterface.h" #include "mlir/Interfaces/InferTypeOpInterface.h" #include "mlir/Interfaces/SideEffectInterfaces.h" #include "llvm/Support/MathExtras.h" namespace mlir { namespace shard { using GridAxis = int16_t; using GridAxesAttr = DenseI16ArrayAttr; using ShardShapeAttr = DenseI64ArrayAttr; using HaloSizePairAttr = DenseI64ArrayAttr; } // namespace shard } // namespace mlir #include "mlir/Dialect/Shard/IR/ShardEnums.h.inc" #define GET_ATTRDEF_CLASSES #include "mlir/Dialect/Shard/IR/ShardAttributes.h.inc" namespace mlir { namespace shard { class Sharding { private: ::mlir::FlatSymbolRefAttr grid; SmallVector split_axes; SmallVector static_halo_sizes; SmallVector static_sharded_dims_offsets; SmallVector dynamic_halo_sizes; SmallVector dynamic_sharded_dims_offsets; public: Sharding(::mlir::FlatSymbolRefAttr grid_ = nullptr); Sharding(Value rhs); static Sharding get(::mlir::FlatSymbolRefAttr grid_, ArrayRef split_axes_, ArrayRef static_halo_sizes_ = {}, ArrayRef static_sharded_dims_offsets_ = {}, ArrayRef dynamic_halo_sizes_ = {}, ArrayRef dynamic_sharded_dims_offsets_ = {}); ::mlir::FlatSymbolRefAttr getGridAttr() const { return grid; } ::llvm::StringRef getGrid() const { return grid ? grid.getValue() : ""; } ArrayRef getSplitAxes() const { return split_axes; } ArrayRef getStaticHaloSizes() const { return static_halo_sizes; } ArrayRef getStaticShardedDimsOffsets() const { return static_sharded_dims_offsets; } ArrayRef getDynamicHaloSizes() const { return dynamic_halo_sizes; } ArrayRef getDynamicShardedDimsOffsets() const { return dynamic_sharded_dims_offsets; } operator bool() const { return (!grid) == false; } bool operator==(Value rhs) const; bool operator!=(Value rhs) const; bool operator==(const Sharding &rhs) const; bool operator!=(const Sharding &rhs) const; bool equalSplitAxes(const Sharding &rhs) const; bool equalHaloAndShardSizes(const Sharding &rhs) const; bool equalHaloSizes(const Sharding &rhs) const; bool equalShardSizes(const Sharding &rhs) const; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Sharding &sharding); inline Diagnostic &operator<<(Diagnostic &diag, const Sharding &sharding) { std::string str; llvm::raw_string_ostream os(str); os << sharding; return diag << os.str(); } } // namespace shard } // namespace mlir #define GET_TYPEDEF_CLASSES #include "mlir/Dialect/Shard/IR/ShardTypes.h.inc" #define GET_OP_CLASSES #include "mlir/Dialect/Shard/IR/ShardOps.h.inc" namespace mlir { namespace shard { inline bool isReductionLoop(utils::IteratorType iType) { return iType == utils::IteratorType::reduction; } // Remove empty subarrays of `array` until a minimum lengh of one is reached. template void removeTrailingEmptySubArray(SmallVector> &array) { while (array.size() > 1 && array.back().empty()) array.pop_back(); } // Is the same tensor replicated on all processes. inline bool isFullReplication(Sharding sharding) { return llvm::all_of(sharding.getSplitAxes(), [](GridAxesAttr axes) { return axes.asArrayRef().empty(); }); } inline shard::GridOp getGridOrNull(Operation *op, FlatSymbolRefAttr gridSymbol, SymbolTableCollection &symbolTableCollection) { if (!gridSymbol) return nullptr; return symbolTableCollection.lookupNearestSymbolFrom( op, gridSymbol); } inline shard::GridOp getGrid(Operation *op, FlatSymbolRefAttr gridSymbol, SymbolTableCollection &symbolTableCollection) { shard::GridOp gridOp = getGridOrNull(op, gridSymbol, symbolTableCollection); assert(gridOp); return gridOp; } // Get the corresponding grid op using the standard attribute nomenclature. template shard::GridOp getGrid(Op op, SymbolTableCollection &symbolTableCollection) { return getGrid(op.getOperation(), op.getGridAttr(), symbolTableCollection); } template <> inline shard::GridOp getGrid(ShardOp op, SymbolTableCollection &symbolTableCollection) { return getGrid( op.getOperation(), cast(op.getSharding().getDefiningOp()).getGridAttr(), symbolTableCollection); } // Get the number of processes that participate in each group // induced by `gridAxes`. template int64_t collectiveProcessGroupSize(GridAxesRange &&gridAxes, GridShapeRange &&gridShape) { int64_t res = 1; for (GridAxis axis : gridAxes) { auto axisSize = *(std::begin(gridShape) + axis); if (ShapedType::isDynamic(axisSize)) { return ShapedType::kDynamic; } res *= axisSize; } return res; } template int64_t collectiveProcessGroupSize(GridAxesRange &&gridAxes, GridOp grid) { return collectiveProcessGroupSize(std::forward(gridAxes), grid.getShape()); } // Get the size of a sharded dimension. inline int64_t shardDimension(int64_t dimSize, int64_t shardCount) { if (ShapedType::isDynamic(dimSize) || ShapedType::isDynamic(shardCount)) return ShapedType::kDynamic; assert(dimSize % shardCount == 0); return dimSize / shardCount; } // Get the size of an unsharded dimension. inline int64_t gatherDimension(int64_t dimSize, int64_t shardCount) { if (ShapedType::isDynamic(dimSize) || ShapedType::isDynamic(shardCount)) return ShapedType::kDynamic; return dimSize * shardCount; } // Return the per-device sharded type for `shape` based on `sharding`. // This is the tensor shape on each grid partition. // Example: // On a 2x4x? grid with split axes = [[0], [1], [2]] the shape ?x5x1 would // result in a shape for each shard of ?x2x?. ShapedType shardShapedType(ShapedType shape, GridOp grid, Sharding sharding); // If ranked tensor type return its sharded counterpart. // // If not ranked tensor type return `type`. // `sharding` in that case must be null. Type shardType(Type type, GridOp grid, Sharding sharding); // Insert shard op if there is not one that already has the same sharding. // Use newShardOp if it is not null. Otherwise create a new one. // May insert resharding if required. // Potentially updates newShardOp. void maybeInsertTargetShardingAnnotation(Sharding sharding, OpResult result, OpBuilder &builder); void maybeInsertSourceShardingAnnotation(Sharding sharding, OpOperand &operand, OpBuilder &builder); /// Converts a vector of OpFoldResults (ints) into vector of Values of the /// provided type. SmallVector getMixedAsValues(OpBuilder b, const Location &loc, llvm::ArrayRef statics, ValueRange dynamics, Type type = Type()); } // namespace shard } // namespace mlir #endif // MLIR_DIALECT_SHARD_IR_SHARDOPS_H