Skip to content

Commit 38d1662

Browse files
authored
[MTHREADS] Fix flip op: correct multi-dim index decoding and per-dim flip logic (flagos-ai#5374)
1 parent f7c55cb commit 38d1662

1 file changed

Lines changed: 43 additions & 22 deletions

File tree

  • src/flag_gems/runtime/backend/_mthreads/ops

src/flag_gems/runtime/backend/_mthreads/ops/flip.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,47 +53,64 @@ def _can_use_triton_kernel(x: torch.Tensor) -> bool:
5353
def _flip_block_kernel(
5454
x_ptr,
5555
out_ptr,
56+
shape_ptr,
57+
strides_ptr,
58+
flip_mask_ptr,
5659
inner_size,
5760
outer_size,
58-
x_outer_stride,
59-
x_inner_stride,
61+
split: tl.constexpr,
6062
BLOCK_SIZE: tl.constexpr,
6163
):
62-
"""Flip kernel for case where innermost dims are NOT flipped.
64+
"""Flip kernel with correct multi-dimensional indexing.
6365
6466
Each program processes multiple blocks (grid-stride loop).
6567
Each block has inner_size contiguous elements.
66-
The outer index is mapped: output block i -> source block (outer_size - 1 - i).
68+
The block_id is decoded to multi-dim indices, each dim independently flipped if needed.
6769
6870
Args:
6971
x_ptr: source tensor data pointer
7072
out_ptr: output tensor data pointer
71-
inner_size: number of contiguous elements per block (non-flipped trailing dims)
72-
outer_size: number of blocks (product of flipped leading dims)
73-
x_outer_stride: stride in source between consecutive blocks (positive)
74-
x_inner_stride: stride within a source block (should be 1 for contiguous)
73+
shape_ptr: shape of the leading dims [0:split]
74+
strides_ptr: strides of the leading dims [0:split]
75+
flip_mask_ptr: boolean mask indicating which dims to flip [0:split]
76+
inner_size: number of contiguous elements per block (trailing non-flipped dims)
77+
outer_size: number of blocks (product of leading dims)
78+
split: number of leading dimensions
7579
BLOCK_SIZE: tile size for processing inner elements
7680
"""
7781
pid = tl.program_id(0)
7882
num_programs = tl.num_programs(0)
7983

8084
# Grid-stride loop over outer blocks
8185
for block_id in range(pid, outer_size, num_programs):
82-
# Map output block id to source block id (reverse for flip)
83-
src_block_id = outer_size - 1 - block_id
86+
# Decode block_id to multi-dimensional index and compute source offset
87+
# Row-major layout: decode from the last dimension backwards
88+
remaining = block_id
89+
src_offset = 0
90+
91+
for dim in range(split - 1, -1, -1):
92+
dim_size = tl.load(shape_ptr + dim)
93+
dim_stride = tl.load(strides_ptr + dim)
94+
flip_dim = tl.load(flip_mask_ptr + dim)
95+
96+
# Extract index for this dimension (in output layout)
97+
idx = remaining % dim_size
98+
remaining = remaining // dim_size
99+
100+
# Apply flip if needed for this dimension
101+
src_idx = tl.where(flip_dim, dim_size - 1 - idx, idx)
102+
103+
# Accumulate source offset
104+
src_offset += src_idx * dim_stride
84105

85-
# Compute base offsets
86-
src_base = src_block_id * x_outer_stride
87106
dst_base = block_id * inner_size
88107

89108
# Process inner elements in tiles
90109
offsets = tl.arange(0, BLOCK_SIZE)
91110
for inner_start in range(0, inner_size, BLOCK_SIZE):
92111
idx = inner_start + offsets
93112
mask = idx < inner_size
94-
# Read from source (coalesced since x_inner_stride is 1)
95-
val = tl.load(x_ptr + src_base + idx * x_inner_stride, mask=mask, other=0.0)
96-
# Write to output (coalesced, contiguous)
113+
val = tl.load(x_ptr + src_offset + idx, mask=mask, other=0.0)
97114
tl.store(
98115
out_ptr + dst_base + idx, val.to(out_ptr.dtype.element_ty), mask=mask
99116
)
@@ -160,15 +177,17 @@ def flip(x: torch.Tensor, dims) -> torch.Tensor:
160177
for i in range(split, ndim):
161178
inner_size *= shape[i]
162179

163-
# Outer size = product of leading dims (all flipped)
180+
# Outer size = product of leading dims
164181
outer_size = 1
165182
for i in range(0, split):
166183
outer_size *= shape[i]
167184

168-
# x_outer_stride = stride for the innermost flipped dimension
169-
# For contiguous tensors, this equals inner_size
170-
x_outer_stride = strides[split - 1] if split > 0 else inner_size
171-
x_inner_stride = 1 # contiguous inner block
185+
# Prepare shape, strides, and flip_mask for leading dims
186+
leading_shape = torch.tensor(shape[:split], dtype=torch.int32, device=x.device)
187+
leading_strides = torch.tensor(strides[:split], dtype=torch.int32, device=x.device)
188+
leading_flip_mask = torch.tensor(
189+
[i in flip_set for i in range(split)], dtype=torch.bool, device=x.device
190+
)
172191

173192
out = torch.empty_like(x)
174193

@@ -180,9 +199,11 @@ def flip(x: torch.Tensor, dims) -> torch.Tensor:
180199
_flip_block_kernel[grid](
181200
x,
182201
out,
202+
leading_shape,
203+
leading_strides,
204+
leading_flip_mask,
183205
inner_size,
184206
outer_size,
185-
x_outer_stride,
186-
x_inner_stride,
207+
split,
187208
)
188209
return out

0 commit comments

Comments
 (0)