Skip to content

Commit 83a98fd

Browse files
[Tsingmicro] Sync backend code (#59)
* [BACKEND] Sync tsingmicro code * [HINT] Rollback --------- Co-authored-by: chengwei <chengwei@tsingmicro.com>
1 parent 9a1b135 commit 83a98fd

14 files changed

Lines changed: 1106 additions & 173 deletions

File tree

include/triton-shared/Analysis/MaskAnalysis.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,23 @@ struct dimInfo {
8585
//
8686
// Example of creating 2D mask:
8787
// mask = (rows[:, None] < M) & (cols[None, :] < N)
88+
//
89+
// NOTE: Bool tensor mask could be saved into dims and record the dim in
90+
// nonContinuousDim in case that dimension failed MaskAnalysis.
91+
// These is to allow case where only one dimension failed while others passed. A
92+
// MakeGatherScatterTensorPtrOp operation could be generated for the failed
93+
// dimension. Only 3 patterns are supported for this.
94+
// 1. offsets[:, None] < n where the offsets is 1d tensor.
95+
// It will in pattern of expandDims -> broadcast -> cmp
96+
// 2. mask[:, None] where mask is 1d bool tensor.
97+
// It will in pattern of cmp -> expandDims -> broadcast
98+
// 3. scalar_mask[:, None] where scalar mask is scalar bool.
99+
// It will in pattern of splat -> expandDims -> broadcast
100+
// These 3 patterns are only about how a bool tensor was created from 1D or
101+
// scalar bool. How the 1D and scalar bool were created is not important for the
102+
// unstructured mask.
103+
// Only one tensor mask is allowed. If multiple dimensions have failed
104+
// MaskAnalysis, then MaskAnalysis will still fail on the current operation.
88105
struct MaskState {
89106
OpFoldResult start;
90107
OpFoldResult end;
@@ -93,13 +110,24 @@ struct MaskState {
93110
const bool useUnsafeMask;
94111
///ASCEND
95112
SmallVector<dimInfo> stateInfo;
96-
113+
114+
SmallVector<int64_t> nonContinuousDim;
97115
void dump() const;
98116

99117
MaskState(bool useUnsafeMask = false) : useUnsafeMask(useUnsafeMask) {}
100118

101119
int64_t getRank() const { return dims.size(); }
102120

121+
bool hasNonContinuousDim() const { return !nonContinuousDim.empty(); }
122+
123+
std::optional<int64_t> getNonContinuousDim() const {
124+
if (hasNonContinuousDim()) {
125+
assert(nonContinuousDim.size() == 1);
126+
return nonContinuousDim[0];
127+
}
128+
return std::nullopt;
129+
}
130+
103131
bool isEmpty() const { return getRank() == 0 && !scalar && !start && !end; }
104132

105133
bool isMask() const { return !start && !end && !scalar && dims.size() != 0; }

include/triton-shared/Analysis/OpFoldResultUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Value materializeValue(OpBuilder &builder, Location loc, OpFoldResult ofr);
2424
// result of an operation too.
2525
std::optional<int64_t> getIntAttr(const OpFoldResult ofr);
2626

27+
std::optional<int64_t> getConstValue(const OpFoldResult ofr);
28+
2729
// Return if ofr contains a constant zero, either represented by an integer
2830
// attribute or a constant value.
2931
bool hasConstZero(const OpFoldResult ofr);

include/triton-shared/AnalysisStructured/PtrAnalysis.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ const extern std::string ptrAnalysisAttr;
3838
// shape field means the same field as tt.make_tensor_ptr; when it describes a
3939
// non-block pointer, shape field indicates how address wraps around (i.e.,
4040
// modulo); a constant 0 indicates no modulo for the dimension.
41+
// Multi-dimension PtrState, which has one unstructured dimension, is supported
42+
// for gather/scatter access. The unstructured dimension is marked by a tensor
43+
// type offset. The tensor offset for the unstructured dimension must be
44+
// expanded from a 1D tensor. The analysis will fail for multi-dimension
45+
// unstructured offsets.
4146
struct PtrState {
4247

4348
SmallVector<OpFoldResult> offsets;
@@ -57,6 +62,10 @@ struct PtrState {
5762

5863
bool dimHasModulo(uint32_t dim) const;
5964

65+
bool dimIsStructured(uint32_t dim) const;
66+
int32_t getNonStructuredDim() const;
67+
bool isStructured() const;
68+
6069
bool isBlockPtr() const;
6170

6271
void dump() const;
@@ -73,8 +82,8 @@ struct PtrState {
7382
LogicalResult mulState(const PtrState &lhsState, const PtrState &rhsState,
7483
Operation *op, OpBuilder &builder);
7584

76-
tts::MakeTensorPtrOp createTTSMakeTensorPtrOp(OpBuilder &builder,
77-
Location loc);
85+
// All dim structured or only one dim unstructured
86+
Operation *createTTSMakeTensorPtrOp(OpBuilder &builder, Location loc);
7887
};
7988

8089
class PtrAnalysis {
@@ -162,6 +171,16 @@ class PtrAnalysis {
162171
LogicalResult visitOperandRem(arith::RemSIOp mulOp, PtrState &state,
163172
const Location loc, OpBuilder &builder);
164173

174+
// Operand is the result of arith.divsi (signed integer division).
175+
// Main assumptions:
176+
// Divisor must be a constant scalar
177+
// Dividend must be 1D tensor or 2D tensor with one dimension being 1
178+
// Division is applied to offsets, strides, and scalar values
179+
// Expected result:
180+
// All offset and stride values are divided by the constant divisor
181+
LogicalResult visitOperandDivSI(arith::DivSIOp divOp, PtrState &state,
182+
const Location loc, OpBuilder &builder);
183+
165184
// Operand is the result of make_range.
166185
// Main assumptions:
167186
// start, end, and shape are all statically known

include/triton-shared/Dialect/TritonStructured/IR/TritonStructuredDialect.td

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
124236
def 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.";

lib/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ add_triton_library(TritonSharedAnalysis
1111

1212
LINK_LIBS PUBLIC
1313
MLIRAnalysis
14+
Common
1415
)

0 commit comments

Comments
 (0)