[Mlir-commits] [mlir] 1fadc09 - [MLIR][XeGPU] Fix order remapping in layout transpose (#205212)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Jun 24 17:51:40 PDT 2026


Author: Jianhui Li
Date: 2026-06-24T17:51:35-07:00
New Revision: 1fadc09c1985a6f3bb0ff993fc876384797116a3

URL: https://github.com/llvm/llvm-project/commit/1fadc09c1985a6f3bb0ff993fc876384797116a3
DIFF: https://github.com/llvm/llvm-project/commit/1fadc09c1985a6f3bb0ff993fc876384797116a3.diff

LOG: [MLIR][XeGPU] Fix order remapping in layout transpose (#205212)

LayoutAttr::transposeDims and LayoutAttr::isTransposeOf mishandled the
`order` field when transposing a layout. The `order` field is
fundamentally different from the size-valued fields (sg_layout, sg_data,
inst_data, lane_layout, lane_data): its values are dimension indices
(order[0] is the fastest-varying dim), not per-position sizes. The two
require different transpose rules:
    - Size fields — reindex by position: new[i] = orig[perm[i]]
- order — relabel values through the inverse permutation: newOrder[i] =
inversePerm[origOrder[i]]

Both functions incorrectly applied the size-field rule to `order`.
Because the bug was applied consistently in both places, it stayed
hidden for trivial/symmetric (e.g. 2D [1,0]) permutations, where the two
rules happen to coincide. It only surfaces for non-trivial permutations
such as the 3D [1,0,2] produced by a broadcast→transpose chain.
   
   Assist-by-Claude

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply at anthropic.com>

Added: 
    

Modified: 
    mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
    mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp b/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
index 311cf9a64c0c4..5d55342afea15 100644
--- a/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
+++ b/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
@@ -933,8 +933,22 @@ DistributeLayoutAttr LayoutAttr::transposeDims(ArrayRef<int64_t> permutation) {
       sgLayout.push_back(static_cast<int32_t>(origSgLayout[idx]));
       sgData.push_back(static_cast<int32_t>(origSgData[idx]));
     }
-    order.push_back(static_cast<int32_t>(origOrder[idx]));
   }
+
+  // `order` is distinct from the size-valued fields above: its *values* are
+  // dimension indices (order[0] is the fastest-varying dim), not per-position
+  // sizes. A transpose relabels dimensions (source dim d becomes result dim
+  // inversePerm[d]) so the dimension values are remapped through the inverse
+  // permutation: newOrder[i] = inversePerm[origOrder[i]].
+  //
+  // The linearization order this describes is invariant under transpose: a
+  // transpose only renames dimensions, so the subgroup ID assigned to a given
+  // block of data must stay the same. Remapping the values through the inverse
+  // permutation is exactly what preserves that order.
+  SmallVector<int64_t> inversePermutation =
+      invertPermutationVector(permutation);
+  for (int64_t dim : origOrder)
+    order.push_back(static_cast<int32_t>(inversePermutation[dim]));
   if (origLaneLayout.empty() && origSgLayout.empty())
     order.clear();
 
@@ -968,13 +982,30 @@ bool LayoutAttr::isTransposeOf(const xegpu::DistributeLayoutAttr &other,
     }
     return true;
   };
+  // `order` is 
diff erent: its *values* are dimension indices, so a transpose
+  // relabels them through the inverse permutation rather than reindexing by
+  // position. `this` (= dst) is a transpose of `other` (= src) iff
+  // dst.order[i] == inversePerm[src.order[i]] for all i. This matches the
+  // convention produced by `transposeDims`.
+  auto checkOrderTranspose = [](ArrayRef<int64_t> dstOrder,
+                                ArrayRef<int64_t> srcOrder,
+                                ArrayRef<int64_t> perm) {
+    if (dstOrder.size() != srcOrder.size())
+      return false;
+    SmallVector<int64_t> inversePerm = invertPermutationVector(perm);
+    for (auto [d, s] : llvm::zip_equal(dstOrder, srcOrder)) {
+      if (d != inversePerm[s])
+        return false;
+    }
+    return true;
+  };
   if (kind == xegpu::LayoutKind::Subgroup)
     return checkTranspose(getEffectiveSgLayoutAsInt(),
                           other.getEffectiveSgLayoutAsInt(), perm) &&
            checkTranspose(getEffectiveSgDataAsInt(),
                           other.getEffectiveSgDataAsInt(), perm) &&
-           checkTranspose(getEffectiveOrderAsInt(),
-                          other.getEffectiveOrderAsInt(), perm);
+           checkOrderTranspose(getEffectiveOrderAsInt(),
+                               other.getEffectiveOrderAsInt(), perm);
   if (kind == xegpu::LayoutKind::InstData)
     return checkTranspose(getEffectiveInstDataAsInt(),
                           other.getEffectiveInstDataAsInt(), perm);
@@ -983,8 +1014,8 @@ bool LayoutAttr::isTransposeOf(const xegpu::DistributeLayoutAttr &other,
                           other.getEffectiveLaneLayoutAsInt(), perm) &&
            checkTranspose(getEffectiveLaneDataAsInt(),
                           other.getEffectiveLaneDataAsInt(), perm) &&
-           checkTranspose(getEffectiveOrderAsInt(),
-                          other.getEffectiveOrderAsInt(), perm);
+           checkOrderTranspose(getEffectiveOrderAsInt(),
+                               other.getEffectiveOrderAsInt(), perm);
 
   return false;
 }

diff  --git a/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir b/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
index d44497d0bba34..4c6353e45cfbe 100644
--- a/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
+++ b/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
@@ -589,3 +589,22 @@ gpu.module @test {
     gpu.return
   }
 }
+
+// -----
+gpu.module @test {
+// CHECK-LABEL: gpu.func @transpose_3d_order_remap(
+// CHECK: %[[TD_LD:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<32x2x32xf16> ->
+// CHECK-SAME: !xegpu.tensor_desc<32x2x32xf16, #xegpu.layout<sg_layout = [1, 2, 2], sg_data = [32, 1, 16], order = [0, 2, 1]>>
+// CHECK: %[[LD:.*]] = xegpu.load_nd %[[TD_LD]][%{{.*}}] <{layout = #xegpu.layout<sg_layout = [1, 2, 2], sg_data = [32, 1, 16], order = [0, 2, 1]>}>
+// CHECK: %[[TR:.*]] = vector.transpose %[[LD]], [1, 0, 2] {layout_result_0 = #xegpu.layout<sg_layout = [2, 1, 2], sg_data = [1, 32, 16], order = [1, 2, 0]>}
+// CHECK-SAME: : vector<32x2x32xf16> to vector<2x32x32xf16>
+  gpu.func @transpose_3d_order_remap(%src: memref<32x2x32xf16>, %dst: memref<2x32x32xf16>) kernel {
+    %c0 = arith.constant 0 : index
+    %td_in = xegpu.create_nd_tdesc %src : memref<32x2x32xf16> -> !xegpu.tensor_desc<32x2x32xf16>
+    %ld = xegpu.load_nd %td_in[%c0, %c0, %c0] : !xegpu.tensor_desc<32x2x32xf16> -> vector<32x2x32xf16>
+    %tr = vector.transpose %ld, [1, 0, 2] : vector<32x2x32xf16> to vector<2x32x32xf16>
+    %td_out = xegpu.create_nd_tdesc %dst : memref<2x32x32xf16> -> !xegpu.tensor_desc<2x32x32xf16>
+    xegpu.store_nd %tr, %td_out[%c0, %c0, %c0] <{layout = #xegpu.layout<sg_layout = [2, 1, 2], sg_data = [1, 32, 16], order = [1, 2, 0]>}> : vector<2x32x32xf16>, !xegpu.tensor_desc<2x32x32xf16>
+    gpu.return
+  }
+}


        


More information about the Mlir-commits mailing list