[Mlir-commits] [mlir] [mlir][xegpu] Add initial support for layout conflict handling. (PR #173090)
Charitha Saumya
llvmlistbot at llvm.org
Wed Jan 28 08:24:15 PST 2026
https://github.com/charithaintc updated https://github.com/llvm/llvm-project/pull/173090
>From a646df0e59d11dab90525b7ae4cfc87769e4aebd Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Tue, 16 Dec 2025 19:37:57 +0000
Subject: [PATCH 1/8] save work
---
.../Dialect/XeGPU/Transforms/Transforms.h | 8 ++
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 74 +++++++++++--------
.../lib/Dialect/XeGPU/TestXeGPUTransforms.cpp | 29 ++++++++
3 files changed, 81 insertions(+), 30 deletions(-)
diff --git a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
index 1776a209d0bf1..b97a7e4aa3211 100644
--- a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
@@ -9,6 +9,8 @@
#ifndef MLIR_DIALECT_XEGPU_TRANSFORMS_TRANSFORMS_H
#define MLIR_DIALECT_XEGPU_TRANSFORMS_TRANSFORMS_H
+#include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/IR/Builders.h"
#include "mlir/IR/Operation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/LogicalResult.h"
@@ -91,6 +93,12 @@ void populateXeGPUWgToSgDistributePatterns(RewritePatternSet &patterns);
void populateXeGPUUnrollPatterns(RewritePatternSet &patterns,
const UnrollOptions &options);
+enum class LayoutKind { Lane, InstData };
+LogicalResult propagateLayouts(OpBuilder &builder, Operation *target,
+ LayoutKind layoutKind, bool printOnly = false);
+
+LogicalResult resolveLayoutConflicts(OpBuilder &builder, Operation *target);
+
} // namespace xegpu
} // namespace mlir
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index dc9eb96c169b4..eae2cdfde0e32 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -15,6 +15,7 @@
#include "mlir/Dialect/Vector/IR/VectorOps.h"
#include "mlir/Dialect/XeGPU/IR/XeGPU.h"
#include "mlir/Dialect/XeGPU/Transforms/Passes.h"
+#include "mlir/Dialect/XeGPU/Transforms/Transforms.h"
#include "mlir/Dialect/XeGPU/Utils/XeGPUUtils.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
@@ -53,8 +54,6 @@ using namespace mlir::dataflow;
namespace {
-enum class LayoutKind { Lane, InstData };
-
//===----------------------------------------------------------------------===//
// LayoutInfo
//===----------------------------------------------------------------------===//
@@ -336,7 +335,7 @@ getSIMTLayoutInfoForDPASOperand(VectorType vectorTy, unsigned operandNum,
class LayoutInfoPropagation
: public SparseBackwardDataFlowAnalysis<LayoutInfoLattice> {
private:
- LayoutKind layoutKind;
+ xegpu::LayoutKind layoutKind;
void visitDpasOp(xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
ArrayRef<const LayoutInfoLattice *> results);
@@ -392,7 +391,7 @@ class LayoutInfoPropagation
public:
LayoutInfoPropagation(DataFlowSolver &solver,
SymbolTableCollection &symbolTable,
- LayoutKind layoutKind)
+ xegpu::LayoutKind layoutKind)
: SparseBackwardDataFlowAnalysis(solver, symbolTable),
layoutKind(layoutKind) {}
using SparseBackwardDataFlowAnalysis::SparseBackwardDataFlowAnalysis;
@@ -482,9 +481,9 @@ bool LayoutInfoPropagation::hasParamsOfLayoutKind(
if (anchorLayout == nullptr) {
return false;
}
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
return !(anchorLayout.getEffectiveInstDataAsInt().empty());
- } else if (layoutKind == LayoutKind::Lane) {
+ } else if (layoutKind == xegpu::LayoutKind::Lane) {
return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
anchorLayout.getEffectiveLaneDataAsInt().empty());
}
@@ -532,7 +531,7 @@ void LayoutInfoPropagation::visitPrefetchNdOp(
instData = {instHeight, instWidth};
}
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
prefetchLayout =
LayoutInfo(xegpu::LayoutAttr::get(tdescTy.getContext(), instData));
else
@@ -705,7 +704,7 @@ void LayoutInfoPropagation::visitDpasOp(
SmallVector<int> instDataA = {maxALen, subgroupSize};
SmallVector<int> instDataB = {subgroupSize, maxBLen};
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
dpasALayout =
LayoutInfo(xegpu::LayoutAttr::get(dpas.getContext(), instDataA));
dpasBLayout =
@@ -719,7 +718,7 @@ void LayoutInfoPropagation::visitDpasOp(
if (operands.size() > 2) {
VectorType cTy = dpas.getAccType();
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
const unsigned dataCLen = bTy.getShape().back();
auto supportedCLen =
uArchInstruction->getSupportedN(bTy.getElementType());
@@ -789,7 +788,7 @@ void LayoutInfoPropagation::visitStoreNdOp(
instData = {instHeight, instWidth};
}
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
storeLayout =
LayoutInfo(xegpu::LayoutAttr::get(dataTy.getContext(), instData));
else
@@ -949,7 +948,7 @@ void LayoutInfoPropagation::visitLoadGatherOp(
instData.push_back(chunkSize);
}
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
loadLayout =
LayoutInfo(xegpu::LayoutAttr::get(load.getContext(), instData));
else
@@ -1012,7 +1011,7 @@ void LayoutInfoPropagation::visitStoreScatterOp(
auto uArch = getUArch(getChipStr(storeScatter).value_or(""));
const int subgroupSize = uArch->getSubgroupSize();
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
SmallVector<int> instData{subgroupSize};
if (auto chunkSize = storeScatter.getChunkSize().value_or(0);
chunkSize > 1)
@@ -1063,7 +1062,8 @@ class RunLayoutInfoPropagation {
public:
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(RunLayoutInfoPropagation)
- RunLayoutInfoPropagation(Operation *op, LayoutKind layoutKind) : target(op) {
+ RunLayoutInfoPropagation(Operation *op, xegpu::LayoutKind layoutKind)
+ : target(op) {
SymbolTableCollection symbolTable;
loadBaselineAnalyses(solver);
solver.load<LayoutInfoPropagation>(symbolTable, layoutKind);
@@ -1305,24 +1305,14 @@ struct XeGPUPropagateLayoutPass final
} // namespace
-void XeGPUPropagateLayoutPass::runOnOperation() {
- LayoutKind layoutKind;
- if (this->layoutKind == "lane") {
- layoutKind = LayoutKind::Lane;
- } else if (this->layoutKind == "inst") {
- layoutKind = LayoutKind::InstData;
- } else {
- getOperation()->emitError("Unsupported layout kind option: " +
- this->layoutKind);
- signalPassFailure();
- return;
- }
- RunLayoutInfoPropagation analysis(getOperation(), layoutKind);
+LogicalResult xegpu::propagateLayouts(OpBuilder &builder, Operation *target,
+ LayoutKind layoutKind, bool printOnly) {
+ RunLayoutInfoPropagation analysis(target, layoutKind);
// Print the analysis result and exit. (for debugging purposes)
if (printOnly) {
auto &os = llvm::outs();
analysis.printAnalysisResult(os);
- return;
+ return success();
}
// Helper to convert LayoutInfo to xegpu::LayoutAttr.
auto getXeGPULayoutForValue = [&](Value val) -> xegpu::DistributeLayoutAttr {
@@ -1336,8 +1326,7 @@ void XeGPUPropagateLayoutPass::runOnOperation() {
return cast<xegpu::LayoutAttr>(layoutAttr);
};
- mlir::OpBuilder builder(&getContext());
- Operation *op = getOperation();
+ Operation *op = target;
auto walkResult = op->walk([&](mlir::Block *block) -> WalkResult {
for (mlir::Operation &op : llvm::reverse(block->getOperations())) {
LogicalResult r = success();
@@ -1362,7 +1351,32 @@ void XeGPUPropagateLayoutPass::runOnOperation() {
}
return WalkResult::advance();
});
- if (walkResult.wasInterrupted()) {
+ if (walkResult.wasInterrupted())
+ return failure();
+
+ return success();
+}
+
+LogicalResult xegpu::resolveLayoutConflicts(OpBuilder &builder,
+ Operation *target) {
+ return success();
+}
+
+void XeGPUPropagateLayoutPass::runOnOperation() {
+ xegpu::LayoutKind layoutKind;
+ if (this->layoutKind == "lane") {
+ layoutKind = xegpu::LayoutKind::Lane;
+ } else if (this->layoutKind == "inst") {
+ layoutKind = xegpu::LayoutKind::InstData;
+ } else {
+ getOperation()->emitError("Unsupported layout kind option: " +
+ this->layoutKind);
+ signalPassFailure();
+ return;
+ }
+ OpBuilder builder(&getContext());
+ if (failed(xegpu::propagateLayouts(builder, getOperation(), layoutKind,
+ this->printOnly))) {
signalPassFailure();
return;
}
diff --git a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
index 93d51441f5b81..610564910daed 100644
--- a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
+++ b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
@@ -278,6 +278,35 @@ struct TestXeGPUMoveFuncBodyToWarpOp
}
};
+struct TestXeGPUPropagateLayouts
+ : public PassWrapper<TestXeGPUPropagateLayouts,
+ OperationPass<gpu::GPUModuleOp>> {
+ MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestXeGPUPropagateLayouts)
+
+ StringRef getArgument() const final { return "test-xegpu-propagate-layouts"; }
+
+ StringRef getDescription() const final {
+ return "Test the implementation of XeGPU propagate layouts.";
+ }
+
+ void getDependentDialects(::mlir::DialectRegistry ®istry) const override {
+ registry.insert<xegpu::XeGPUDialect>();
+ registry.insert<gpu::GPUDialect>();
+ }
+
+ TestXeGPUPropagateLayouts() = default;
+ TestXeGPUPropagateLayouts(const TestXeGPUPropagateLayouts &pass) = default;
+
+ void runOnOperation() override {
+ OpBuilder builder(getOperation());
+ if (xegpu::propagateLayouts(OpBuilder(getOperation()), getOperation(),
+ LayoutKind::Lane)
+ .failed()) {
+ signalPassFailure();
+ }
+ }
+};
+
struct TestXeGPULayoutInterface
: public PassWrapper<TestXeGPULayoutInterface,
OperationPass<gpu::GPUModuleOp>> {
>From c61bfe6038766fc8152ae5a72620737fd9bc3bb9 Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Thu, 18 Dec 2025 23:49:22 +0000
Subject: [PATCH 2/8] save work
---
.../Dialect/XeGPU/Transforms/Transforms.h | 2 +-
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 55 ++++++++++++++++++-
.../lib/Dialect/XeGPU/TestXeGPUTransforms.cpp | 24 ++++++--
3 files changed, 74 insertions(+), 7 deletions(-)
diff --git a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
index b97a7e4aa3211..e3ce853dc2859 100644
--- a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
@@ -93,7 +93,7 @@ void populateXeGPUWgToSgDistributePatterns(RewritePatternSet &patterns);
void populateXeGPUUnrollPatterns(RewritePatternSet &patterns,
const UnrollOptions &options);
-enum class LayoutKind { Lane, InstData };
+enum class LayoutKind { Lane, InstData, Subgroup };
LogicalResult propagateLayouts(OpBuilder &builder, Operation *target,
LayoutKind layoutKind, bool printOnly = false);
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index a122e63247416..463b94ae649af 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -530,7 +530,7 @@ bool LayoutInfoPropagation::hasParamsOfLayoutKind(
} else if (layoutKind == xegpu::LayoutKind::Lane) {
return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
anchorLayout.getEffectiveLaneDataAsInt().empty());
- } else if (layoutKind == LayoutKind::Subgroup) {
+ } else if (layoutKind == xegpu::LayoutKind::Subgroup) {
return !(anchorLayout.getEffectiveSgLayoutAsInt().empty() ||
anchorLayout.getEffectiveSgDataAsInt().empty());
}
@@ -1402,6 +1402,57 @@ LogicalResult xegpu::propagateLayouts(OpBuilder &builder, Operation *target,
LogicalResult xegpu::resolveLayoutConflicts(OpBuilder &builder,
Operation *target) {
+ auto r = target->walk([&](xegpu::LoadNdOp loadNdOp) -> WalkResult {
+ // Load op has a conflict if tensor desc layout is different from the its
+ // result layout.
+ auto getResultLayout = [](OpResult result) {
+ auto resultLayoutName = xegpu::getTemporaryLayoutName(result);
+ return result.getOwner()->getAttrOfType<xegpu::DistributeLayoutAttr>(
+ resultLayoutName);
+ };
+ auto hasConflict = [&getResultLayout](xegpu::LoadNdOp loadNdOp) -> bool {
+ auto tdescType = loadNdOp.getTensorDescType();
+ auto tdescLayout = tdescType.getLayout();
+ auto resultLayoutName =
+ xegpu::getTemporaryLayoutName(loadNdOp->getOpResult(0));
+ auto resultLayout = getResultLayout(loadNdOp->getOpResult(0));
+ return tdescLayout && resultLayout && tdescLayout != resultLayout;
+ };
+ if (hasConflict(loadNdOp)) {
+ OpBuilder builder(loadNdOp);
+ // Try to get the defining createNdDesc op.
+ auto createNdOp =
+ loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
+ if (!createNdOp) {
+ DBGS() << "Failed to resolve LoadNdOp layout conflict: " << *loadNdOp
+ << "\n";
+ return WalkResult::interrupt();
+ }
+
+ builder.setInsertionPointAfter(createNdOp);
+ auto tdescType = loadNdOp.getTensorDescType();
+ auto expectedLayout = getResultLayout(loadNdOp->getOpResult(0));
+ auto newTensorDescType = xegpu::TensorDescType::get(
+ createNdOp.getContext(), tdescType.getShape(),
+ tdescType.getElementType(), tdescType.getEncoding(), expectedLayout);
+ auto newOp = xegpu::CreateNdDescOp::create(
+ builder, loadNdOp.getLoc(), newTensorDescType,
+ createNdOp->getOperands(), createNdOp->getAttrs());
+ // Replace only the conflicting uses of the createNdOp that can be
+ // resolved using the new layout.
+ createNdOp->replaceUsesWithIf(
+ ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
+ auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
+ if (!userLoadNdOp)
+ return false;
+ auto resultLayout = getResultLayout(userLoadNdOp->getOpResult(0));
+ return hasConflict(userLoadNdOp) && resultLayout == expectedLayout;
+ });
+ }
+ return WalkResult::advance();
+ });
+ if (r.wasInterrupted())
+ return failure();
return success();
}
@@ -1412,7 +1463,7 @@ void XeGPUPropagateLayoutPass::runOnOperation() {
} else if (this->layoutKind == "inst") {
layoutKind = xegpu::LayoutKind::InstData;
} else if (this->layoutKind == "subgroup") {
- layoutKind = LayoutKind::Subgroup;
+ layoutKind = xegpu::LayoutKind::Subgroup;
} else {
getOperation()->emitError("Unsupported layout kind option: " +
this->layoutKind);
diff --git a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
index cd9e8a6daa42a..cfe8f32fdaf70 100644
--- a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
+++ b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
@@ -294,13 +294,29 @@ struct TestXeGPUPropagateLayouts
}
TestXeGPUPropagateLayouts() = default;
- TestXeGPUPropagateLayouts(const TestXeGPUPropagateLayouts &pass) = default;
+ TestXeGPUPropagateLayouts(const TestXeGPUPropagateLayouts &pass)
+ : PassWrapper(pass) {}
+
+ Option<std::string> layoutKind{
+ *this, "layout-kind",
+ llvm::cl::desc("Propagate `subgroup` / `inst` / `lane` level of xegpu "
+ "layouts."),
+ llvm::cl::init("lane")};
void runOnOperation() override {
OpBuilder builder(getOperation());
- if (xegpu::propagateLayouts(OpBuilder(getOperation()), getOperation(),
- LayoutKind::Lane)
- .failed()) {
+ LayoutKind kind;
+ if (layoutKind == "subgroup")
+ kind = LayoutKind::Subgroup;
+ else if (layoutKind == "inst")
+ kind = LayoutKind::InstData;
+ else if (layoutKind == "lane")
+ kind = LayoutKind::Lane;
+ else {
+ signalPassFailure();
+ return;
+ }
+ if (xegpu::propagateLayouts(builder, getOperation(), kind).failed()) {
signalPassFailure();
}
}
>From b76ad1dc5f5a179e4112bc3061e45fc1de4cb4e7 Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Fri, 19 Dec 2025 19:28:16 +0000
Subject: [PATCH 3/8] save work
---
.../Dialect/XeGPU/Transforms/Transforms.h | 2 +-
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 192 +++++++++++++-----
.../XeGPU/resolve-layout-conflicts.mlir | 23 +++
.../lib/Dialect/XeGPU/TestXeGPUTransforms.cpp | 33 ++-
4 files changed, 194 insertions(+), 56 deletions(-)
create mode 100644 mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
diff --git a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
index e3ce853dc2859..80ea1e3407058 100644
--- a/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/XeGPU/Transforms/Transforms.h
@@ -97,7 +97,7 @@ enum class LayoutKind { Lane, InstData, Subgroup };
LogicalResult propagateLayouts(OpBuilder &builder, Operation *target,
LayoutKind layoutKind, bool printOnly = false);
-LogicalResult resolveLayoutConflicts(OpBuilder &builder, Operation *target);
+LogicalResult resolveLayoutConflicts(Operation *target);
} // namespace xegpu
} // namespace mlir
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index 463b94ae649af..c8138a4d16016 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -38,6 +38,7 @@
#include "llvm/Support/raw_ostream.h"
#include "mlir/Dialect/XeGPU/uArch/IntelGpuXe2.h"
+#include "mlir/Support/WalkResult.h"
namespace mlir {
namespace xegpu {
@@ -1180,6 +1181,77 @@ void RunLayoutInfoPropagation::printAnalysisResult(llvm::raw_ostream &os) {
printFunctionResult(funcOp);
}
+namespace {
+
+//===----------------------------------------------------------------------===//
+// ResolveLayoutConflicts
+//===----------------------------------------------------------------------===//
+struct ResolveLayoutConflicts {
+ ResolveLayoutConflicts(Operation *parentOp)
+ : parentOp(parentOp), builder(parentOp->getContext()) {}
+ LogicalResult run();
+
+private:
+ Operation *parentOp;
+ OpBuilder builder;
+ LogicalResult resolveLoadNdOp(xegpu::LoadNdOp loadNdOp);
+};
+
+}; // namespace
+
+LogicalResult ResolveLayoutConflicts::run() {
+ auto r = parentOp->walk([&](Operation *op) -> WalkResult {
+ TypeSwitch<Operation *>(op).Case([&](xegpu::LoadNdOp loadNdOp) {
+ return failed(resolveLoadNdOp(loadNdOp)) ? WalkResult::interrupt()
+ : WalkResult::advance();
+ });
+ // TODO: Add other layout conflict resolution methods as needed.
+ return WalkResult::advance();
+ });
+
+ return r.wasInterrupted() ? failure() : success();
+}
+
+/// LoadNd has a conflict if the tensor descriptor layout is different from the
+/// load's anchor layout.
+LogicalResult
+ResolveLayoutConflicts::resolveLoadNdOp(xegpu::LoadNdOp loadNdOp) {
+ Attribute anchorLayout = loadNdOp.getLayoutAttr();
+ Attribute tdescLayout = loadNdOp.getTensorDescType().getLayout();
+
+ if (anchorLayout && tdescLayout && anchorLayout != tdescLayout) {
+ // Try to get the defining CreateNdDescOp of the tensor descriptor.
+ auto conflictingCreateNdOp =
+ loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
+ if (!conflictingCreateNdOp) {
+ DBGS() << "Unable to find defining CreateNdDescOp for tensor descriptor: "
+ << loadNdOp.getTensorDesc() << "\n";
+ return failure();
+ }
+ // Duplicate the CreateNdDescOp with the expected layout.
+ builder.setInsertionPointAfter(conflictingCreateNdOp);
+ xegpu::TensorDescType tdescType = loadNdOp.getTensorDescType();
+ auto expectedLayout = anchorLayout;
+ auto newTensorDescType = xegpu::TensorDescType::get(
+ conflictingCreateNdOp.getContext(), tdescType.getShape(),
+ tdescType.getElementType(), tdescType.getEncoding(), expectedLayout);
+ xegpu::CreateNdDescOp newOp = xegpu::CreateNdDescOp::create(
+ builder, loadNdOp.getLoc(), newTensorDescType,
+ conflictingCreateNdOp->getOperands(),
+ conflictingCreateNdOp->getAttrs());
+ // Replace only the conflicting uses of the createNdOp that can be
+ // resolved using the new layout.
+ conflictingCreateNdOp->replaceUsesWithIf(
+ ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
+ auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
+ if (!userLoadNdOp)
+ return false;
+ return userLoadNdOp.getLayoutAttr() == expectedLayout;
+ });
+ }
+ return success();
+}
+
using GetLayoutFnTy = function_ref<xegpu::DistributeLayoutAttr(Value)>;
/// Update an operation with the layout of its results. If the result type is
/// a vector type, a temporary layout attribute is added to the operation. If
@@ -1400,60 +1472,67 @@ LogicalResult xegpu::propagateLayouts(OpBuilder &builder, Operation *target,
return success();
}
-LogicalResult xegpu::resolveLayoutConflicts(OpBuilder &builder,
- Operation *target) {
- auto r = target->walk([&](xegpu::LoadNdOp loadNdOp) -> WalkResult {
- // Load op has a conflict if tensor desc layout is different from the its
- // result layout.
- auto getResultLayout = [](OpResult result) {
- auto resultLayoutName = xegpu::getTemporaryLayoutName(result);
- return result.getOwner()->getAttrOfType<xegpu::DistributeLayoutAttr>(
- resultLayoutName);
- };
- auto hasConflict = [&getResultLayout](xegpu::LoadNdOp loadNdOp) -> bool {
- auto tdescType = loadNdOp.getTensorDescType();
- auto tdescLayout = tdescType.getLayout();
- auto resultLayoutName =
- xegpu::getTemporaryLayoutName(loadNdOp->getOpResult(0));
- auto resultLayout = getResultLayout(loadNdOp->getOpResult(0));
- return tdescLayout && resultLayout && tdescLayout != resultLayout;
- };
- if (hasConflict(loadNdOp)) {
- OpBuilder builder(loadNdOp);
- // Try to get the defining createNdDesc op.
- auto createNdOp =
- loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
- if (!createNdOp) {
- DBGS() << "Failed to resolve LoadNdOp layout conflict: " << *loadNdOp
- << "\n";
- return WalkResult::interrupt();
- }
-
- builder.setInsertionPointAfter(createNdOp);
- auto tdescType = loadNdOp.getTensorDescType();
- auto expectedLayout = getResultLayout(loadNdOp->getOpResult(0));
- auto newTensorDescType = xegpu::TensorDescType::get(
- createNdOp.getContext(), tdescType.getShape(),
- tdescType.getElementType(), tdescType.getEncoding(), expectedLayout);
- auto newOp = xegpu::CreateNdDescOp::create(
- builder, loadNdOp.getLoc(), newTensorDescType,
- createNdOp->getOperands(), createNdOp->getAttrs());
- // Replace only the conflicting uses of the createNdOp that can be
- // resolved using the new layout.
- createNdOp->replaceUsesWithIf(
- ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
- auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
- if (!userLoadNdOp)
- return false;
- auto resultLayout = getResultLayout(userLoadNdOp->getOpResult(0));
- return hasConflict(userLoadNdOp) && resultLayout == expectedLayout;
- });
- }
- return WalkResult::advance();
- });
- if (r.wasInterrupted())
- return failure();
- return success();
+// LogicalResult xegpu::resolveLayoutConflicts(OpBuilder &builder,
+// Operation *target) {
+// auto r = target->walk([&](xegpu::LoadNdOp loadNdOp) -> WalkResult {
+// // Load op has a conflict if tensor desc layout is different from the its
+// // result layout.
+// auto getResultLayout = [](OpResult result) {
+// auto resultLayoutName = xegpu::getTemporaryLayoutName(result);
+// return result.getOwner()->getAttrOfType<xegpu::DistributeLayoutAttr>(
+// resultLayoutName);
+// };
+// auto hasConflict = [&getResultLayout](xegpu::LoadNdOp loadNdOp) -> bool {
+// auto tdescType = loadNdOp.getTensorDescType();
+// auto tdescLayout = tdescType.getLayout();
+// auto resultLayoutName =
+// xegpu::getTemporaryLayoutName(loadNdOp->getOpResult(0));
+// auto resultLayout = getResultLayout(loadNdOp->getOpResult(0));
+// return tdescLayout && resultLayout && tdescLayout != resultLayout;
+// };
+// if (hasConflict(loadNdOp)) {
+// OpBuilder builder(loadNdOp);
+// // Try to get the defining createNdDesc op.
+// auto createNdOp =
+// loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
+// if (!createNdOp) {
+// DBGS() << "Failed to resolve LoadNdOp layout conflict: " << *loadNdOp
+// << "\n";
+// return WalkResult::interrupt();
+// }
+
+// builder.setInsertionPointAfter(createNdOp);
+// auto tdescType = loadNdOp.getTensorDescType();
+// auto expectedLayout = getResultLayout(loadNdOp->getOpResult(0));
+// auto newTensorDescType = xegpu::TensorDescType::get(
+// createNdOp.getContext(), tdescType.getShape(),
+// tdescType.getElementType(), tdescType.getEncoding(),
+// expectedLayout);
+// auto newOp = xegpu::CreateNdDescOp::create(
+// builder, loadNdOp.getLoc(), newTensorDescType,
+// createNdOp->getOperands(), createNdOp->getAttrs());
+// // Replace only the conflicting uses of the createNdOp that can be
+// // resolved using the new layout.
+// createNdOp->replaceUsesWithIf(
+// ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
+// auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
+// if (!userLoadNdOp)
+// return false;
+// auto resultLayout =
+// getResultLayout(userLoadNdOp->getOpResult(0)); return
+// hasConflict(userLoadNdOp) && resultLayout == expectedLayout;
+// });
+// }
+// return WalkResult::advance();
+// });
+// if (r.wasInterrupted())
+// return failure();
+// return success();
+// }
+
+LogicalResult xegpu::resolveLayoutConflicts(Operation *target) {
+ ResolveLayoutConflicts resolver(target);
+ return resolver.run();
}
void XeGPUPropagateLayoutPass::runOnOperation() {
@@ -1476,4 +1555,9 @@ void XeGPUPropagateLayoutPass::runOnOperation() {
signalPassFailure();
return;
}
+ // Resolve layout conflicts if any.
+ if (failed(xegpu::resolveLayoutConflicts(getOperation()))) {
+ signalPassFailure();
+ return;
+ }
}
diff --git a/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir b/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
new file mode 100644
index 0000000000000..dd3f3c8bdc29e
--- /dev/null
+++ b/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
@@ -0,0 +1,23 @@
+// RUN: mlir-opt --test-xegpu-resolve-layout-conflicts -split-input-file %s | FileCheck %s
+
+#load_lo = #xegpu.layout<inst_data = [8, 16]>
+#prefetch_lo = #xegpu.layout<inst_data = [16, 16]>
+gpu.module @test {
+
+// CHECK-LABEL: func.func @load_nd_with_conflicting_tensor_desc
+// CHECK: %{{.*}} = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+// CHECK-NEXT: %[[T1:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [8, 16]>>
+// CHECK-NEXT: %{{.*}} = xegpu.load_nd %[[T1]][%{{.*}}, %{{.*}}] <{layout = #xegpu.layout<inst_data = [8, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [8, 16]>> -> vector<16x16xf16>
+func.func @load_nd_with_conflicting_tensor_desc(%arg0: memref<64x64xf16>) -> vector<16x16xf16> {
+ %c0 = arith.constant 0 : index
+ %0 = xegpu.create_nd_tdesc %arg0 : memref<64x64xf16>
+ -> !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ %1 = xegpu.load_nd %0 [%c0, %c0] {layout = #load_lo} : !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ -> vector<16x16xf16>
+ xegpu.prefetch_nd %0 [%c0, %c0] {layout = #prefetch_lo} : !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ return %1 : vector<16x16xf16>
+}
+}
diff --git a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
index cfe8f32fdaf70..c8a6a6d7b8eb8 100644
--- a/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
+++ b/mlir/test/lib/Dialect/XeGPU/TestXeGPUTransforms.cpp
@@ -316,7 +316,36 @@ struct TestXeGPUPropagateLayouts
signalPassFailure();
return;
}
- if (xegpu::propagateLayouts(builder, getOperation(), kind).failed()) {
+ if (failed(xegpu::propagateLayouts(builder, getOperation(), kind))) {
+ signalPassFailure();
+ }
+ }
+};
+
+struct TestXeGPUResolveLayoutConflicts
+ : public PassWrapper<TestXeGPUResolveLayoutConflicts,
+ OperationPass<gpu::GPUModuleOp>> {
+ MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestXeGPUResolveLayoutConflicts)
+
+ StringRef getArgument() const final {
+ return "test-xegpu-resolve-layout-conflicts";
+ }
+
+ StringRef getDescription() const final {
+ return "Test the implementation of XeGPU layout conflict resolution.";
+ }
+
+ void getDependentDialects(::mlir::DialectRegistry ®istry) const override {
+ registry.insert<xegpu::XeGPUDialect>();
+ registry.insert<gpu::GPUDialect>();
+ }
+
+ TestXeGPUResolveLayoutConflicts() = default;
+ TestXeGPUResolveLayoutConflicts(const TestXeGPUResolveLayoutConflicts &pass) =
+ default;
+
+ void runOnOperation() override {
+ if (failed(xegpu::resolveLayoutConflicts(getOperation()))) {
signalPassFailure();
}
}
@@ -387,6 +416,8 @@ void registerTestXeGPULowerings() {
PassRegistration<TestXeGPULayoutInterface>();
PassRegistration<TestXeGPUSGDistribute>();
PassRegistration<TestXeGPUMoveFuncBodyToWarpOp>();
+ PassRegistration<TestXeGPUPropagateLayouts>();
+ PassRegistration<TestXeGPUResolveLayoutConflicts>();
}
} // namespace test
} // namespace mlir
>From 5d2a2c5f4afb274c37e10bfc0fecb8afbc0a22c9 Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Fri, 19 Dec 2025 19:34:32 +0000
Subject: [PATCH 4/8] save work
---
.../test/Dialect/XeGPU/propagate-layout-inst-data.mlir | 2 +-
mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir | 2 +-
mlir/test/Dialect/XeGPU/propagate-layout.mlir | 10 +++++-----
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/mlir/test/Dialect/XeGPU/propagate-layout-inst-data.mlir b/mlir/test/Dialect/XeGPU/propagate-layout-inst-data.mlir
index 5f70831f45e97..5e095fe0df89e 100644
--- a/mlir/test/Dialect/XeGPU/propagate-layout-inst-data.mlir
+++ b/mlir/test/Dialect/XeGPU/propagate-layout-inst-data.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -xevm-attach-target='chip=pvc' -xegpu-propagate-layout="layout-kind=inst" -split-input-file %s | FileCheck %s
+// RUN: mlir-opt -xevm-attach-target='chip=pvc' -test-xegpu-propagate-layouts="layout-kind=inst" -split-input-file %s | FileCheck %s
// CHECK-LABEL: func.func @load_store_no_array_len(
diff --git a/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir b/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
index 092a4cf442782..7675c44be1c61 100644
--- a/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
+++ b/mlir/test/Dialect/XeGPU/propagate-layout-subgroup.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -xevm-attach-target='chip=pvc' -xegpu-propagate-layout="layout-kind=subgroup" -split-input-file %s | FileCheck %s
+// RUN: mlir-opt -xevm-attach-target='chip=pvc' -test-xegpu-propagate-layouts="layout-kind=subgroup" -split-input-file %s | FileCheck %s
gpu.module @test {
// CHECK-LABEL: store_nd
diff --git a/mlir/test/Dialect/XeGPU/propagate-layout.mlir b/mlir/test/Dialect/XeGPU/propagate-layout.mlir
index b88d8e1a78a26..3e7f3d5156d62 100644
--- a/mlir/test/Dialect/XeGPU/propagate-layout.mlir
+++ b/mlir/test/Dialect/XeGPU/propagate-layout.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -xevm-attach-target='chip=pvc' -xegpu-propagate-layout="layout-kind=lane" -split-input-file %s | FileCheck %s
+// RUN: mlir-opt -xevm-attach-target='chip=pvc' -test-xegpu-propagate-layouts="layout-kind=lane" -split-input-file %s | FileCheck %s
gpu.module @test {
// CHECK-LABEL: func.func @dpas_f16(
@@ -32,7 +32,7 @@ func.func @dpas_f16(%arg0: memref<8x16xf16>, %arg1: memref<16x16xf16>, %arg2: me
gpu.module @test {
// CHECK-LABEL: func.func @dpas_i8(
// CHECK-SAME: %[[ARG0:[0-9a-zA-Z]+]]: vector<8x32xi8>, %[[ARG1:[0-9a-zA-Z]+]]: vector<32x16xi8>, %[[ARG2:[0-9a-zA-Z]+]]: memref<8x16xi32>) {
-// CHECK: %[[T0:.*]] = xegpu.dpas %[[ARG0]], %[[ARG1]] {layout_a = #xegpu.layout<lane_layout = [1, 16], lane_data = [1, 2]>, layout_b = #xegpu.layout<lane_layout = [1, 16], lane_data = [4, 1]>, layout_cd = #xegpu.layout<lane_layout = [1, 16], lane_data = [1, 1]>}
+// CHECK: %[[T0:.*]] = xegpu.dpas %[[ARG0]], %[[ARG1]] {layout_a = #xegpu.layout<lane_layout = [1, 16], lane_data = [1, 2]>, layout_b = #xegpu.layout<lane_layout = [1, 16], lane_data = [4, 1]>, layout_cd = #xegpu.layout<lane_layout = [1, 16], lane_data = [1, 1]>}
func.func @dpas_i8(%arg0: vector<8x32xi8>, %arg1: vector<32x16xi8>, %arg2: memref<8x16xi32>) {
%c0 = arith.constant 0 : index
@@ -109,7 +109,7 @@ gpu.module @test {
// CHECK-NEXT: %[[CST0:.*]] = arith.constant {layout_result_0 = #xegpu.layout<lane_layout = [16], lane_data = [1]>} dense<true> : vector<16xi1>
// CHECK-NEXT: %[[T2:.*]] = xegpu.create_tdesc %[[ARG1]], %[[CST]] : memref<256xf16>, vector<16xindex> ->
// CHECK-SAME: !xegpu.tensor_desc<16x16xf16, #xegpu.scatter_tdesc_attr<chunk_size = 16 : i64>, #xegpu.layout<lane_layout = [16, 1], lane_data = [1, 2]>>
-// CHECK-NEXT: %{{.*}} = xegpu.load %[[T2]], %[[CST0]] <{layout = #xegpu.layout<lane_layout = [16, 1], lane_data = [1, 2]>}>
+// CHECK-NEXT: %{{.*}} = xegpu.load %[[T2]], %[[CST0]] <{layout = #xegpu.layout<lane_layout = [16, 1], lane_data = [1, 2]>}>
// CHECK-SAME: !xegpu.tensor_desc<16x16xf16, #xegpu.scatter_tdesc_attr<chunk_size = 16 : i64>, #xegpu.layout<lane_layout = [16, 1], lane_data = [1, 2]>>, vector<16xi1> -> vector<16x16xf16>
func.func @load_gather_with_chunksize(%arg0: memref<8x16xf16>, %arg1: memref<256xf16>, %arg2: memref<8x16xf32>) {
%c0 = arith.constant 0 : index
@@ -240,7 +240,7 @@ gpu.module @test {
// CHECK-SAME: %[[ARG0:[0-9a-zA-Z]+]]: memref<256xf16>) {
// CHECK: %[[MASK:.*]] = arith.constant {layout_result_0 = #xegpu.layout<lane_layout = [8], lane_data = [1]>} dense<true> : vector<16xi1>
// CHECK: %[[OFFSETS:.*]] = arith.constant {layout_result_0 = #xegpu.layout<lane_layout = [8], lane_data = [1]>} dense<12> : vector<16xindex>
-// CHECK: %[[LOAD_VEC:.*]] = xegpu.load %[[ARG0]][%[[OFFSETS]]], %[[MASK]]
+// CHECK: %[[LOAD_VEC:.*]] = xegpu.load %[[ARG0]][%[[OFFSETS]]], %[[MASK]]
// CHECK-SAME: memref<256xf16>, vector<16xindex>, vector<16xi1> -> vector<16xf16>
// CHECK: %[[ADD_RES:.*]] = arith.addf %[[LOAD_VEC]], %[[LOAD_VEC]] {layout_result_0 = #xegpu.layout<lane_layout = [8], lane_data = [1]>} : vector<16xf16>
// CHECK: xegpu.store %[[ADD_RES]], %[[ARG0]][%[[OFFSETS]]], %[[MASK]]
@@ -697,4 +697,4 @@ func.func @vector_broadcast_scalar_to_vector(%arg0: !xegpu.tensor_desc<16x16xf16
xegpu.store_nd %6, %arg0 : vector<16x16xf16>, !xegpu.tensor_desc<16x16xf16>
return
}
-}
\ No newline at end of file
+}
>From 23bb87cbdd69610d83f54449fdd980583f2ee527 Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Fri, 19 Dec 2025 19:51:21 +0000
Subject: [PATCH 5/8] save work
---
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 60 +------------------
1 file changed, 1 insertion(+), 59 deletions(-)
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index c8138a4d16016..9c35b14be0bd5 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -1197,7 +1197,7 @@ struct ResolveLayoutConflicts {
LogicalResult resolveLoadNdOp(xegpu::LoadNdOp loadNdOp);
};
-}; // namespace
+} // namespace
LogicalResult ResolveLayoutConflicts::run() {
auto r = parentOp->walk([&](Operation *op) -> WalkResult {
@@ -1472,64 +1472,6 @@ LogicalResult xegpu::propagateLayouts(OpBuilder &builder, Operation *target,
return success();
}
-// LogicalResult xegpu::resolveLayoutConflicts(OpBuilder &builder,
-// Operation *target) {
-// auto r = target->walk([&](xegpu::LoadNdOp loadNdOp) -> WalkResult {
-// // Load op has a conflict if tensor desc layout is different from the its
-// // result layout.
-// auto getResultLayout = [](OpResult result) {
-// auto resultLayoutName = xegpu::getTemporaryLayoutName(result);
-// return result.getOwner()->getAttrOfType<xegpu::DistributeLayoutAttr>(
-// resultLayoutName);
-// };
-// auto hasConflict = [&getResultLayout](xegpu::LoadNdOp loadNdOp) -> bool {
-// auto tdescType = loadNdOp.getTensorDescType();
-// auto tdescLayout = tdescType.getLayout();
-// auto resultLayoutName =
-// xegpu::getTemporaryLayoutName(loadNdOp->getOpResult(0));
-// auto resultLayout = getResultLayout(loadNdOp->getOpResult(0));
-// return tdescLayout && resultLayout && tdescLayout != resultLayout;
-// };
-// if (hasConflict(loadNdOp)) {
-// OpBuilder builder(loadNdOp);
-// // Try to get the defining createNdDesc op.
-// auto createNdOp =
-// loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
-// if (!createNdOp) {
-// DBGS() << "Failed to resolve LoadNdOp layout conflict: " << *loadNdOp
-// << "\n";
-// return WalkResult::interrupt();
-// }
-
-// builder.setInsertionPointAfter(createNdOp);
-// auto tdescType = loadNdOp.getTensorDescType();
-// auto expectedLayout = getResultLayout(loadNdOp->getOpResult(0));
-// auto newTensorDescType = xegpu::TensorDescType::get(
-// createNdOp.getContext(), tdescType.getShape(),
-// tdescType.getElementType(), tdescType.getEncoding(),
-// expectedLayout);
-// auto newOp = xegpu::CreateNdDescOp::create(
-// builder, loadNdOp.getLoc(), newTensorDescType,
-// createNdOp->getOperands(), createNdOp->getAttrs());
-// // Replace only the conflicting uses of the createNdOp that can be
-// // resolved using the new layout.
-// createNdOp->replaceUsesWithIf(
-// ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
-// auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
-// if (!userLoadNdOp)
-// return false;
-// auto resultLayout =
-// getResultLayout(userLoadNdOp->getOpResult(0)); return
-// hasConflict(userLoadNdOp) && resultLayout == expectedLayout;
-// });
-// }
-// return WalkResult::advance();
-// });
-// if (r.wasInterrupted())
-// return failure();
-// return success();
-// }
-
LogicalResult xegpu::resolveLayoutConflicts(Operation *target) {
ResolveLayoutConflicts resolver(target);
return resolver.run();
>From c1e98457fe74fd845b38941d60920cda343c8a56 Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Mon, 26 Jan 2026 22:47:19 +0000
Subject: [PATCH 6/8] address comments
---
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 114 ++++++++++++------
.../XeGPU/resolve-layout-conflicts.mlir | 58 +++++++++
2 files changed, 132 insertions(+), 40 deletions(-)
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index 0e890a002ad04..878dc5486f24c 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -26,6 +26,7 @@
#include "mlir/IR/Visitors.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/FunctionInterfaces.h"
+#include "mlir/Interfaces/LoopLikeInterface.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
@@ -810,7 +811,7 @@ void LayoutInfoPropagation::visitDpasOp(
}
instDataCD = {maxALen, maxCLen};
}
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
dpasALayout =
LayoutInfo(xegpu::LayoutAttr::get(dpas.getContext(), instDataA));
dpasBLayout =
@@ -819,7 +820,7 @@ void LayoutInfoPropagation::visitDpasOp(
dpasCDLayout =
LayoutInfo(xegpu::LayoutAttr::get(dpas.getContext(), instDataCD));
}
- } else if (layoutKind == LayoutKind::Lane) {
+ } else if (layoutKind == xegpu::LayoutKind::Lane) {
dpasALayout = getSIMTLayoutInfoForDPASOperand(
aTy, 0, uArch, uArchInstruction->getPackedFormatBitSizeA());
dpasBLayout = getSIMTLayoutInfoForDPASOperand(
@@ -981,7 +982,7 @@ void LayoutInfoPropagation::visitStoreNdOp(
if (layoutKind == xegpu::LayoutKind::InstData)
storeLayout =
LayoutInfo(xegpu::LayoutAttr::get(dataTy.getContext(), instData));
- else if (layoutKind == LayoutKind::Lane)
+ else if (layoutKind == xegpu::LayoutKind::Lane)
storeLayout =
getSIMTLayoutInfoBlockIO(store.getValueType(), uArch,
uArchInstruction->getPackedFormatBitSize());
@@ -1171,7 +1172,7 @@ void LayoutInfoPropagation::visitLoadGatherOp(
uArch->getInstruction(xegpu::uArch::InstructionKind::LoadGather));
// Check if value inst_data complies with uArch
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
// Each lane loads either one element
SmallVector<int> instDataUarch{subgroupSize};
// Or multiple elements as 2D with lane's elements in the inner dimension
@@ -1213,10 +1214,10 @@ void LayoutInfoPropagation::visitLoadGatherOp(
// Rank >1 data: Enforce the default xegpu 1D layout for mask.
if (!hasParamsOfLayoutKind(anchorLayout) ||
load.getValueType().getRank() > 1) {
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
maskLayout = LayoutInfo(
xegpu::LayoutAttr::get(load->getContext(), {subgroupSize}));
- else if (layoutKind == LayoutKind::Lane)
+ else if (layoutKind == xegpu::LayoutKind::Lane)
maskLayout =
getDefaultSIMTLayoutInfo(load->getContext(), 1, subgroupSize);
}
@@ -1271,7 +1272,7 @@ void LayoutInfoPropagation::visitStoreScatterOp(
return;
}
- if (layoutKind == LayoutKind::InstData) {
+ if (layoutKind == xegpu::LayoutKind::InstData) {
const auto *uArchInstruction =
dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
xegpu::uArch::InstructionKind::StoreScatter));
@@ -1308,10 +1309,10 @@ void LayoutInfoPropagation::visitStoreScatterOp(
// Rank >1 data: Enforce the default xegpu 1D layout for mask.
if (!hasParamsOfLayoutKind(anchorLayout) ||
storeScatter.getValueType().getRank() > 1) {
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
maskLayout = LayoutInfo(
xegpu::LayoutAttr::get(storeScatter->getContext(), {subgroupSize}));
- else if (layoutKind == LayoutKind::Lane)
+ else if (layoutKind == xegpu::LayoutKind::Lane)
maskLayout =
getDefaultSIMTLayoutInfo(storeScatter->getContext(), 1, subgroupSize);
}
@@ -1345,7 +1346,7 @@ void LayoutInfoPropagation::visitStoreMatrixOp(
assert(payloadTy.getRank() == 2 && "Expecting 2D vector for store matrix.");
auto uArch = getUArch(getChipStr(storeMatrix).value_or(""));
SmallVector<int> instData = {1, uArch->getSubgroupSize()};
- if (layoutKind == LayoutKind::InstData)
+ if (layoutKind == xegpu::LayoutKind::InstData)
layout = LayoutInfo(
xegpu::LayoutAttr::get(storeMatrix.getContext(), instData));
else
@@ -1453,60 +1454,93 @@ struct ResolveLayoutConflicts {
private:
Operation *parentOp;
OpBuilder builder;
- LogicalResult resolveLoadNdOp(xegpu::LoadNdOp loadNdOp);
+ LogicalResult resolveTensorDescConsumer(OpOperand &operand);
+ LogicalResult resolveVectorConsumer(OpOperand &operand);
};
} // namespace
LogicalResult ResolveLayoutConflicts::run() {
+ // Helper function to get the tensor descriptor operand, or null if none found
+ // TODO: We assume only one tensor descriptor operand per op.
+ auto getTensorDescOperand = [](Operation *op) -> OpOperand * {
+ for (OpOperand &opnd : op->getOpOperands()) {
+ if (isa<xegpu::TensorDescType>(opnd.get().getType())) {
+ return &opnd;
+ }
+ }
+ return nullptr;
+ };
+
auto r = parentOp->walk([&](Operation *op) -> WalkResult {
- TypeSwitch<Operation *>(op).Case([&](xegpu::LoadNdOp loadNdOp) {
- return failed(resolveLoadNdOp(loadNdOp)) ? WalkResult::interrupt()
- : WalkResult::advance();
- });
- // TODO: Add other layout conflict resolution methods as needed.
+ // Check if this op is an anchor op and at least one operand is a tensor
+ // descriptor type.
+ OpOperand *tdescOperand = getTensorDescOperand(op);
+ if (isa<xegpu::AnchorLayoutInterface>(op) && tdescOperand) {
+ auto res = resolveTensorDescConsumer(*tdescOperand);
+ return succeeded(res) ? WalkResult::advance() : WalkResult::interrupt();
+ }
return WalkResult::advance();
});
return r.wasInterrupted() ? failure() : success();
}
-/// LoadNd has a conflict if the tensor descriptor layout is different from the
-/// load's anchor layout.
-LogicalResult
-ResolveLayoutConflicts::resolveLoadNdOp(xegpu::LoadNdOp loadNdOp) {
- Attribute anchorLayout = loadNdOp.getLayoutAttr();
- Attribute tdescLayout = loadNdOp.getTensorDescType().getLayout();
+/// Helper to get the defining CreateNdDescOp of a tensor descriptor value. This
+/// function tries to find the defining CreateNdDescOp recursively accross
+/// control-flow boundaries.
+static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
+ // Try to get the defining CreateNdDescOp of the tensor descriptor.
+ auto definingOp = tdescValue.getDefiningOp<xegpu::CreateNdDescOp>();
+ if (definingOp)
+ return definingOp;
+ // If tdescValue is an argument, try to get the tied init value from the
+ // parent loop-like op.
+ if (auto arg = dyn_cast<BlockArgument>(tdescValue)) {
+ auto *parentOp = arg.getOwner()->getParentOp();
+ if (auto loop = dyn_cast<LoopLikeOpInterface>(parentOp)) {
+ OpOperand *tiedInit = loop.getTiedLoopInit(arg);
+ if (tiedInit)
+ return getDefiningCreateNdDescOp(tiedInit->get());
+ }
+ }
+ // If not found, return null.
+ return nullptr;
+}
- if (anchorLayout && tdescLayout && anchorLayout != tdescLayout) {
+LogicalResult
+ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
+ Operation *consumerOp = operand.getOwner();
+ Value tdescValue = operand.get();
+ auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(consumerOp);
+ auto currTDescType = dyn_cast<xegpu::TensorDescType>(tdescValue.getType());
+ assert(anchorOp && currTDescType &&
+ "Expected anchor layout op and tensor descriptor consumer.");
+ Attribute currLayout = currTDescType.getLayout();
+ Attribute expectedLayout = anchorOp.getAnchorLayout();
+ // A conflict exists in tensot descriptor operand if tensor descriptor's
+ // layout is different from the anchor layout expected by the consumer.
+ if (expectedLayout && currLayout && expectedLayout != currLayout) {
// Try to get the defining CreateNdDescOp of the tensor descriptor.
- auto conflictingCreateNdOp =
- loadNdOp.getTensorDesc().getDefiningOp<xegpu::CreateNdDescOp>();
+ auto conflictingCreateNdOp = getDefiningCreateNdDescOp(tdescValue);
if (!conflictingCreateNdOp) {
DBGS() << "Unable to find defining CreateNdDescOp for tensor descriptor: "
- << loadNdOp.getTensorDesc() << "\n";
+ << tdescValue << "\n";
return failure();
}
// Duplicate the CreateNdDescOp with the expected layout.
builder.setInsertionPointAfter(conflictingCreateNdOp);
- xegpu::TensorDescType tdescType = loadNdOp.getTensorDescType();
- auto expectedLayout = anchorLayout;
auto newTensorDescType = xegpu::TensorDescType::get(
- conflictingCreateNdOp.getContext(), tdescType.getShape(),
- tdescType.getElementType(), tdescType.getEncoding(), expectedLayout);
+ conflictingCreateNdOp.getContext(), currTDescType.getShape(),
+ currTDescType.getElementType(), currTDescType.getEncoding(),
+ expectedLayout);
xegpu::CreateNdDescOp newOp = xegpu::CreateNdDescOp::create(
- builder, loadNdOp.getLoc(), newTensorDescType,
+ builder, consumerOp->getLoc(), newTensorDescType,
conflictingCreateNdOp->getOperands(),
conflictingCreateNdOp->getAttrs());
- // Replace only the conflicting uses of the createNdOp that can be
- // resolved using the new layout.
- conflictingCreateNdOp->replaceUsesWithIf(
- ArrayRef<Value>(newOp.getResult()), [&](OpOperand &opnd) {
- auto userLoadNdOp = dyn_cast<xegpu::LoadNdOp>(opnd.getOwner());
- if (!userLoadNdOp)
- return false;
- return userLoadNdOp.getLayoutAttr() == expectedLayout;
- });
+ // Replace the tensor descriptor operand in the consumer op with the new
+ // tensor descriptor.
+ consumerOp->replaceUsesOfWith(tdescValue, newOp.getResult());
}
return success();
}
diff --git a/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir b/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
index dd3f3c8bdc29e..d1dbe8bcff509 100644
--- a/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
+++ b/mlir/test/Dialect/XeGPU/resolve-layout-conflicts.mlir
@@ -2,6 +2,7 @@
#load_lo = #xegpu.layout<inst_data = [8, 16]>
#prefetch_lo = #xegpu.layout<inst_data = [16, 16]>
+#load_lo1 = #xegpu.layout<inst_data = [32, 16]>
gpu.module @test {
// CHECK-LABEL: func.func @load_nd_with_conflicting_tensor_desc
@@ -20,4 +21,61 @@ func.func @load_nd_with_conflicting_tensor_desc(%arg0: memref<64x64xf16>) -> vec
xegpu.prefetch_nd %0 [%c0, %c0] {layout = #prefetch_lo} : !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
return %1 : vector<16x16xf16>
}
+
+// CHECK-LABEL: func.func @multiple_tensor_desc_conflicts
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK-NEXT: %[[T0:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [8, 16]>>
+// CHECK-NEXT: %[[T1:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+// CHECK-NEXT: %[[T2:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [32, 16]>>
+// CHECK-NEXT: %{{.*}} = xegpu.load_nd %[[T0]][%[[C0]], %[[C0]]] <{layout = #xegpu.layout<inst_data = [8, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [8, 16]>> -> vector<32x16xf16>
+// CHECK-NEXT: %{{.*}} = xegpu.load_nd %[[T2]][%[[C0]], %[[C0]]] <{layout = #xegpu.layout<inst_data = [32, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [32, 16]>> -> vector<32x16xf16>
+// CHECK-NEXT: xegpu.prefetch_nd %[[T1]][%[[C0]], %[[C0]]] <{layout = #xegpu.layout<inst_data = [16, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<32x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+func.func @multiple_tensor_desc_conflicts(%arg0: memref<64x64xf16>) -> vector<32x16xf16> {
+ %c0 = arith.constant 0 : index
+ %tdesc1 = xegpu.create_nd_tdesc %arg0 : memref<64x64xf16>
+ -> !xegpu.tensor_desc<32x16xf16, #load_lo>
+ %load1 = xegpu.load_nd %tdesc1 [%c0, %c0] {layout = #load_lo} : !xegpu.tensor_desc<32x16xf16, #load_lo>
+ -> vector<32x16xf16>
+ %load2 = xegpu.load_nd %tdesc1 [%c0, %c0] {layout = #load_lo1} : !xegpu.tensor_desc<32x16xf16, #load_lo>
+ -> vector<32x16xf16>
+ xegpu.prefetch_nd %tdesc1 [%c0, %c0] {layout = #prefetch_lo} : !xegpu.tensor_desc<32x16xf16, #load_lo>
+ %result = arith.addf %load1, %load2 : vector<32x16xf16>
+ return %result : vector<32x16xf16>
+}
+
+// CHECK-LABEL: func.func @load_nd_with_conflicting_tensor_desc_in_loop
+// CHECK: %[[T0:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+// CHECK-NEXT: %[[T1:.*]] = xegpu.create_nd_tdesc %{{.*}} : memref<64x64xf16>
+// CHECK-SAME: -> !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [8, 16]>>
+// CHECK-NEXT: %{{.*}}:2 = scf.for %{{.*}} = %{{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %[[T0]])
+// CHECK-SAME: -> (vector<16x16xf16>, !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [16, 16]>>) {
+// CHECK-NEXT: %{{.*}} = xegpu.load_nd %[[T1]][%{{.*}}] <{layout = #xegpu.layout<inst_data = [8, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [8, 16]>> -> vector<16x16xf16>
+// CHECK: scf.yield %{{.*}}, %{{.*}} : vector<16x16xf16>, !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+// CHECK: xegpu.prefetch_nd %[[T0]][%{{.*}}] <{layout = #xegpu.layout<inst_data = [16, 16]>}> :
+// CHECK-SAME: !xegpu.tensor_desc<16x16xf16, #xegpu.layout<inst_data = [16, 16]>>
+// CHECK-NEXT: return %{{.*}}#0 : vector<16x16xf16>
+func.func @load_nd_with_conflicting_tensor_desc_in_loop(%arg0: memref<64x64xf16>) -> vector<16x16xf16> {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c4 = arith.constant 4 : index
+ %cst = arith.constant dense<0.0> : vector<16x16xf16>
+ %0 = xegpu.create_nd_tdesc %arg0 : memref<64x64xf16>
+ -> !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ %1:2 = scf.for %i = %c0 to %c4 step %c1 iter_args(%acc = %cst, %tdesc = %0) -> (vector<16x16xf16>, !xegpu.tensor_desc<16x16xf16, #prefetch_lo>) {
+ %2 = xegpu.load_nd %tdesc [%c0, %c0] {layout = #load_lo} : !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ -> vector<16x16xf16>
+ %3 = arith.addf %acc, %2 : vector<16x16xf16>
+ scf.yield %3, %tdesc : vector<16x16xf16>, !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ }
+ xegpu.prefetch_nd %0 [%c0, %c0] {layout = #prefetch_lo} : !xegpu.tensor_desc<16x16xf16, #prefetch_lo>
+ return %1#0 : vector<16x16xf16>
+}
}
>From e1c2d6c4a8652aeebcf0b6e1d57bf211d52ae1ca Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Mon, 26 Jan 2026 23:05:06 +0000
Subject: [PATCH 7/8] fix
---
.../XeGPU/Transforms/XeGPUPropagateLayout.cpp | 39 +++++++++++--------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index 878dc5486f24c..809dce09ff519 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -1461,24 +1461,22 @@ struct ResolveLayoutConflicts {
} // namespace
LogicalResult ResolveLayoutConflicts::run() {
- // Helper function to get the tensor descriptor operand, or null if none found
- // TODO: We assume only one tensor descriptor operand per op.
- auto getTensorDescOperand = [](Operation *op) -> OpOperand * {
- for (OpOperand &opnd : op->getOpOperands()) {
- if (isa<xegpu::TensorDescType>(opnd.get().getType())) {
- return &opnd;
- }
- }
- return nullptr;
- };
-
+ // Scan all operations in the parent operation and resolve layout conflicts at
+ // tensor descriptor and vector use points.
auto r = parentOp->walk([&](Operation *op) -> WalkResult {
- // Check if this op is an anchor op and at least one operand is a tensor
- // descriptor type.
- OpOperand *tdescOperand = getTensorDescOperand(op);
- if (isa<xegpu::AnchorLayoutInterface>(op) && tdescOperand) {
- auto res = resolveTensorDescConsumer(*tdescOperand);
- return succeeded(res) ? WalkResult::advance() : WalkResult::interrupt();
+ for (OpOperand &operand : op->getOpOperands()) {
+ // Handle conflicts in tensor descriptor operands.
+ Type operandType = operand.get().getType();
+ if (isa<xegpu::AnchorLayoutInterface>(op) &&
+ isa<xegpu::TensorDescType>(operandType)) {
+ auto res = resolveTensorDescConsumer(operand);
+ return succeeded(res) ? WalkResult::advance() : WalkResult::interrupt();
+ }
+ // Handle conflicts in vector operands.
+ if (isa<VectorType>(operandType)) {
+ auto res = resolveVectorConsumer(operand);
+ return succeeded(res) ? WalkResult::advance() : WalkResult::interrupt();
+ }
}
return WalkResult::advance();
});
@@ -1508,6 +1506,13 @@ static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
return nullptr;
}
+LogicalResult
+ResolveLayoutConflicts::resolveVectorConsumer(OpOperand &operand) {
+ // TODO: Implement vector consumer layout conflict resolution. Requires layout
+ // utilities.
+ return success();
+}
+
LogicalResult
ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
Operation *consumerOp = operand.getOwner();
>From 416b3edf2eb4e4639ef0904354da1190f3f2b92b Mon Sep 17 00:00:00 2001
From: Charitha Saumya <charitha.saumya.gusthinna.waduge at intel.com>
Date: Tue, 27 Jan 2026 17:48:09 +0000
Subject: [PATCH 8/8] fix
---
mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index 809dce09ff519..7825d5e5a7923 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -1521,6 +1521,12 @@ ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
auto currTDescType = dyn_cast<xegpu::TensorDescType>(tdescValue.getType());
assert(anchorOp && currTDescType &&
"Expected anchor layout op and tensor descriptor consumer.");
+ // TODO: Scattered tensor desc is not supported for now.
+ if (currTDescType.isScattered()) {
+ DBGS() << "Scattered tensor descriptor not supported: " << tdescValue
+ << "\n";
+ return failure();
+ }
Attribute currLayout = currTDescType.getLayout();
Attribute expectedLayout = anchorOp.getAnchorLayout();
// A conflict exists in tensot descriptor operand if tensor descriptor's
More information about the Mlir-commits
mailing list