Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions mlir/include/mlir/Dialect/XeGPU/IR/XeGPUAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ def DistributeLayoutAttr: AttrInterface<"DistributeLayoutAttr"> {
InterfaceMethod<"Check the availability of workgroup level layouts",
"bool",
"isForWorkgroup">,
InterfaceMethod<"Check the availability of subgroup level layouts",
"bool",
"isForSubgroup">,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: hasSgLayouts express the intenstion better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rolled back the naming after thinking it twice. I don't want to mess up it with sgLayout field.

InterfaceMethod<"Get the rank of attribute",
"int64_t",
"getRank">,
Expand All @@ -202,9 +205,21 @@ def DistributeLayoutAttr: AttrInterface<"DistributeLayoutAttr"> {
InterfaceMethod<"Get the SgData field of the attribute as integer array",
"std::optional<SmallVector<int64_t>>",
"getSgDataAsInt">,
InterfaceMethod<"Get the InstData field of the attribute as integer array",
"std::optional<SmallVector<int64_t>>",
"getInstDataAsInt">,
InterfaceMethod<"Get the LaneLayout field of the attribute as integer array",
"std::optional<SmallVector<int64_t>>",
"getLaneLayoutAsInt">,
InterfaceMethod<"Get the LaneData field of the attribute as integer array",
"std::optional<SmallVector<int64_t>>",
"getLaneDataAsInt">,
InterfaceMethod<"Derive a new layout by dropping sgLayout and sgData",
"xegpu::DistributeLayoutAttr",
"dropSgLayoutAndData">,
InterfaceMethod<"Derive a new layout by dropping InstData",
"xegpu::DistributeLayoutAttr",
"dropInstData">,
InterfaceMethod<[{Delinearizes a linear subgroup ID into its multidimensional
indices based on the effective subgroup layout.}],
"FailureOr<SmallVector<Value>>",
Expand Down Expand Up @@ -388,6 +403,24 @@ def XeGPU_LayoutAttr : XeGPUAttr<"Layout", "layout", [DistributeLayoutAttr]> {
return std::nullopt;
}

std::optional<SmallVector<int64_t>> getInstDataAsInt() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious.
Do we have cases of valid empty SmallVector?
If not, checking for std::nullopt could be replaced with a size check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it is a good suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated them to return SmallVector<int64_t> with size 0.

if (DenseI32ArrayAttr inst = getInstData())
return llvm::to_vector_of<int64_t>(inst.asArrayRef());
return std::nullopt;
}

std::optional<SmallVector<int64_t>> getLaneLayoutAsInt() const {
if (DenseI32ArrayAttr layout = getLaneLayout())
return llvm::to_vector_of<int64_t>(layout.asArrayRef());
return std::nullopt;
}

std::optional<SmallVector<int64_t>> getLaneDataAsInt() const {
if (DenseI32ArrayAttr data = getLaneData())
return llvm::to_vector_of<int64_t>(data.asArrayRef());
return std::nullopt;
}

/// Delinearizes a linear subgroup ID into its multidimensional indices
/// based on the effective subgroup layout.
FailureOr<SmallVector<Value>>
Expand Down Expand Up @@ -488,6 +521,42 @@ def XeGPU_SliceAttr : XeGPUAttr<"Slice", "slice", [DistributeLayoutAttr]> {
return std::nullopt;
}

/// Returns the InstData of the attribute, computed by applying
/// the slice dimensions to the underlying LayoutAttr.
std::optional<SmallVector<int64_t>> getInstDataAsInt() const {
SliceAttr attr = flatten();
auto parent = dyn_cast<LayoutAttr>(attr.getParent());
if (auto inst = parent.getInstDataAsInt()) {
ArrayRef<int64_t> dims = attr.getDims().asArrayRef();
return XeGPUDialect::slice(llvm::ArrayRef<int64_t>(*inst), dims);
}
return std::nullopt;
}

/// Returns the LaneLayout of the attribute, computed by applying
/// the slice dimensions to the underlying LayoutAttr.
std::optional<SmallVector<int64_t>> getLaneLayoutAsInt() const {
SliceAttr attr = flatten();
auto parent = dyn_cast<LayoutAttr>(attr.getParent());
if (auto layout = parent.getLaneLayoutAsInt()) {
ArrayRef<int64_t> dims = attr.getDims().asArrayRef();
return XeGPUDialect::slice(llvm::ArrayRef<int64_t>(*layout), dims);
}
return std::nullopt;
}

/// Returns the LaneData of the attribute, computed by applying
/// the slice dimensions to the underlying LayoutAttr.
std::optional<SmallVector<int64_t>> getLaneDataAsInt() const {
SliceAttr attr = flatten();
auto parent = dyn_cast<LayoutAttr>(attr.getParent());
if (auto data = parent.getLaneDataAsInt()) {
ArrayRef<int64_t> dims = attr.getDims().asArrayRef();
return XeGPUDialect::slice(llvm::ArrayRef<int64_t>(*data), dims);
}
return std::nullopt;
}

SliceAttr dropSgLayoutAndData() {
SliceAttr attr = flatten();
auto parent = dyn_cast<LayoutAttr>(attr.getParent());
Expand Down
2 changes: 1 addition & 1 deletion mlir/include/mlir/Dialect/XeGPU/IR/XeGPUDialect.td
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def XeGPU_Dialect : Dialect {
let extraClassDeclaration = [{
/// Checks if the given shape can be evenly distributed based on the layout
/// and data factors provided by the LayoutAttr.
static bool isEvenlyDistributable(llvm::ArrayRef<int64_t> shape, xegpu::LayoutAttr attr);
static bool isEvenlyDistributable(llvm::ArrayRef<int64_t> shape, xegpu::DistributeLayoutAttr attr);

/// drops/slices the shape in the specified dims, and return the rest. e.g.,
/// for shape = [32, 64, 8], dims = [0, 2], it will return [64]
Expand Down
4 changes: 2 additions & 2 deletions mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1162,8 +1162,8 @@ def XeGPU_ConvertLayoutOp: XeGPU_Op<"convert_layout", [Pure, AllTypesMatch<["sou
the IR is lowered to WI level because that is the end result of all distributions.
}];
let arguments = (ins XeGPU_VectorType: $source,
XeGPU_LayoutAttr: $input_layout,
XeGPU_LayoutAttr: $target_layout);
DistributeLayoutAttr: $input_layout,
DistributeLayoutAttr: $target_layout);
let results = (outs XeGPU_VectorType: $result);
let assemblyFormat = [{
$source prop-dict attr-dict `:` type($source)
Expand Down
56 changes: 35 additions & 21 deletions mlir/include/mlir/Dialect/XeGPU/Utils/XeGPUUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef MLIR_DIALECT_XEGPU_UTILS_XEGPUUTILS_H_
#define MLIR_DIALECT_XEGPU_UTILS_XEGPUUTILS_H_

#include "mlir/Dialect/XeGPU/IR/XeGPU.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/OpDefinition.h"
namespace mlir {
Expand All @@ -21,6 +22,7 @@ class ValueRange;
class TypeConverter;

namespace xegpu {
class DistributeLayoutAttr;
class LayoutAttr;
class TensorDescType;
} // namespace xegpu
Expand Down Expand Up @@ -60,46 +62,58 @@ FailureOr<VectorType> getDistributedVectorType(xegpu::TensorDescType tdescTy);
FailureOr<VectorType> getDistributedVectorType(VectorType originalType,
LayoutAttr layout);

/// Return the attribute name for the OpOperand to attach LayoutAttr
/// Return the attribute name for the OpOperand to attach DistributeLayoutAttr
std::string getLayoutName(const OpOperand &operand);

/// Return the attribute name for the OpResult to attach LayoutAttr
/// Return the attribute name for the OpResult to attach DistributeLayoutAttr
std::string getLayoutName(const OpResult result);

/// Retrieves the LayoutAttr associated with a given Value. For TensorDescType
/// values, the LayoutAttr is extracted from the TensorDescType itself. For
/// other values, it is obtained from the attributes of the defining operation.
/// Returns nullptr if no LayoutAttr is found.
LayoutAttr getLayoutAttr(const Value value);
/// Retrieves the DistributeLayoutAttr associated with a given Value. For
/// TensorDescType values, the DistributeLayoutAttr is extracted from the
/// TensorDescType itself. For other values, it is obtained from the attributes
/// of the defining operation. Returns nullptr if no DistributeLayoutAttr is
/// found.
DistributeLayoutAttr getDistributeLayoutAttr(const Value value);

/// Retrieves the LayoutAttr associated with a given OpOperand. It will
/// first check the operand_layout_{id} of the owner operation. If not found,
/// it will check the operand itself and its defining op.
LayoutAttr getLayoutAttr(const OpOperand &opr);
template <typename AttrTy>
AttrTy getDistributeLayoutAttrOfType(const Value value) {
return dyn_cast_if_present<AttrTy>(getDistributeLayoutAttr(value));
}

/// Retrieves the DistributeLayoutAttr associated with a given OpOperand. It
/// will first check the operand_layout_{id} of the owner operation. If not
/// found, it will check the operand itself and its defining op.
DistributeLayoutAttr getDistributeLayoutAttr(const OpOperand &opr);

template <typename AttrTy>
AttrTy getDistributeLayoutAttrOfType(const OpOperand &opr) {
return dyn_cast_if_present<AttrTy>(getDistributeLayoutAttr(opr));
}

/// Removes the LayoutAttr for a given OpOperand or OpResult if it exists.
template <typename T,
typename = std::enable_if_t<std::is_same_v<T, OpOperand> ||
std::is_same_v<T, OpResult>>>
void removeLayoutAttr(const T &operandOrResult);

/// Removes the LayoutAttr for each OpOperand and OpResult of the given
/// operation if they exist. If the operation contains regions, it is also
/// Removes the DistributeLayoutAttr for each OpOperand and OpResult of the
/// given operation if they exist. If the operation contains regions, it is also
/// applied recursively to the contained operations
void removeLayoutAttrs(Operation *op);

/// Sets the LayoutAttr for a given OpOperand or OpResult by attaching
/// Sets the DistributeLayoutAttr for a given OpOperand or OpResult by attaching
/// it to the owner's dictionary attributes
template <typename T,
typename = std::enable_if_t<std::is_same_v<T, OpOperand> ||
std::is_same_v<T, OpResult>>>
void setLayoutAttr(const T &operandOrResult, const LayoutAttr layout);

/// Set the LayoutAttr for each OpOperand and OpResult of the given operation.
/// If the operation contains regions, it is also applied recursively to the
/// contained operations
void setLayoutAttrs(Operation *op,
function_ref<LayoutAttr(Value)> getLayoutImpl);
void setDistributeLayoutAttr(const T &operandOrResult,
const DistributeLayoutAttr layout);

/// Set the DistributeLayoutAttr for each OpOperand and OpResult of the given
/// operation. If the operation contains regions, it is also applied recursively
/// to the contained operations
void setDistributeLayoutAttrs(
Operation *op, function_ref<DistributeLayoutAttr(Value)> getLayoutImpl);

/// Extract a set of small vectors from a value with a given shape using
/// vector.extract_stride_slice
Expand Down
27 changes: 13 additions & 14 deletions mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ genOffsetsComputingInsts(OpBuilder &builder, Location loc,
// Checks if the given shape can be evenly distributed based on the layout
// and data factors provided by the LayoutAttr.
bool XeGPUDialect::isEvenlyDistributable(llvm::ArrayRef<int64_t> shape,
xegpu::LayoutAttr attr) {
xegpu::DistributeLayoutAttr attr) {
assert(attr && "Layout attribute is missing.");

// Checks whether the given shape can be evenly distributed using the
Expand All @@ -104,52 +104,51 @@ bool XeGPUDialect::isEvenlyDistributable(llvm::ArrayRef<int64_t> shape,
// smaller than `layout[i] * data[i]`, allowing multiple compute units to
// share the data.
auto tryDistribute = [&](llvm::ArrayRef<int64_t> shape,
DenseI32ArrayAttr layout, DenseI32ArrayAttr data,
std::optional<SmallVector<int64_t>> layout,
std::optional<SmallVector<int64_t>> data,
bool rr = true) -> optional<SmallVector<int64_t>> {
llvm::SmallVector<int64_t> newShape(shape);
if (layout) {
auto vec = llvm::to_vector_of<int64_t>(layout.asArrayRef());
if (vec.size() != shape.size())
if ((*layout).size() != shape.size())
return std::nullopt;
auto ratio = computeShapeRatio(shape, vec);
auto ratio = computeShapeRatio(shape, *layout);
if (!ratio.has_value())
return std::nullopt;
newShape = ratio.value();
}

if (data) {
auto vec = llvm::to_vector_of<int64_t>(data.asArrayRef());
if (vec.size() != shape.size())
if ((*data).size() != shape.size())
return std::nullopt;
auto ratio = computeShapeRatio(newShape, vec);
auto ratio = computeShapeRatio(newShape, *data);
if (!ratio.has_value() && rr)
ratio = computeShapeRatio(vec, newShape);
ratio = computeShapeRatio(*data, newShape);
if (!ratio.has_value())
return std::nullopt;

// if data is not null, we always return it for next phase.
newShape = vec;
newShape = *data;
}
return newShape;
};

// check the sgLayout and sgData
auto maybeSgShape =
tryDistribute(shape, attr.getSgLayout(), attr.getSgData());
tryDistribute(shape, attr.getSgLayoutAsInt(), attr.getSgDataAsInt());
if (!maybeSgShape)
return false;
auto sgShape = maybeSgShape.value();

// check InstData, it neither have layout nor need round-robin
auto maybeInstShape =
tryDistribute(sgShape, nullptr, attr.getInstData(), false);
tryDistribute(sgShape, std::nullopt, attr.getInstDataAsInt(), false);
if (!maybeInstShape)
return false;
auto instShape = maybeInstShape.value();

// check LaneLayout and LaneData
auto maybeLaneShape =
tryDistribute(instShape, attr.getLaneLayout(), attr.getLaneData(), false);
auto maybeLaneShape = tryDistribute(instShape, attr.getLaneLayoutAsInt(),
attr.getLaneDataAsInt(), false);
return maybeLaneShape.has_value();
}

Expand Down
28 changes: 16 additions & 12 deletions mlir/lib/Dialect/XeGPU/Transforms/XeGPUBlocking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ struct ConvertLayoutOpPattern
using OpRewritePattern::OpRewritePattern;
LogicalResult matchAndRewrite(xegpu::ConvertLayoutOp op,
PatternRewriter &rewriter) const override {
xegpu::LayoutAttr input_layout = op.getInputLayoutAttr();
xegpu::LayoutAttr target_layout = op.getTargetLayoutAttr();
if (!input_layout.getInstData() || !target_layout.getInstData())
xegpu::DistributeLayoutAttr input_layout = op.getInputLayoutAttr();
xegpu::DistributeLayoutAttr target_layout = op.getTargetLayoutAttr();
if (!input_layout.getInstDataAsInt() || !target_layout.getInstDataAsInt())
return rewriter.notifyMatchFailure(op, "Not a target ConvertLayoutOp.");

input_layout = input_layout.dropInstData();
Expand Down Expand Up @@ -140,10 +140,11 @@ XeGPUBlockingPass::getTileShape(const T &operandOrResult) const {
else
value = (Value)operandOrResult;

xegpu::LayoutAttr layout = xegpu::getLayoutAttr(operandOrResult);
xegpu::DistributeLayoutAttr layout =
xegpu::getDistributeLayoutAttr(operandOrResult);
if (layout && layout.isForSubgroup()) {
if (auto inst_data = layout.getInstData())
return llvm::to_vector_of<int64_t>(inst_data.asArrayRef());
if (auto inst_data = layout.getInstDataAsInt())
return inst_data.value();

if (auto type = dyn_cast<ShapedType>(value.getType()))
return llvm::to_vector(type.getShape());
Expand Down Expand Up @@ -204,12 +205,14 @@ bool XeGPUBlockingPass::needsUnroll(Operation *op) const {
// skip the op if any of its operands or results has workgroup level layouts
bool hasWgLayoutOperands =
llvm::any_of(op->getOpOperands(), [](OpOperand &opr) {
xegpu::LayoutAttr layout = xegpu::getLayoutAttr(opr);
xegpu::DistributeLayoutAttr layout =
xegpu::getDistributeLayoutAttr(opr);
return layout && layout.isForWorkgroup();
});
bool hasWgLayoutResults =
llvm::any_of(op->getOpResults(), [](OpResult result) {
xegpu::LayoutAttr layout = xegpu::getLayoutAttr(result);
xegpu::DistributeLayoutAttr layout =
xegpu::getDistributeLayoutAttr(result);
return layout && layout.isForWorkgroup();
});
if (hasWgLayoutOperands || hasWgLayoutResults) {
Expand All @@ -220,8 +223,8 @@ bool XeGPUBlockingPass::needsUnroll(Operation *op) const {
auto isUnrollable = [](Value value, ArrayRef<int64_t> tileShape) {
Type valTy = value.getType();
if (auto tdescTy = dyn_cast<xegpu::TensorDescType>(valTy)) {
xegpu::LayoutAttr layout = tdescTy.getLayoutAttr();
return layout && layout.getInstData();
xegpu::DistributeLayoutAttr layout = tdescTy.getLayoutAttr();
return layout && layout.getInstDataAsInt();
}
auto shapedType = dyn_cast<ShapedType>(valTy);
return shapedType && !llvm::equal(tileShape, shapedType.getShape());
Expand All @@ -247,7 +250,8 @@ void XeGPUBlockingPass::runOnOperation() {
// Preserve the LayoutAttr for each operand to the owner's DictionaryAttr.
// This ensures that the LayoutAttr remains accessible even if the defining
// operation is replaced.
xegpu::setLayoutAttrs(op, [](Value v) { return xegpu::getLayoutAttr(v); });
xegpu::setDistributeLayoutAttrs(
op, [](Value v) { return xegpu::getDistributeLayoutAttr(v); });

auto getTileShapeAndCount = [](llvm::ArrayRef<int64_t> shape,
xegpu::LayoutAttr layout) {
Expand Down Expand Up @@ -377,7 +381,7 @@ void XeGPUBlockingPass::runOnOperation() {
if (auto layout = op->getAttrOfType<xegpu::LayoutAttr>(name)) {
op->removeAttr(name);
if (!isa<LoopLikeOpInterface>(op))
xegpu::setLayoutAttr(result, layout.dropInstData());
xegpu::setDistributeLayoutAttr(result, layout.dropInstData());
}
}

Expand Down
4 changes: 2 additions & 2 deletions mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ static LogicalResult updateOp(mlir::OpBuilder &builder, mlir::Operation *op,
}
// If the result is a vector type, add a temporary layout attribute to the
// op.
xegpu::setLayoutAttr(result, layout);
xegpu::setDistributeLayoutAttr(result, layout);
}
return success();
}
Expand Down Expand Up @@ -800,7 +800,7 @@ updateControlFlowOps(mlir::OpBuilder &builder,
// If the type is a vector type and this region argument is an OpResult,
// set the layout attribute on the OpResult.
if (auto result = dyn_cast<OpResult>(successorInput))
xegpu::setLayoutAttr(result, successorOperandLayout);
xegpu::setDistributeLayoutAttr(result, successorOperandLayout);
}
}
return success();
Expand Down
Loading