-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[mlir][xegpu] Add definition of SliceAttr #150146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 29 commits
2bc70b6
3959f9e
2027cfc
638c085
91048f0
7eaf0a6
ddc42c2
36e2c3a
6872e6d
ded53b4
223fab9
60e20a0
3630966
398d69b
08e4aa9
62aa1dd
de0a1bb
a483699
e7f2977
e3e4a61
4d72663
3f59105
129312a
0865612
b67f2b1
01e4efe
3077c6c
d1f7bac
27da02a
e49e1cf
59de450
1b16552
0511e1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,7 +175,38 @@ def XeGPU_FenceScopeAttr: | |
let assemblyFormat = "$value"; | ||
} | ||
|
||
def XeGPU_LayoutAttr : XeGPUAttr<"Layout", "layout"> { | ||
def LayoutTrait: AttrInterface<"LayoutTrait"> { | ||
let cppNamespace = "::mlir::xegpu"; | ||
let description = [{ | ||
Common trait for all XeGPU layouts. | ||
}]; | ||
|
||
let methods = [ | ||
InterfaceMethod<"Get the rank of attribute", | ||
"int64_t", | ||
"getRank">, | ||
InterfaceMethod<"Get the SgLayout field of the attribute as integer array", | ||
"std::optional<SmallVector<int64_t>>", | ||
"getSgLayoutAsInt">, | ||
InterfaceMethod<"Get the SgData field of the attribute as integer array", | ||
"std::optional<SmallVector<int64_t>>", | ||
"getSgDataAsInt">, | ||
InterfaceMethod<[{Delinearizes a linear subgroup ID into its multidimensional | ||
indices based on the effective subgroup layout.}], | ||
"FailureOr<SmallVector<Value>>", | ||
"delinearizeSubgroupId", | ||
(ins "OpBuilder &": $builder, "Location":$loc, "Value":$linearId)>, | ||
InterfaceMethod<[{Generates instructions to compute multidimensional offsets for blocks | ||
assigned to a subgroup identified by linearId. The shape parameter | ||
represents the workgroup-level problem size. Each subgroup may access | ||
multiple blocks according to round-robin distribution rules.}], | ||
"FailureOr<SmallVector<SmallVector<Value>>>", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the need for having a vector<vector<>> here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since each subgroup may be assigned with multiple blocks. |
||
"getOffsets", | ||
(ins "OpBuilder &": $builder, "Location":$loc, "Value":$linearId, "ArrayRef<int64_t>":$shape)> | ||
]; | ||
} | ||
|
||
def XeGPU_LayoutAttr : XeGPUAttr<"Layout", "layout", [LayoutTrait]> { | ||
let summary = [{ | ||
Describes the data distribution to subgroups and work-items for a tensor | ||
specified by the tensor descriptor. | ||
|
@@ -330,12 +361,143 @@ def XeGPU_LayoutAttr : XeGPUAttr<"Layout", "layout"> { | |
return LayoutAttr::get(getContext(), getSgLayout(), getSgData(), nullptr, | ||
getLaneLayout(), getLaneData(), getOrder()); | ||
} | ||
|
||
std::optional<SmallVector<int64_t>> getSgLayoutAsInt() const { | ||
if (DenseI32ArrayAttr layout = getSgLayout()) | ||
return llvm::to_vector_of<int64_t>(layout.asArrayRef()); | ||
return std::nullopt; | ||
} | ||
|
||
std::optional<SmallVector<int64_t>> getSgDataAsInt() const { | ||
if (DenseI32ArrayAttr data = getSgData()) | ||
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>> | ||
delinearizeSubgroupId(OpBuilder &builder, Location loc, Value linearId); | ||
|
||
/// Generates instructions to compute multidimensional offsets for blocks | ||
/// assigned to a subgroup identified by linearId. The shape parameter | ||
/// represents the workgroup-level problem size. Each subgroup may access | ||
/// multiple blocks according to round-robin distribution rules. | ||
FailureOr<SmallVector<SmallVector<Value>>> | ||
getOffsets(OpBuilder &builder, Location loc, Value linearId, ArrayRef<int64_t> shape); | ||
|
||
}]; | ||
|
||
let assemblyFormat = "`<` struct(params) `>`"; | ||
let genVerifyDecl = 1; | ||
} | ||
|
||
|
||
def XeGPU_SliceAttr : XeGPUAttr<"Slice", "slice", [LayoutTrait]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is desirable to allow nested slice attribute to match the staged reduction use case, where reduction may follow another reduction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will add it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the support for nested SliceAttr has been enabled. @Jianhui-Li |
||
let summary = [{Describes the data distribution and sharing among subgroups or work-items.}]; | ||
|
||
let description = [{ | ||
Like LayoutAttr, SliceAttr describes data distribution among subgroups or work-items. | ||
However, whereas LayoutAttr requires the data to have the same rank as the attribute, | ||
SliceAttr permits the data to have a lower rank. In this case, compute units in the | ||
specified dimensions (given by `$dims`) share the data, provided that the remaining | ||
ranks match the data rank. SliceAttr is commonly used by operations such as | ||
vector.multi_reduction and vector.broadcast. | ||
|
||
Example: | ||
``` | ||
#l = #xegpu.layout<sg_layout = [8, 4], sg_data = [32, 32]> | ||
#r = #xegpu.slice<#l, dim = [0]> | ||
|
||
%exp = math.exp %input {layout_result_0 = #l}: vector<256x128xf32> | ||
%red = vector.multi_reduction<add>, %exp, %acc [0] {layout_result_0 = #r}: vector<256x128xf32> to vector<128xf32> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better to add a comment here explaning the output layout of
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated it |
||
%bcast = vector.broadcast %red {layout_result_0 = #l} : vector<128xf32> to vector<256x128xf32> | ||
``` | ||
In this example, %red is conceptually divided into 4 vectors of type vector<32xf32>, each assigned to | ||
a group of subgroups. Each group consists of 8 subgroups from the same column of sg_layout, sharing a | ||
single reduction result of type vector<32xf32>. | ||
|
||
}]; | ||
|
||
let parameters = (ins | ||
"xegpu::LayoutTrait": $parent, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is parent a trait and not LayoutAttr? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is to support using both LayoutAttr and SliceAttr as parent. The later one is nested definition. |
||
"DenseI64ArrayAttr": $dims | ||
); | ||
|
||
let extraClassDeclaration = [{ | ||
|
||
int64_t getRank() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
return parent.getRank() - attr.getDims().size(); | ||
} | ||
|
||
DenseI32ArrayAttr getOrder() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
return parent.getOrder(); | ||
} | ||
|
||
bool isWgLayout() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
return parent.isWgLayout(); | ||
} | ||
|
||
bool isSgLayout() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
return parent.isSgLayout(); | ||
} | ||
|
||
/// Returns the SgLayout of the attribute, computed by applying | ||
/// the slice dimensions to the underlying LayoutAttr. | ||
std::optional<SmallVector<int64_t>> getSgLayoutAsInt() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
if (auto layout = parent.getSgLayoutAsInt()) { | ||
ArrayRef<int64_t> dims = attr.getDims().asArrayRef(); | ||
return XeGPUDialect::slice(llvm::ArrayRef<int64_t>(*layout), dims); | ||
} | ||
return std::nullopt; | ||
} | ||
|
||
/// Returns the SgData of the attribute, computed by applying | ||
/// the slice dimensions to the underlying LayoutAttr. | ||
std::optional<SmallVector<int64_t>> getSgDataAsInt() const { | ||
SliceAttr attr = flatten(); | ||
auto parent = dyn_cast<LayoutAttr>(attr.getParent()); | ||
if (auto data = parent.getSgDataAsInt()) { | ||
ArrayRef<int64_t> dims = attr.getDims().asArrayRef(); | ||
return XeGPUDialect::slice(llvm::ArrayRef<int64_t>(*data), dims); | ||
} | ||
return std::nullopt; | ||
} | ||
|
||
/// flatten a nested SliceAttr, e.g., for 2-level nested SliceAttr | ||
/// #xegpu.slice<#xegpu.slice<#xegpu.layout<sg_layout = [4, 8, 12]>, dims = [0]>, dims = [0]> | ||
/// it will coalese two slice operations and return a simplified SliceAttr | ||
/// #xegpu.slice<#xegpu.layout<sg_layout = [4, 8, 12]>, dims = [0, 1]> | ||
SliceAttr flatten() const; | ||
|
||
/// Delinearizes a linear subgroup ID into its multidimensional indices | ||
/// based on the effective subgroup layout. | ||
FailureOr<SmallVector<Value>> | ||
delinearizeSubgroupId(OpBuilder &builder, Location loc, Value linearId); | ||
|
||
/// Generates instructions to compute multidimensional offsets for blocks | ||
/// assigned to a subgroup identified by linearId. The shape parameter | ||
/// represents the workgroup-level problem size. Each subgroup may access | ||
/// multiple blocks according to round-robin distribution rules. | ||
FailureOr<SmallVector<SmallVector<Value>>> | ||
getOffsets(OpBuilder &builder, Location loc, Value linearId, ArrayRef<int64_t> shape); | ||
|
||
}]; | ||
|
||
let assemblyFormat = "`<` qualified($parent) `,` `dims` `=` $dims `>`"; | ||
let genVerifyDecl = 1; | ||
} | ||
|
||
def XeGPU_RangeAttr : XeGPUAttr<"Range", "range"> { | ||
let summary = [{Specifies a half-open range}]; | ||
let description = [{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,18 @@ def XeGPU_Dialect : Dialect { | |
/// 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); | ||
|
||
/// 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] | ||
template<typename T, typename U> | ||
static llvm::SmallVector<T> slice(llvm::ArrayRef<T> shape, llvm::ArrayRef<U> dims) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like this unrelated to XeGPUDialect. any reason for placing here? can it be moved to Utils? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving it to Utils will incur recursive dependence for linker. |
||
llvm::SmallVector<T> result; | ||
for (auto [i, v]: llvm::enumerate(shape)) { | ||
if (!llvm::is_contained(dims, i)) | ||
result.push_back(v); | ||
} | ||
return result; | ||
} | ||
}]; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,11 +7,14 @@ add_mlir_dialect_library(MLIRXeGPUDialect | |
|
||
DEPENDS | ||
MLIRXeGPUIncGen | ||
MLIRXeGPUAttrInterfaceIncGen | ||
MLIRXeGPUAttrsIncGen | ||
MLIRXeGPUEnumsIncGen | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dont you need to link MLIRIndexDialect ,MLIRAffineUtils here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Yes, they are needed. |
||
LINK_LIBS PUBLIC | ||
MLIRArithDialect | ||
MLIRIndexDialect | ||
MLIRAffineUtils | ||
MLIRArithUtils | ||
MLIRDialectUtils | ||
MLIRIR | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.