@@ -18,12 +18,17 @@ def Triton_Structured_Dialect : Dialect {
1818 }];
1919
2020 let dependentDialects = [
21- "triton::TritonDialect"
21+ "triton::TritonDialect",
22+ "tensor::TensorDialect"
2223 ];
2324
2425 let usePropertiesForAttributes = 1;
26+ let hasCanonicalizer = 1;
2527}
2628
29+ // FIXME: AnyTensor
30+ def TT_IndexTensorLike : AnyTypeOf<[I1Tensor, I8Tensor, I16Tensor, I32Tensor, I64Tensor, IndexTensor]>;
31+
2732//
2833// Op Base
2934//
@@ -121,6 +126,113 @@ def TTS_MakeTensorPtrOp
121126 //let hasCanonicalizer = 1;
122127}
123128
129+ def TTS_MakeGatherScatterTensorPtrOp
130+ : TTS_Op<"make_gather_scatter_tptr", [AttrSizedOperandSegments, Pure]> {
131+ // NOTE: Only support cases where the offset for each dimension is defined in a different operation.
132+ // Not support case where the offset is a tensor load from other ptr which for multiple dimension.
133+ //
134+ // offset_m = tl.arange(0, M)
135+ // offset_n = tl.arange(0, N)
136+ // offset_k = tl.arange(0, K)
137+ // ld_offsets = tl.load(a_ptr + offset_m[:,None]+offsets_n[None,:])
138+ // not_support = tl.load(b_ptr + ld_offsets)
139+ // not_support2 = tl.load(b_ptr + ld_offsets * (offset_m[:,None]+offsets_n[None,:]))
140+ // not_support3 = tl.load(b_ptr + (ld_offsets * (offset_m[:,None]+offsets_n[None,:]))[:, :, None] + offset_k[None,None,:])
141+ //
142+ // # Support cases where one dimension is structured while the other is not.
143+ // # For example, `offset_m[:, None] // K` is not structured, whereas `offset_n[None, :]` is structured in next line.
144+ // supported = tl.load(b_ptr + offset_m[:, None] // K + offset_n[None, :])
145+ // # `ld_offsets_1d[:, None]` is not structured, whereas `offset_n[None, :]`.
146+ // ld_offsets_1d = tl.load(a_ptr + offsets_m)
147+ // supported2 = tl.load(b_ptr + ld_offsets_1d[:, None] + offsets_n[None, :])
148+
149+ let summary = "create an pointer that points to a tensor in memory for gather/scatter";
150+ let description = [{
151+ The `tts.make_gather_scatter_tptr` operation is similar to `tts.make_tptr`.
152+ The key difference is that `make_gather_scatter_tptr` accesses the tensor non-continuously.
153+ Currently, only one dimension is allowed to be non-continuous.
154+ This dimension is saved in `gather_scatter_dim`, and the offset for that dimension is saved in `gather_scatter_offset`.
155+ Each contiguous load will load from this offset.
156+ Cases with more than one non-continuous dimension are not supported.
157+ }];
158+
159+ // base: Base pointer used to contruct the tensor of pointers or pointer to tensor.
160+ // gather_scatter_dim: The dimension for gather_scatter_offset.
161+ // sizes: Size of the data being loaded or stored.
162+ // strides: The strides of the parent tensor, which means how much to increase the pointer
163+ // by when moving by 1 element in a specific axis.
164+ // offsets: Offset of the block along each dimension from base.
165+ // result: A tensor of pointers.
166+
167+ let arguments = (ins TT_Ptr:$base,
168+ TT_IndexTensorLike:$gather_scatter_offset,
169+ I32Attr:$gather_scatter_dim,
170+ DenseI64ArrayAttr:$sizes,
171+ Variadic<Index>:$strides,
172+ Variadic<Index>:$offsets,
173+ DenseI64ArrayAttr:$static_strides,
174+ DenseI64ArrayAttr:$static_offsets,
175+ Optional<TT_BoolLike>:$gather_scatter_mask);
176+
177+ let results = (outs TT_PtrLike:$result);
178+
179+ let assemblyFormat = [{
180+ $base `to` `sizes` `` `:` $sizes
181+ `gather_scatter_dim` `` `:` $gather_scatter_dim
182+ `gather_scatter_offset` `` `:` $gather_scatter_offset
183+ (`gather_scatter_mask` `` `:` $gather_scatter_mask^)?
184+ `` `,` `strides` `` `:`
185+ custom<DynamicIndexList>($strides, $static_strides)
186+ `` `,` `offsets` `` `:`
187+ custom<DynamicIndexList>($offsets, $static_offsets)
188+ attr-dict `:` type($gather_scatter_offset) type($gather_scatter_mask) type($base) `to` type($result)
189+ }];
190+
191+
192+ let builders = [
193+ // Build with mixed static and dynamic entries.
194+ OpBuilder<(ins
195+ "Value":$base,
196+ "Value":$gather_scatter_offset,
197+ "int":$gather_scatter_dim,
198+ "ArrayRef<int64_t>":$sizes,
199+ "ArrayRef<OpFoldResult>":$strides,
200+ "ArrayRef<OpFoldResult>":$offsets)>,
201+
202+ OpBuilder<(ins
203+ "Value":$base,
204+ "Value":$gather_scatter_offset,
205+ "Value":$gather_scatter_mask,
206+ "int":$gather_scatter_dim,
207+ "ArrayRef<int64_t>":$sizes,
208+ "ArrayRef<OpFoldResult>":$strides,
209+ "ArrayRef<OpFoldResult>":$offsets)>,
210+ ];
211+
212+ let extraClassDeclaration = [{
213+ /// Return a vector of all the static or dynamic fields
214+ SmallVector<OpFoldResult> getMixedSizes() {
215+ Builder b(getContext());
216+ SmallVector<Value> dynSizes; // sizes are always static
217+ return ::mlir::getMixedValues(getSizes(), dynSizes, b);
218+ }
219+ /// Return a vector of all the static or dynamic fields
220+ SmallVector<OpFoldResult> getMixedStrides() {
221+ Builder b(getContext());
222+ return ::mlir::getMixedValues(getStaticStrides(), getStrides(), b);
223+ }
224+ SmallVector<OpFoldResult> getMixedOffsets() {
225+ Builder b(getContext());
226+ return ::mlir::getMixedValues(getStaticOffsets(), getOffsets(), b);
227+ }
228+ }];
229+
230+ let hasVerifier = 1;
231+ let hasCanonicalizer = 0;
232+ }
233+
234+ // SameVariadicResultSize
235+ // AttrSizedResultSegments
124236def TTS_GetStructuredStateOp : TTS_Op<"get_structured_state", [AttrSizedResultSegments, Pure]> {
125237 let summary = "Placeholder for the structured pointer states computed during PtrAnalysis.";
126238 let description = "Used to pass the offsets and strides to scf.for op to simplify IR rewrites.";
0 commit comments