@@ -14,22 +14,15 @@ include "mlir/IR/EnumAttr.td"
1414include "mlir/Dialect/SparseTensor/IR/SparseTensorBase.td"
1515include "mlir/IR/TensorEncoding.td"
1616
17- // All of the Tensor attributes will extend this class.
17+ // All of the sparse tensor attributes will extend this class.
1818class SparseTensor_Attr<string name,
1919 list<Trait> traits = []>
2020 : AttrDef<SparseTensor_Dialect, name, traits>;
2121
2222//===----------------------------------------------------------------------===//
23- // Type aliases.
24- //
25- // These attributes are just like `IndexAttr` (include/mlir/IR/OpBase.td),
26- // except that:
27- // (1) the `summary` is more specific (i.e., the fourth parameter to
28- // `TypedAttrBase`), which helps tablegen provide better error messages.
29- // (2) tablegen-generated getters will have the given `returnType`, in
30- // lieu of the `APInt` that `IndexAttr` uses. This avoids the boilerplate
31- // of needing to say `get{FOO}().getZExtValue()`, as well as using
32- // C++ types which better document intent.
23+ // These attributes are just like `IndexAttr` except that they clarify whether
24+ // the index refers to a dimension (an axis of the semantic tensor) or a level
25+ // (an axis of the actual storage format).
3326//===----------------------------------------------------------------------===//
3427
3528def DimensionAttr :
@@ -107,79 +100,71 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
107100 let mnemonic = "encoding";
108101
109102 let description = [{
110- An attribute to encode TACO-style information on sparsity properties
111- of tensors. The encoding is eventually used by a **sparse compiler**
112- pass to generate sparse code fully automatically for all tensor
113- expressions that involve tensors with a sparse encoding. Compiler
114- passes that run before this sparse compiler pass need to be
115- aware of the semantics of tensor types with such an encoding.
116-
117- Each sparse tensor comes equipped with two different sets of axes for
118- describing the tensor's multi-dimensional structure. We use the term
119- "dimension" to refer to the axes of the semantic tensor itself; whereas,
120- we use the term "level" to refer to the axes of the storage scheme,
121- which is the operational representation of that tensor. Therefore,
122- the fields of the encoding attribute (further explained below) satisfy
123- the following correspondences:
124-
125- - Dimensions:
126- - the shape of the tensor type
127- - the `dimSlices` field
128- - the arguments of the `dimToLvl` field
129- - Levels:
130- - the results of the `dimToLvl` field
131- - the `lvlTypes` field
132-
133- The attribute consists of the following fields.
134-
135- - Level-type for each level of a tensor type:
136- - **dense** : all entries along this level are stored.
137- - **compressed** : only nonzeros along this level are stored.
138- - **singleton** : a variant of the compressed level-format,
139- for when coordinates are guaranteed to have no siblings at this level.
140- By default, each level-type has the property of being unique (no
141- duplicates at that level) and ordered (coordinates appear sorted
142- at that level). The following two suffixes can be used to specify
143- that the level should instead be non-unique (duplicates may appear)
144- and/or non-ordered (coordinates may appear unsorted).
145- - **-nu** : not unique
146- - **-no** : not ordered
147- Currently, these suffixes (if present) must appear in this order.
148- In the future, we may introduce additional level-types and
149- properties, and split up how the level-format and properties are
150- specified rather than using this suffix mechanism.
151-
152- - An optional affine map from dimension-coordinates to level-coordinates;
153- defaulting to the identity map. For example, given a 2-d tensor:
154- `(i, j) -> (i, j)` specifies row-wise storage, `(i, j) -> (j, i)`
155- specifies column-wise storage, and
156- `(i, j) -> (i floordiv 2, j floordiv 3, i mod 2, j mod 3)`
157- specifies 2x3 block-sparsity. For block-sparsity, blocks are typically
158- stored with compression while dense storage is used within each block
159- (although hybrid schemes are possible as well).
160-
161- (The following will be corrected in an upcoming change that completely
162- overhauls the syntax of this attribute.)
163-
164- The dimToLvl mapping also provides a notion of "counting a
165- dimension", where every stored element with the same coordinate
166- is mapped to a new slice. For instance, ELL storage of a 2-d
167- tensor can be defined with the mapping `(i, j) -> (#i, i, j)`
168- using the notation of [Chou20]. Lacking the `#` symbol in MLIR's
169- affine mapping, we use a free symbol `c` to define such counting,
170- together with a constant that denotes the number of resulting
171- slices. For example, the mapping `(i, j)[c] -> (c * 3 * i, i, j)`
172- with the level-types `["dense", "dense", "compressed"]` denotes ELL
173- storage with three jagged diagonals that count the dimension `i`.
174-
175- - The required bitwidth for "position" storage (integral offsets
103+ An attribute to encode information on sparsity properties of tensors, inspired
104+ by the TACO formalization of sparse tensors. This encoding is eventually used
105+ by a **sparsifier** pass to generate sparse code fully automatically from a
106+ sparsity-agnostic representation of the computation, i.e., an implicit sparse
107+ representation is converted to an explicit sparse representation where co-iterating
108+ loops operate on sparse storage formats rather than tensors with a sparsity
109+ encoding. Compiler passes that run before this sparse compiler pass need to
110+ be aware of the semantics of tensor types with such a sparsity encoding.
111+
112+ In this encoding, we use `dimension` to refer to the axes of the semantic tensor,
113+ and `level` to refer to the axes of the actual storage format, i.e., the
114+ operational representation of the sparse tensor in memory. The number of
115+ dimensions is usually the same as the number of levels (such as CSR storage format).
116+ However, the encoding can also map dimensions to higher-order levels (for example,
117+ to encode a block-sparse BSR storage format) or to lower-order levels
118+ (for example, to linearize dimensions as a single level in the storage).
119+
120+ The encoding contains a `map` that provides the following:
121+
122+ - An ordered sequence of dimension specifications, each of which defines:
123+ - the dimension-size (implicit from the tensor’s dimension-shape)
124+ - a **dimension-expression**
125+ - An ordered sequence of level specifications, each of which includes a required
126+ **level-type**, which defines how the level should be stored. Each level-type
127+ consists of:
128+ - a **level-format**
129+ - a collection of **level-properties** that apply to the level-format
130+ - a **level-expression**, which defines what is stored
131+
132+ Each level-expression is an affine expression over dimension-variables. Thus, the
133+ level-expressions collectively define an affine map from dimension-coordinates to
134+ level-coordinates. The dimension-expressions collectively define the inverse map,
135+ which only needs to be provided for elaborate cases where it cannot be inferred
136+ automatically. Within the sparse storage format, we refer to indices that are
137+ stored explicitly as `coordinates` and indices into the storage format as `positions`.
138+
139+ The supported level-formats are the following:
140+
141+ - **dense** : all entries along this level are stored
142+ - **compressed** : only nonzeros along this level are stored
143+ - **singleton** : a variant of the compressed format, where coordinates have no siblings
144+
145+ Different level-formats may have different collections of level-properties.
146+ By default, each level-type has the property of being unique (no duplicate
147+ coordinates at that level), ordered (coordinates appear sorted at that
148+ level), and, for compression, storing the positions in a compact way where
149+ an interval is defined by a lower bound "pos(i)" and an upper bound "pos(i+1)-1".
150+ The following properties can be added to a level-format to change this
151+ default behavior:
152+
153+ - **nonunique** : duplicate coordinates may appear at the level
154+ - **nonordered** : coordinates may appear in arbribratry order
155+ - **high** : the upper bound is stored explicitly in a separate array
156+ - **block2_4** : the compression uses a 2:4 encoding per 1x4 block
157+
158+ In addition to the `map`, the following two fields are optional:
159+
160+ - The required bitwidth for `position` storage (integral offsets
176161 into the sparse storage scheme). A narrow width reduces the memory
177162 footprint of overhead storage, as long as the width suffices to
178163 define the total required range (viz. the maximum number of stored
179164 entries over all indirection levels). The choices are `8`, `16`,
180165 `32`, `64`, or, the default, `0` to indicate the native bitwidth.
181166
182- - The required bitwidth for " coordinate" storage (the coordinates
167+ - The required bitwidth for ` coordinate` storage (the coordinates
183168 of stored entries). A narrow width reduces the memory footprint
184169 of overhead storage, as long as the width suffices to define
185170 the total required range (viz. the maximum value of each tensor
@@ -194,41 +179,46 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
194179 ```mlir
195180 // Sparse vector.
196181 #SparseVector = #sparse_tensor.encoding<{
197- map = (d0 ) -> (d0 : compressed)
182+ map = (i ) -> (i : compressed)
198183 }>
199184 ... tensor<?xf32, #SparseVector> ...
200185
201- // Sorted Coordinate Scheme .
186+ // Sorted coordinate scheme .
202187 #SortedCOO = #sparse_tensor.encoding<{
203- map = (d0, d1 ) -> (d0 : compressed(nonunique), d1 : singleton)
188+ map = (i, j ) -> (i : compressed(nonunique), j : singleton)
204189 }>
205190 ... tensor<?x?xf64, #SortedCOO> ...
206191
192+ // Batched sorted coordinate scheme, with high encoding.
193+ #BCOO = #sparse_tensor.encoding<{
194+ map = (i, j, k) -> (i : dense, j : compressed(nonunique, high), k : singleton)
195+ }>
196+ ... tensor<10x10xf32, #BCOO> ...
197+
198+ // Compressed sparse row.
199+ #CSR = #sparse_tensor.encoding<{
200+ map = (i, j) -> (i : dense, j : compressed)
201+ }>
202+ ... tensor<100x100xbf16, #CSR> ...
203+
207204 // Doubly compressed sparse column storage with specific bitwidths.
208205 #DCSC = #sparse_tensor.encoding<{
209- map = (d0, d1 ) -> (d1 : compressed, d0 : compressed),
206+ map = (i, j ) -> (j : compressed, i : compressed),
210207 posWidth = 32,
211208 crdWidth = 8
212209 }>
213210 ... tensor<8x8xf64, #DCSC> ...
214211
215212 // Block sparse row storage (2x3 blocks).
216- #BCSR = #sparse_tensor.encoding<{
213+ #BSR = #sparse_tensor.encoding<{
217214 map = ( i, j ) ->
218215 ( i floordiv 2 : compressed,
219216 j floordiv 3 : compressed,
220217 i mod 2 : dense,
221218 j mod 3 : dense
222219 )
223220 }>
224- ... tensor<20x30xf32, #BCSR> ...
225-
226- // ELL storage (4 jagged diagonals, i.e., at most 4 nonzeros per row).
227- #ELL = #sparse_tensor.encoding<{
228- lvlTypes = [ "dense", "dense", "compressed" ],
229- dimToLvl = affine_map<(i, j)[c] -> (c * 4 * i, i, j)>
230- }>
231- ... tensor<?x?xf64, #ELL> ...
221+ ... tensor<20x30xf32, #BSR> ...
232222
233223 // CSR slice (offset = 0, size = 4, stride = 1 on the first dimension;
234224 // offset = 0, size = 8, and a dynamic stride on the second dimension).
@@ -444,7 +434,6 @@ def AnyRankedSparseTensor : RankedSparseTensorOf<[AnyType]>;
444434class ScalarLikeOf<list<Type> allowedTypes>
445435 : AnyTypeOf<[0DTensorOf<allowedTypes>, AnyTypeOf<allowedTypes>]>;
446436
447-
448437//===----------------------------------------------------------------------===//
449438// Sparse Tensor Sorting Algorithm Attribute.
450439//===----------------------------------------------------------------------===//
0 commit comments