brintos

brintos / llvm-project-archived public Read only

0
0
Text · 95.1 KiB · 0d1c5ee Raw
2044 lines · cpp
1//===- XeGPUSubgroupDistribute.cpp - XeGPU Subgroup Distribute Pass -------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "mlir/Dialect/GPU/IR/GPUDialect.h"9#include "mlir/Dialect/GPU/Utils/DistributionUtils.h"10#include "mlir/Dialect/Index/IR/IndexDialect.h"11#include "mlir/Dialect/MemRef/IR/MemRef.h"12#include "mlir/Dialect/Vector/IR/VectorOps.h"13#include "mlir/Dialect/Vector/Transforms/VectorDistribution.h"14#include "mlir/Dialect/XeGPU/IR/XeGPU.h"15#include "mlir/Dialect/XeGPU/Transforms/Passes.h"16#include "mlir/Dialect/XeGPU/Transforms/Transforms.h"17#include "mlir/Dialect/XeGPU/Utils/XeGPUUtils.h"18#include "mlir/Dialect/XeGPU/uArch/IntelGpuXe2.h"19#include "mlir/IR/AffineMap.h"20#include "mlir/IR/Attributes.h"21#include "mlir/IR/Builders.h"22#include "mlir/IR/BuiltinAttributes.h"23#include "mlir/IR/BuiltinOps.h"24#include "mlir/IR/BuiltinTypes.h"25#include "mlir/IR/Operation.h"26#include "mlir/IR/PatternMatch.h"27#include "mlir/IR/TypeRange.h"28#include "mlir/IR/Value.h"29#include "mlir/IR/Visitors.h"30#include "mlir/Interfaces/FunctionInterfaces.h"31#include "mlir/Support/LLVM.h"32#include "mlir/Transforms/DialectConversion.h"33#include "mlir/Transforms/GreedyPatternRewriteDriver.h"34#include "mlir/Transforms/InliningUtils.h"35#include "llvm/ADT/ArrayRef.h"36#include "llvm/ADT/STLExtras.h"37#include "llvm/ADT/SmallVector.h"38 39namespace mlir {40namespace xegpu {41#define GEN_PASS_DEF_XEGPUSUBGROUPDISTRIBUTE42#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"43} // namespace xegpu44} // namespace mlir45 46#define DEBUG_TYPE "xegpu-subgroup-distribute"47#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")48 49using namespace mlir;50 51static const char *const resolveSIMTTypeMismatch =52    "resolve_simt_type_mismatch"; // Attribute name for identifying53                                  // UnrelizedConversionCastOp added to resolve54                                  // SIMT type mismatches.55 56namespace {57 58//===----------------------------------------------------------------------===//59// SIMT Distribution Patterns60//===----------------------------------------------------------------------===//61 62/// In certain cases, we may need to favor XeGPU specific distribution patterns63/// over generic vector distribution patterns. In such cases, we can assign64/// priorities to patterns.65static constexpr unsigned regularPatternBenefit = 1;66static constexpr unsigned highPatternBenefit = 2;67 68/// Helper function to get  distributed vector type for a source vector type69/// according to the lane_layout. We simply divide each dimension of tensor70/// descriptor shape by corresponding lane_layout dimension. If71/// array_length > 1, that is appended to the front of the ditributed shape.72/// NOTE: This is the vector type that will be returned by the73/// gpu.warp_execute_on_lane0 op.74///75/// Examples:76/// | original vector shape | lane_layout | distributed vector shape |77/// |-----------------------|-------------|--------------------------|78/// | 32x16                 | [1, 16]     | 32x1                     |79/// | 32x16                 | [2, 8]      | 16x2                     |80/// | 2x32x16               | [1, 16]     | 2x32x1                   |81static FailureOr<VectorType>82getDistVecTypeBasedOnLaneLayout(xegpu::DistributeLayoutAttr layout,83                                VectorType originalType) {84  if (!layout)85    return failure();86  assert((isa<xegpu::LayoutAttr>(layout) || isa<xegpu::SliceAttr>(layout)) &&87         "Expecting a valid layout.");88  SmallVector<int64_t> effectiveLaneLayout =89      layout.getEffectiveLaneLayoutAsInt();90  assert(static_cast<size_t>(originalType.getRank()) >=91             effectiveLaneLayout.size() &&92         "Rank of the original vector type should be greater or equal to the "93         "size of the lane layout to distribute the vector type.");94  SmallVector<int64_t> distributedShape(originalType.getShape());95  // Only distribute the last `laneLayout.size()` dimensions. The remaining96  // dimensions are not distributed.97  unsigned distributionStart =98      originalType.getRank() - effectiveLaneLayout.size();99  for (auto [i, dim] : llvm::enumerate(originalType.getShape())) {100    if (i < distributionStart)101      continue;102 103    // Check if the dimension can be distributed evenly.104    if (dim % effectiveLaneLayout[i - distributionStart] != 0)105      return failure();106    distributedShape[i] = dim / effectiveLaneLayout[i - distributionStart];107  }108  return VectorType::get(distributedShape, originalType.getElementType());109}110 111/// Helper function to resolve types if the distributed type out of112/// gpu.warp_execute_on_lane0 is different from the expected xegpu SIMT type.113/// Example 1:114///   distributed type: vector<8x1xf32>115///   expected type: vector<8xf32>116///   resolved using,117///   %0 = vector.shape_cast %1 : vector<8x1xf32> to vector<8xf32>118/// Example 2:119///   distributed type: xegpu.tensor_desc<8x16xf32, #xegpu.layout<...>>120///   expected type: xegpu.tensor_desc<8x16xf32>121///   resolved using,122///   %0 = unrealized_conversion_cast %1 :123///      xegpu.tensor_desc<8x16xf32, #xegpu.layout<..>> ->124///      xegpu.tensor_desc<8x16xf32>125template <typename T>126static Value resolveDistributedTy(Value orig, T expected,127                                  PatternRewriter &rewriter) {128  // If orig and expected types are the same, return orig.129  if (orig.getType() == expected)130    return orig;131  // If orig is a vector type, create a shape cast op to reconcile the types.132  if (isa<VectorType>(orig.getType())) {133    auto castOp =134        vector::ShapeCastOp::create(rewriter, orig.getLoc(), expected, orig);135    return castOp.getResult();136  }137  // If orig is a tensor descriptor type, create an unrealized conversion cast138  // op to reconcile the types.139  if (isa<xegpu::TensorDescType>(orig.getType())) {140    auto castOp = UnrealizedConversionCastOp::create(rewriter, orig.getLoc(),141                                                     expected, orig);142    castOp->setAttr(resolveSIMTTypeMismatch, rewriter.getUnitAttr());143    return castOp.getResult(0);144  }145  llvm_unreachable("Unsupported type for reconciliation");146  return orig;147}148 149/// Helper function to check if the layout is packed. Layout is packed if it is150/// 2D and lane_data[0] != 1 (data packed from col dimension).151/// TODO: Move to target info.152static bool requirePacked(const xegpu::LayoutAttr layout) {153  if (!layout)154    return false;155  auto laneData = layout.getEffectiveLaneDataAsInt();156  if (laneData.size() != 2)157    return false;158  return laneData[0] != 1;159}160 161/// Helper function to check if the layout requires a transpose effect.162static bool requireTranspose(const xegpu::LayoutAttr layout,163                             const xegpu::uArch::uArch *uArch) {164  // Return false for unsupported targets.165  // TODO: Add more support or move to target info.166  if (uArch->getName().equals_insensitive("pvc") &&167      uArch->getName().equals_insensitive("bmg"))168    return false;169  if (!layout)170    return false;171  auto laneLayout = layout.getEffectiveLaneLayoutAsInt();172  if (laneLayout.size() != 2)173    return false;174  return laneLayout[0] == uArch->getSubgroupSize() && laneLayout[1] == 1;175}176 177/// Given a vector type and its distributed vector type, return the list of178/// dimensions that are distributed.179static SmallVector<int64_t> getDistributedDims(VectorType originalType,180                                               VectorType distributedType) {181  assert(originalType.getRank() == distributedType.getRank() &&182         "sequential and distributed vector types must have the same rank");183  SmallVector<int64_t> distributedDims;184  for (int64_t i = 0; i < originalType.getRank(); ++i) {185    if (distributedType.getDimSize(i) != originalType.getDimSize(i)) {186      distributedDims.push_back(i);187    }188  }189  return distributedDims;190}191 192/// Given a GPUFuncOp, this pattern creates a new GPUFuncOp and moves the body193/// of the original GPUFuncOp to the new GPUFuncOp such that entire body is194/// contained within a WarpExecuteOnLane0Op.195/// Example:196///197/// ```198///   gpu.func @foo(%arg0: memref<*xf16>) -> vector<8x16xf32> {199///     ...200///     ...201///     gpu.return %result: vector<8x16xf32>202///   }203/// ```204/// To205/// ```206///   gpu.func @foo(%arg0: memref<*xf16>) -> vector<8x16xf32> {207///     %laneid = gpu.lane_id : index208///     %0 = gpu.warp_execute_on_lane_0(%laneid) -> vector<8x16xf32> {209///       ...210///       ...211///       gpu.yield %result: vector<8x16xf32>212///     }213///     return %0214///   }215struct MoveFuncBodyToWarpOp : public OpRewritePattern<gpu::GPUFuncOp> {216  using OpRewritePattern<gpu::GPUFuncOp>::OpRewritePattern;217  LogicalResult matchAndRewrite(gpu::GPUFuncOp gpuFuncOp,218                                PatternRewriter &rewriter) const override {219    auto uArch = getUArch(xegpu::getChipStr(gpuFuncOp).value_or(""));220    if (!uArch)221      return rewriter.notifyMatchFailure(222          gpuFuncOp, "Subgroup distribution requires target attribute attached "223                     "to set the warp size");224    // If the function only contains a single void return, skip.225    if (llvm::all_of(gpuFuncOp.getBody().getOps(), [](Operation &op) {226          return isa<gpu::ReturnOp>(op) && !op.getNumOperands();227        }))228      return failure();229    // If the function already moved inside a warp_execute_on_lane0, skip.230    if (llvm::any_of(gpuFuncOp.getBody().getOps(), [](Operation &op) {231          return isa<gpu::WarpExecuteOnLane0Op>(op);232        }))233      return failure();234    // Create a new function with the same signature and same attributes.235    SmallVector<Type> workgroupAttributionsTypes =236        llvm::map_to_vector(gpuFuncOp.getWorkgroupAttributions(),237                            [](BlockArgument arg) { return arg.getType(); });238    SmallVector<Type> privateAttributionsTypes =239        llvm::map_to_vector(gpuFuncOp.getPrivateAttributions(),240                            [](BlockArgument arg) { return arg.getType(); });241    auto newGpuFunc = gpu::GPUFuncOp::create(242        rewriter, gpuFuncOp.getLoc(), gpuFuncOp.getName(),243        gpuFuncOp.getFunctionType(), workgroupAttributionsTypes,244        privateAttributionsTypes);245    newGpuFunc->setAttrs(gpuFuncOp->getAttrs());246    // Create a WarpExecuteOnLane0Op with same arguments and results as the247    // original gpuFuncOp.248    rewriter.setInsertionPointToEnd(&newGpuFunc.getFunctionBody().front());249    auto laneId = gpu::LaneIdOp::create(250        rewriter, newGpuFunc.getLoc(), rewriter.getIndexType(),251        /** upperBound = **/ mlir::IntegerAttr());252    ArrayRef<Type> gpuFuncResultType = gpuFuncOp.getFunctionType().getResults();253    auto warpOp = gpu::WarpExecuteOnLane0Op::create(254        rewriter, laneId.getLoc(), gpuFuncResultType, laneId,255        uArch->getSubgroupSize(), newGpuFunc.getArguments(),256        newGpuFunc.getArgumentTypes());257    Block &warpBodyBlock = warpOp.getBodyRegion().front();258    // Replace the ReturnOp of the original gpu function with a YieldOp.259    auto origRetunOp =260        cast<gpu::ReturnOp>(gpuFuncOp.getBlocks().back().getTerminator());261    rewriter.setInsertionPointAfter(origRetunOp);262    gpu::YieldOp::create(rewriter, origRetunOp.getLoc(),263                         origRetunOp.getOperands());264    rewriter.eraseOp(origRetunOp);265    // Move the original function body to the WarpExecuteOnLane0Op body.266    rewriter.inlineRegionBefore(gpuFuncOp.getBody(), warpOp.getBodyRegion(),267                                warpOp.getBodyRegion().begin());268    rewriter.eraseBlock(&warpBodyBlock);269    // Insert a new ReturnOp after the WarpExecuteOnLane0Op.270    rewriter.setInsertionPointAfter(warpOp);271    gpu::ReturnOp::create(rewriter, newGpuFunc.getLoc(), warpOp.getResults());272    rewriter.replaceOp(gpuFuncOp, newGpuFunc);273    return success();274  }275};276 277/// Distribute a create_nd_tdesc feeding into vector.yield op of the enclosing278/// `gpu.warp_execute_on_lane_0` region. After the sinking, the warp op will279/// still contain the original op that will not be used by the yield op (and280/// should be cleaned up later). The yield op will bypass the create_nd_tdesc's281/// arguments. Tensor descriptor shape is not distributed because it is a282/// uniform value across all work items within the subgroup. However, the283/// layout information is dropped in the new tensor descriptor type.284///285/// Example:286///287/// ```288///   #layout0 = #xegpu.layout<wi_layout = [1, 8], wi_data = [1, 1]>289///   %r = gpu.warp_execute_on_lane_0(%laneid) ->290///                   (!xegpu.tensor_desc<4x8xf32, #layout0>) {291///     ...292///     %td = xegpu.create_nd_tdesc %arg0293///               : memref<4x8xf32> -> !xegpu.tensor_desc<4x8xf32, #layout0>294///     vector.yield %td295///   }296/// ```297/// To298/// ```299///   %r:2 = gpu.warp_execute_on_lane_0(%laneid) -> (...) {300///     ...301///     %dead = xegpu.create_nd_tdesc %arg0302///               : memref<4x8xf32> -> !xegpu.tensor_desc<4x8xf32, #layout0>303///     vector.yield %arg0, %dead304///   }305///   %td = xegpu.create_nd_tdesc %r#0: memref<4x8xf32>306///                                 -> !xegpu.tensor_desc<4x8xf32>307///308/// ```309struct CreateNdDescDistribution final : public gpu::WarpDistributionPattern {310  using gpu::WarpDistributionPattern::WarpDistributionPattern;311  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,312                                PatternRewriter &rewriter) const override {313    OpOperand *operand =314        getWarpResult(warpOp, llvm::IsaPred<xegpu::CreateNdDescOp>);315    if (!operand)316      return rewriter.notifyMatchFailure(317          warpOp, "warp result is not a xegpu::CreateNdDesc op");318    auto descOp = operand->get().getDefiningOp<xegpu::CreateNdDescOp>();319    unsigned operandIdx = operand->getOperandNumber();320 321    xegpu::LayoutAttr layout = descOp.getType().getLayoutAttr();322    if (!layout)323      return rewriter.notifyMatchFailure(324          descOp, "the tensor descriptor lacks layout attribute");325    // CreateNdOp must not have offsets.326    if (descOp.getMixedOffsets().size())327      return rewriter.notifyMatchFailure(328          descOp, "xegpu::CreateNdDescOp must not have offsets");329 330    SmallVector<size_t> newRetIndices;331    rewriter.setInsertionPoint(warpOp);332    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(333        rewriter, warpOp, /* new yieled values = */ descOp->getOperands(),334        /* new yielded types = */ descOp.getOperandTypes(), newRetIndices);335 336    SmallVector<Value> newDescOperands = llvm::map_to_vector(337        newRetIndices, [&](size_t i) { return newWarpOp.getResult(i); });338    rewriter.setInsertionPointAfter(newWarpOp);339    xegpu::TensorDescType distributedTensorDescTy =340        descOp.getType().dropLayouts(); // Distributed tensor descriptor type341                                        // does not contain layout info.342    Value newDescOp = xegpu::CreateNdDescOp::create(343        rewriter, newWarpOp.getLoc(), distributedTensorDescTy, newDescOperands,344        descOp->getAttrs());345 346    Value distributedVal = newWarpOp.getResult(operandIdx);347    // Resolve the distributed type to the expected type.348    newDescOp =349        resolveDistributedTy(newDescOp, distributedVal.getType(), rewriter);350    rewriter.replaceAllUsesWith(distributedVal, newDescOp);351    return success();352  }353};354 355/// Distribute a store_nd op at the end of enclosing356/// `gpu.warp_execute_on_lane_0`. In case arguments for the store are passed357/// through the warp op interface they would be propagated as returned values.358/// Source vector is distributed based on lane layout. Appropriate cast ops are359/// inserted if the distributed types does not match expected xegpu SIMT types.360///361/// Example:362///363/// ```364///   #layout0 = #xegpu.layout<wi_layout = [1, 8], wi_data = [1, 1]>365///   gpu.warp_execute_on_lane_0(%laneid) -> () {366///     ...367///     xegpu.store_nd %arg0, %arg1 [%x, %y]: vector<4x8xf32>,368///                                 !xegpu.tensor_desc<4x8xf32, #layout0>369///   }370/// ```371/// To372/// ```373///   %r:2 = gpu.warp_execute_on_lane_0(%laneid) -> (vector<4x1xf32>,374///   !xegpu.tensor_desc<4x8xf32, #layout0>, index, index) {375///     ...376///     gpu.yield %arg0, %arg1, %x, %y: vector<4x8xf32>,377///     !xegpu.tensor_desc<4x8xf32, #layout0>, index, index378///   }379///   %0 = vector.shape_cast %r#0: vector<4x1xf32> to vector<4xf32>380///   %1 = unrealized_conversion_cast %r#1: !xegpu.tensor_desc<4x8xf32,381///   #layout0>382///     -> !xegpu.tensor_desc<4x8xf32>383///   xegpu.store_nd %0, %1 [%r#2, %r#3]: vector<4xf32>,384///     !xegpu.tensor_desc<4x8xf32>385///386/// ```387struct StoreNdDistribution final : public gpu::WarpDistributionPattern {388  using gpu::WarpDistributionPattern::WarpDistributionPattern;389  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,390                                PatternRewriter &rewriter) const override {391    gpu::YieldOp yield = warpOp.getTerminator();392    Operation *lastNode = yield->getPrevNode();393    auto storeOp = dyn_cast_or_null<xegpu::StoreNdOp>(lastNode);394    if (!storeOp)395      return failure();396 397    SmallVector<OpFoldResult> offsets = storeOp.getMixedOffsets();398    // Expecting offsets to be present.399    if (offsets.empty())400      return rewriter.notifyMatchFailure(storeOp,401                                         "the store op must have offsets");402    SmallVector<Value> offsetsAsValues =403        vector::getAsValues(rewriter, storeOp.getLoc(), offsets);404    SmallVector<Type> offsetTypes = llvm::to_vector(405        llvm::map_range(offsetsAsValues, [](Value v) { return v.getType(); }));406    xegpu::TensorDescType tensorDescTy = storeOp.getTensorDescType();407    xegpu::LayoutAttr layout = tensorDescTy.getLayoutAttr();408    if (!layout)409      return rewriter.notifyMatchFailure(410          storeOp, "the source tensor descriptor lacks layout attribute");411 412    FailureOr<VectorType> distributedTypeByWarpOpOrFailure =413        getDistVecTypeBasedOnLaneLayout(layout, storeOp.getValueType());414    if (failed(distributedTypeByWarpOpOrFailure))415      return rewriter.notifyMatchFailure(storeOp,416                                         "Failed to distribute the type");417    VectorType distributedTypeByWarpOp =418        distributedTypeByWarpOpOrFailure.value();419 420    SmallVector<size_t> newRetIndices;421    SmallVector<Value> newYieldedValues = {storeOp.getValue(),422                                           storeOp.getTensorDesc()};423    SmallVector<Type> newYieldedTypes = {distributedTypeByWarpOp, tensorDescTy};424    newYieldedValues.append(offsetsAsValues.begin(), offsetsAsValues.end());425    newYieldedTypes.append(offsetTypes.begin(), offsetTypes.end());426    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(427        rewriter, warpOp, newYieldedValues, newYieldedTypes, newRetIndices);428    // Create a new store op outside the warp op with the distributed vector429    // type. Tensor descriptor is not distributed.430    rewriter.setInsertionPointAfter(newWarpOp);431    SmallVector<Value> newStoreOperands;432 433    // For the value operand, there can be a mismatch between the vector type434    // distributed by the warp op and (xegpu-specific) distributed type435    // supported by the store op. Type mismatch must be resolved using436    // appropriate cast op.437    FailureOr<VectorType> storeNdDistributedValueTyOrFailure =438        xegpu::getDistributedVectorType(storeOp.getTensorDescType());439    if (failed(storeNdDistributedValueTyOrFailure))440      return rewriter.notifyMatchFailure(441          storeOp, "Failed to get distributed vector type for the store op");442    newStoreOperands.push_back(resolveDistributedTy(443        newWarpOp.getResult(newRetIndices[0]),444        storeNdDistributedValueTyOrFailure.value(), rewriter));445    // For the tensor descriptor operand, the layout attribute is dropped after446    // distribution. Types needs to be resolved in this case also.447    xegpu::TensorDescType distributedTensorDescTy =448        storeOp.getTensorDescType().dropLayouts();449    newStoreOperands.push_back(450        resolveDistributedTy(newWarpOp.getResult(newRetIndices[1]),451                             distributedTensorDescTy, rewriter));452    // Collect offsets.453    for (size_t i = 2; i < newRetIndices.size(); ++i)454      newStoreOperands.push_back(newWarpOp.getResult(newRetIndices[i]));455 456    auto newStoreOp =457        xegpu::StoreNdOp::create(rewriter, newWarpOp.getLoc(), TypeRange{},458                                 newStoreOperands, storeOp->getAttrs());459    xegpu::removeLayoutAttrs(newStoreOp);460    rewriter.eraseOp(storeOp);461    return success();462  }463};464 465/// Distribute a load_nd op feeding into vector.yield op for the enclosing466/// `gpu.warp_execute_on_lane_0` and put it after the warp op.467/// The warp op will still contain the original op that will not be used by468/// the yield op (and should be cleaned up later). The yield op will469/// bypass the load's arguments. Only the loaded vector is distributed470/// according to lane layout and, tensor descriptor types is not471/// distributed. Appropriate cast ops are inserted if the distributed types does472/// not match expected xegpu SIMT types.473///474/// Example:475///476/// ```477///   #layout0 = #xegpu.layout<wi_layout = [1, 8], wi_data = [1, 1]>478///   %r = gpu.warp_execute_on_lane_0(%laneid) ->479///                   (vector<4x1xf32>) {480///     ...481///     %ld = xegpu.load_nd %arg0, %arg1: !xegpu.tensor_desc<4x8xf32, #layout0>482///     ->483///       vector<4x8xf32>484///     gpu.yield %ld485///   }486/// ```487/// To488/// ```489///   %r:2 = gpu.warp_execute_on_lane_0(%laneid) -> (vector<4x1xf32>,490///   !xegpu.tensor_desc<4x8xf32, #layout0>) {491///     ...492///     %dead = xegpu.load_nd %arg0: !xegpu.tensor_desc<4x8xf32, #layout0> ->493///     vector<4x8xf32> gpu.yield %dead, %arg0494///   }495///   %0 = unrealized_conversion_cast %r#1: !xegpu.tensor_desc<4x8xf32,496///        #layout0> -> !xegpu.tensor_desc<4x8xf32>497///   %1 = xegpu.load_nd %0: !xegpu.tensor_desc<4x8xf32> -> vector<4xf32>498///   %2 = vector.shape_cast %r#0: vector<4xf32> to vector<4x1xf32>499///500/// ```501struct LoadNdDistribution final : public gpu::WarpDistributionPattern {502  using gpu::WarpDistributionPattern::WarpDistributionPattern;503  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,504                                PatternRewriter &rewriter) const override {505    OpOperand *operand = getWarpResult(warpOp, [&](Operation *op) {506      if (!isa<xegpu::LoadNdOp>(op))507        return false;508      // Make sure the same load op is the last operation in the warp op body.509      // This ensure that load op is not sinked earlier violating any barrier510      // synchronizations.511      gpu::YieldOp yield = warpOp.getTerminator();512      return yield->getPrevNode() == op;513    });514 515    if (!operand)516      return rewriter.notifyMatchFailure(517          warpOp, "warp result is not a xegpu::LoadNd op");518 519    auto loadOp = operand->get().getDefiningOp<xegpu::LoadNdOp>();520    auto uArch = getUArch(xegpu::getChipStr(loadOp).value_or(""));521    if (!uArch)522      return rewriter.notifyMatchFailure(523          loadOp, "xegpu::LoadNdOp require target attribute attached to "524                  "determine transpose "525                  "requirement");526    // Chip information is required to decide if the layout requires transpose527    // effect.528    // Expecting offsets to be present.529    SmallVector<OpFoldResult> offsets = loadOp.getMixedOffsets();530    if (offsets.empty())531      return rewriter.notifyMatchFailure(loadOp,532                                         "the load op must have offsets");533    SmallVector<Value> offsetsAsValues =534        vector::getAsValues(rewriter, loadOp.getLoc(), offsets);535    SmallVector<Type> offsetTypes = llvm::to_vector(536        llvm::map_range(offsetsAsValues, [](Value v) { return v.getType(); }));537 538    xegpu::TensorDescType tensorDescTy = loadOp.getTensorDescType();539    xegpu::LayoutAttr layout = tensorDescTy.getLayoutAttr();540    if (!layout)541      return rewriter.notifyMatchFailure(542          loadOp, "the source tensor descriptor lacks layout attribute");543 544    unsigned operandIdx = operand->getOperandNumber();545    VectorType distributedTypeByWarpOp =546        cast<VectorType>(warpOp.getResult(operandIdx).getType());547 548    SmallVector<size_t> newRetIndices;549    SmallVector<Value> newYieldedValues = {loadOp.getTensorDesc()};550    SmallVector<Type> newYieldedTypes = {tensorDescTy};551    newYieldedValues.append(offsetsAsValues.begin(), offsetsAsValues.end());552    newYieldedTypes.append(offsetTypes.begin(), offsetTypes.end());553    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(554        rewriter, warpOp, newYieldedValues, newYieldedTypes, newRetIndices);555 556    // Create a new load op outside the warp op with the distributed vector557    // type.558    rewriter.setInsertionPointAfter(newWarpOp);559    FailureOr<VectorType> loadNdDistValueTyOrFailure =560        xegpu::getDistributedVectorType(loadOp.getTensorDescType());561    if (failed(loadNdDistValueTyOrFailure))562      return rewriter.notifyMatchFailure(563          loadOp, "Failed to get distributed vector type for the load op");564    xegpu::TensorDescType distributedTensorDescTy =565        loadOp.getTensorDescType().dropLayouts(); // Distributed tensor566                                                  // descriptor type does not567                                                  // contain layout info.568    SmallVector<Value> newLoadOperands{569        resolveDistributedTy(newWarpOp.getResult(newRetIndices[0]),570                             distributedTensorDescTy, rewriter)};571    // Collect offsets.572    for (size_t i = 1; i < newRetIndices.size(); ++i)573      newLoadOperands.push_back(newWarpOp.getResult(newRetIndices[i]));574    auto newLoadOp = xegpu::LoadNdOp::create(575        rewriter, newWarpOp.getLoc(), loadNdDistValueTyOrFailure.value(),576        newLoadOperands, loadOp->getAttrs());577    xegpu::removeLayoutAttrs(newLoadOp);578    // Set the packed attribute if the layout requires it.579    newLoadOp.setPacked(requirePacked(layout));580    // Set the transpose attribute if the layout requires it.581    if (requireTranspose(layout, uArch))582      newLoadOp.setTranspose(583          DenseI64ArrayAttr::get(rewriter.getContext(), {1, 0}));584    Value distributedVal = newWarpOp.getResult(operandIdx);585    // There can be a conflict between the vector type distributed by the586    // warp op and (xegpu-specific) distributed type supported by the load587    // op. Resolve these mismatches by inserting a cast.588    Value tyResolvedVal = resolveDistributedTy(589        newLoadOp.getResult(), distributedTypeByWarpOp, rewriter);590    rewriter.replaceAllUsesWith(distributedVal, tyResolvedVal);591    return success();592  }593};594 595/// Distribute a dpas op feeding into vector.yield op for the enclosing596/// `gpu.warp_execute_on_lane_0` and put it after the warp op.597/// The warp op will still contain the original op that will not be used by598/// the yield op (and should be cleaned up later). The yield op will599/// bypass the dpas's arguments. Appropriate cast ops are inserted if the600/// distributed types does not match expected xegpu SIMT types.601/// Example:602/// ```603///   #lo_a = #xegpu.layout<wi_layout = [1, 16], wi_data = [1, 1]>604///   #lo_b = #xegpu.layout<wi_layout = [1, 16], wi_data = [2, 1]>605///   #lo_c = #xegpu.layout<wi_layout = [1, 16], wi_data = [1, 1]>606///   %r = gpu.warp_execute_on_lane_0(%laneid) ->607///                   (vector<8x1xf32>) {608///     ...609///     %dpas = xegpu.dpas %arg0, %arg1: vector<8x16xf16>, vector<16x16xf16> ->610///       vector<8x16xf32>611///     gpu.yield %dpas612///   }613/// ```614/// To615/// ```616///   %r:2 = gpu.warp_execute_on_lane_0(%laneid) -> (vector<8x1xf32>,617///   vector<8x1xf16>, vector<16x1xf16>) {618///     ...619///     %dead = xegpu.dpas %arg0, %arg1: vector<8x16xf16>, vector<16x16xf16>620///       -> vector<8x16xf32>621///     gpu.yield %dead, %arg0, %arg1622///   }623///   %0 = vector.shape_cast %r#1: vector<8x1xf16> to vector<8xf16>624///   %1 = vector.shape_cast %r#2: vector<16x1xf16> to vector<16xf16>625///   %2 = xegpu.dpas %0, %1: vector<8xf16>, vector<16xf16> ->626///     vector<8xf32>627///   %dpas = vector.shape_cast %2: vector<8xf32> to vector<8x1xf32>628/// ```629struct DpasDistribution final : public gpu::WarpDistributionPattern {630  using gpu::WarpDistributionPattern::WarpDistributionPattern;631  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,632                                PatternRewriter &rewriter) const override {633    OpOperand *operand = getWarpResult(warpOp, llvm::IsaPred<xegpu::DpasOp>);634    if (!operand)635      return rewriter.notifyMatchFailure(warpOp,636                                         "warp result is not a xegpu::Dpas op");637 638    auto dpasOp = operand->get().getDefiningOp<xegpu::DpasOp>();639    unsigned operandIdx = operand->getOperandNumber();640    std::string layoutAName = xegpu::getLayoutName(dpasOp->getOpOperand(0));641    std::string layoutBName = xegpu::getLayoutName(dpasOp->getOpOperand(1));642    std::string layoutCName = xegpu::getLayoutName(dpasOp->getOpResult(0));643 644    xegpu::LayoutAttr layoutA =645        dpasOp->getAttrOfType<xegpu::LayoutAttr>(layoutAName);646    xegpu::LayoutAttr layoutB =647        dpasOp->getAttrOfType<xegpu::LayoutAttr>(layoutBName);648    xegpu::LayoutAttr layoutOut =649        dpasOp->getAttrOfType<xegpu::LayoutAttr>(layoutCName);650    if (!layoutA || !layoutB || !layoutOut)651      return rewriter.notifyMatchFailure(652          dpasOp,653          "the xegpu::Dpas op lacks layout attribute for A, B or output");654 655    FailureOr<VectorType> distLhsTypeByWarpOpOrFailure =656        getDistVecTypeBasedOnLaneLayout(layoutA, dpasOp.getLhsType());657    FailureOr<VectorType> distRhsTypeByWarpOpOrFailure =658        getDistVecTypeBasedOnLaneLayout(layoutB, dpasOp.getRhsType());659    FailureOr<VectorType> distResultTypeByWarpOpOrFailure =660        getDistVecTypeBasedOnLaneLayout(layoutOut, dpasOp.getResultType());661    if (failed(distLhsTypeByWarpOpOrFailure) ||662        failed(distRhsTypeByWarpOpOrFailure) ||663        failed(distResultTypeByWarpOpOrFailure))664      return rewriter.notifyMatchFailure(665          dpasOp,666          "Failed to distribute the A, B or output types in xegpu::Dpas op");667 668    llvm::SmallVector<Value, 3> newYieldValues{dpasOp.getLhs(),669                                               dpasOp.getRhs()};670    llvm::SmallVector<Type, 3> newYieldTypes{671        distLhsTypeByWarpOpOrFailure.value(),672        distRhsTypeByWarpOpOrFailure.value()};673    // Dpas acc operand is optional.674    if (dpasOp.getAcc()) {675      newYieldValues.push_back(dpasOp.getAcc());676      newYieldTypes.push_back(distResultTypeByWarpOpOrFailure.value());677    }678    // Create a new warp op without the dpas.679    SmallVector<size_t> newRetIndices;680    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(681        rewriter, warpOp, newYieldValues, newYieldTypes, newRetIndices);682 683    FailureOr<VectorType> expectedDistLhsTyOrFailure =684        xegpu::getDistributedVectorType(dpasOp.getLhsType(), layoutA);685    FailureOr<VectorType> expectedDistRhsTyOrFailure =686        xegpu::getDistributedVectorType(dpasOp.getRhsType(), layoutB);687    FailureOr<VectorType> expectedDistResultTyOrFailure =688        xegpu::getDistributedVectorType(dpasOp.getResultType(), layoutOut);689    if (failed(expectedDistLhsTyOrFailure) ||690        failed(expectedDistRhsTyOrFailure) ||691        failed(expectedDistResultTyOrFailure))692      return rewriter.notifyMatchFailure(693          dpasOp,694          "Failed to get distributed vector type for the dpas operands.");695    // Create a new dpas op outside the warp op.696    rewriter.setInsertionPointAfter(newWarpOp);697    SmallVector<Value> newDpasOperands;698    SmallVector<VectorType> newDpasOperandExpectedTypes;699 700    // Resolve the distributed types with the original types.701    newDpasOperandExpectedTypes.push_back(expectedDistLhsTyOrFailure.value());702    newDpasOperandExpectedTypes.push_back(expectedDistRhsTyOrFailure.value());703    VectorType distributedResultTy = expectedDistResultTyOrFailure.value();704    if (dpasOp.getAcc())705      newDpasOperandExpectedTypes.push_back(distributedResultTy);706 707    for (unsigned i = 0; i < newRetIndices.size(); i++) {708      newDpasOperands.push_back(709          resolveDistributedTy(newWarpOp.getResult(newRetIndices[i]),710                               newDpasOperandExpectedTypes[i], rewriter));711    }712    auto newDpasOp = xegpu::DpasOp::create(rewriter, newWarpOp->getLoc(),713                                           distributedResultTy, newDpasOperands,714                                           dpasOp->getAttrs());715    xegpu::removeLayoutAttrs(newDpasOp);716    Value distributedVal = newWarpOp.getResult(operandIdx);717    // Resolve the output type.718    Value typeResolved =719        resolveDistributedTy(newDpasOp.getResult(),720                             distResultTypeByWarpOpOrFailure.value(), rewriter);721    rewriter.replaceAllUsesWith(distributedVal, typeResolved);722    return success();723  }724};725 726/// Distribute a prefetch_nd op at the end of enclosing727/// `gpu.warp_execute_on_lane_0`. In case arguments for the prefetch are passed728/// through the warp op interface they would be propagated as returned values.729/// Tensor descriptor shape is not distributed because it is a uniform value730/// across all work items within the subgroup. Appropriate cast ops are inserted731/// if the distributed types does not match expected xegpu SIMT types.732///733/// Example:734///735/// ```736///   #layout0 = #xegpu.layout<wi_layout = [1, 8], wi_data = [1, 1]>737///   gpu.warp_execute_on_lane_0(%laneid) -> () {738///     ...739///     xegpu.prefetch_nd %arg0 [%x, %y] : !xegpu.tensor_desc<4x8xf32, #layout0>740///   }741/// ```742/// To743/// ```744///   %r:1 = gpu.warp_execute_on_lane_0(%laneid) -> (745///    !xegpu.tensor_desc<4x8xf32, #layout0>, index, index) {746///     gpu.yield %arg0, %x, %y: !xegpu.tensor_desc<4x8xf32, #layout0>, index,747///     index748///   }749///   %1 = unrealized_conversion_cast %r#0: !xegpu.tensor_desc<4x8xf32,750///     #layout0> -> !xegpu.tensor_desc<4x8xf32>751///   xegpu.prefetch_nd %1 [%r#1, %r#2] : !xegpu.tensor_desc<4x8xf32>752///753/// ```754struct PrefetchNdDistribution final : public gpu::WarpDistributionPattern {755  using gpu::WarpDistributionPattern::WarpDistributionPattern;756  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,757                                PatternRewriter &rewriter) const override {758    gpu::YieldOp yield = warpOp.getTerminator();759    Operation *lastNode = yield->getPrevNode();760    auto prefetchOp = dyn_cast_or_null<xegpu::PrefetchNdOp>(lastNode);761    if (!prefetchOp)762      return failure();763 764    SmallVector<OpFoldResult> offsets = prefetchOp.getMixedOffsets();765    // PrefetchNdOp must have offsets.766    if (offsets.empty())767      return rewriter.notifyMatchFailure(prefetchOp,768                                         "the prefetch op must have offsets");769    SmallVector<Value> offsetsAsValues =770        vector::getAsValues(rewriter, prefetchOp.getLoc(), offsets);771    SmallVector<Type> offsetTypes = llvm::to_vector(772        llvm::map_range(offsetsAsValues, [](Value v) { return v.getType(); }));773 774    xegpu::LayoutAttr layout = prefetchOp.getTensorDescType().getLayoutAttr();775    if (!layout)776      return rewriter.notifyMatchFailure(777          prefetchOp, "the source tensor descriptor lacks layout attribute");778 779    SmallVector<Value> newYieldValues = {prefetchOp.getTensorDesc()};780    SmallVector<Type> newYieldTypes = {prefetchOp.getTensorDescType()};781    newYieldValues.append(offsetsAsValues.begin(), offsetsAsValues.end());782    newYieldTypes.append(offsetTypes.begin(), offsetTypes.end());783    SmallVector<size_t> newRetIndices;784    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(785        rewriter, warpOp, newYieldValues, newYieldTypes, newRetIndices);786    // Create a new prefetch op outside the warp op with updated tensor787    // descriptor type. Source tensor descriptor require type resolution.788    xegpu::TensorDescType newTensorDescTy =789        prefetchOp.getTensorDescType().dropLayouts();790    rewriter.setInsertionPointAfter(newWarpOp);791    SmallVector<Value> newPrefetchOperands = {resolveDistributedTy(792        newWarpOp.getResult(newRetIndices[0]), newTensorDescTy, rewriter)};793    // Collect offsets.794    for (size_t i = 1; i < newRetIndices.size(); ++i)795      newPrefetchOperands.push_back(newWarpOp.getResult(newRetIndices[i]));796    xegpu::PrefetchNdOp::create(rewriter, newWarpOp.getLoc(), TypeRange{},797                                newPrefetchOperands, prefetchOp->getAttrs());798    xegpu::removeLayoutAttrs(prefetchOp);799    rewriter.eraseOp(prefetchOp);800    return success();801  }802};803 804/// Sink a gpu::BarrierOp at the end of enclosing `gpu.warp_execute_on_lane_0`805/// region. This will simply move the barrier op outside of the warp op.806struct GpuBarrierDistribution final : public gpu::WarpDistributionPattern {807  using gpu::WarpDistributionPattern::WarpDistributionPattern;808  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,809                                PatternRewriter &rewriter) const override {810    gpu::YieldOp yield = warpOp.getTerminator();811    Operation *lastNode = yield->getPrevNode();812    // The last node must be a gpu::BarrierOp.813    auto barrierOp = dyn_cast_or_null<gpu::BarrierOp>(lastNode);814    if (!barrierOp)815      return failure();816    // Move the barrier op outside of the warp op.817    rewriter.setInsertionPointAfter(warpOp);818    gpu::BarrierOp::create(rewriter, barrierOp.getLoc(),819                           barrierOp->getResultTypes(),820                           barrierOp->getOperands(), barrierOp->getAttrs());821    rewriter.eraseOp(barrierOp);822    return success();823  }824};825 826/// Distribute a scattered store op. The offsets argument is required.827/// Both offset and mask vectors must be 1D and have #subgroup_size elements.828/// The layouts are fixed and implicit: one offset/mask per lane.829/// The pass changes the offset/mask vector shapes to a830/// single-element vector, **it is assumed that their producer will also be831/// distributed**. The payload vector also has a fixed distribution:832///   no chunk size -> vector of one element.833///   chunk size    -> vector of the innermost dimension of the SG-payload.834/// Example 1 (no chunk size):835///    %mask = producer_op : vector<16xi1>836///    %offset = producer_op : vector<16xindex>837///    xegpu.store %payload, %src[%offset], %mask : vector<16xf16>,838///     memref<256xf16>, vector<16xindex>, vector<16xi1>839/// To840///    %mask = producer_op : vector<1xi1>841///    %offset = producer_op : vector<1xindex>842///    xegpu.store %payload, %src[%offset], %mask : vector<1xf16>,843///     memref<256xf16>, vector<1xindex>, vector<1xi1>844/// Example 2 (chunk size, same mask and offsets):845///    xegpu.store %payload, %src[%offset], %mask <{chunk_size=8}> :846///     vector<16x8xf16>, memref<256xf16>, vector<16xindex>, vector<16xi1>847/// To848///    xegpu.store %payload, %src[%offset], %mask <{chunk_size=8}> :849///     vector<8xf16>, memref<256xf16>, vector<1xindex>, vector<1xi1>850struct StoreDistribution final : public gpu::WarpDistributionPattern {851  using gpu::WarpDistributionPattern::WarpDistributionPattern;852  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,853                                PatternRewriter &rewriter) const override {854    Operation *lastNode = warpOp.getTerminator()->getPrevNode();855    auto storeScatterOp = dyn_cast_or_null<xegpu::StoreScatterOp>(lastNode);856    if (!storeScatterOp)857      return failure();858    auto offsets = storeScatterOp.getOffsets();859    if (!offsets || !isa<VectorType>(offsets.getType()))860      return rewriter.notifyMatchFailure(861          storeScatterOp, "Store op must have a vector of offsets argument");862    VectorType offsetsTy = cast<VectorType>(offsets.getType());863    VectorType maskTy = cast<VectorType>(storeScatterOp.getMask().getType());864    if (offsetsTy.getRank() != 1 || maskTy.getRank() != 1)865      return rewriter.notifyMatchFailure(storeScatterOp,866                                         "Expected 1D offsets and mask vector");867    VectorType storeVecTy = cast<VectorType>(storeScatterOp.getValueType());868    if (storeVecTy.getRank() > 2)869      return rewriter.notifyMatchFailure(870          storeScatterOp, "Expected at most 2D result at SG level");871 872    std::string layoutPayloadName =873        xegpu::getLayoutName(storeScatterOp->getOpOperand(0));874    std::string layoutOffsetsName =875        xegpu::getLayoutName(storeScatterOp->getOpOperand(2));876    std::string layoutMaskName =877        xegpu::getLayoutName(storeScatterOp->getOpOperand(3));878 879    xegpu::LayoutAttr layoutPayload =880        storeScatterOp->getAttrOfType<xegpu::LayoutAttr>(layoutPayloadName);881    xegpu::LayoutAttr layoutOffsets =882        storeScatterOp->getAttrOfType<xegpu::LayoutAttr>(layoutOffsetsName);883    xegpu::LayoutAttr layoutMask =884        storeScatterOp->getAttrOfType<xegpu::LayoutAttr>(layoutMaskName);885 886    FailureOr<VectorType> distStoreVecByWarpOpOrFailure =887        getDistVecTypeBasedOnLaneLayout(layoutPayload, storeVecTy);888    FailureOr<VectorType> distOffsetsByWarpOpOrFailure =889        getDistVecTypeBasedOnLaneLayout(layoutOffsets, offsetsTy);890    FailureOr<VectorType> distMaskByWarpOpOrFailure =891        getDistVecTypeBasedOnLaneLayout(layoutMask, maskTy);892    if (failed(distStoreVecByWarpOpOrFailure) ||893        failed(distOffsetsByWarpOpOrFailure) ||894        failed(distMaskByWarpOpOrFailure)) {895      return rewriter.notifyMatchFailure(896          storeScatterOp,897          "Some vector operands have no layouts, using defaults instead.");898    }899    // Distributed store payload type according to the lane layout.900    VectorType distPayloadTyByWarpOp = distStoreVecByWarpOpOrFailure.value();901    // Expected distributed payload type is always 1D.902    VectorType expectedPayloadTy =903        VectorType::get({distPayloadTyByWarpOp.getNumElements()},904                        distPayloadTyByWarpOp.getElementType());905 906    SmallVector<size_t> newRetIndices;907    SmallVector<Value> operands = storeScatterOp->getOperands();908    SmallVector<Type> operandTypesToYield = {909        distPayloadTyByWarpOp, operands[1].getType(),910        distOffsetsByWarpOpOrFailure.value(),911        distMaskByWarpOpOrFailure.value()};912 913    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(914        rewriter, warpOp, operands, operandTypesToYield, newRetIndices);915    SmallVector<Value> newStoreScatterOpOperands = llvm::map_to_vector(916        newRetIndices, [&](size_t idx) { return newWarpOp.getResult(idx); });917    // The payload operand may need type adjustment due to mismatch between warp918    // distributed type and expected SIMT type.919    rewriter.setInsertionPointAfter(newWarpOp);920    newStoreScatterOpOperands[0] = resolveDistributedTy(921        newStoreScatterOpOperands[0], expectedPayloadTy, rewriter);922    xegpu::StoreScatterOp newOp = xegpu::StoreScatterOp::create(923        rewriter, newWarpOp.getLoc(), TypeRange{}, newStoreScatterOpOperands,924        storeScatterOp->getAttrs());925    xegpu::removeLayoutAttrs(newOp);926    rewriter.eraseOp(storeScatterOp);927    return success();928  }929};930 931static SmallVector<Value> computeDistributedCoordinatesForMatrixOp(932    PatternRewriter &rewriter, Location loc, xegpu::DistributeLayoutAttr layout,933    Value laneId, ArrayRef<int64_t> payloadShape, ValueRange origOffsets) {934  SmallVector<Value> newCoods;935  auto maybeCoords =936      layout.computeDistributedCoords(rewriter, loc, laneId, payloadShape);937  if (failed(maybeCoords))938    return {};939  assert(maybeCoords.value().size() == 1 &&940         "Expected one set of distributed offsets");941  SmallVector<OpFoldResult> ofrVec = xegpu::addWithRightAligned(942      rewriter, loc, getAsOpFoldResult(maybeCoords.value()[0]),943      getAsOpFoldResult(origOffsets));944  newCoods = llvm::map_to_vector(ofrVec, llvm::CastTo<Value>);945  return newCoods;946}947 948/// Pattern for distributing xegpu::LoadMatrixOp.949struct LoadMatrixDistribution final : public gpu::WarpDistributionPattern {950  using gpu::WarpDistributionPattern::WarpDistributionPattern;951  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,952                                PatternRewriter &rewriter) const override {953    gpu::YieldOp yield = warpOp.getTerminator();954    Operation *lastNode = yield->getPrevNode();955    auto matrixOp = dyn_cast_or_null<xegpu::LoadMatrixOp>(lastNode);956    if (!matrixOp)957      return failure();958 959    OpOperand *producedByLastLoad = getWarpResult(warpOp, [&](Operation *op) {960      return isa<xegpu::LoadMatrixOp>(op) && matrixOp == op;961    });962    if (!producedByLastLoad)963      return rewriter.notifyMatchFailure(964          warpOp, "The last op is not xegpu::LoadMatrixOp");965    const int operandIdx = producedByLastLoad->getOperandNumber();966 967    VectorType sgPayloadTy =968        dyn_cast<VectorType>(matrixOp.getResult().getType());969    VectorType warpResultTy =970        cast<VectorType>(warpOp.getResult(operandIdx).getType());971    if (!sgPayloadTy)972      return rewriter.notifyMatchFailure(973          matrixOp, "the matrix op payload must be a vector type");974 975    auto loc = matrixOp.getLoc();976    auto offsets = matrixOp.getMixedOffsets();977    if (offsets.empty())978      return rewriter.notifyMatchFailure(matrixOp,979                                         "the load op must have offsets");980    SmallVector<Value> offsetsAsValues =981        vector::getAsValues(rewriter, matrixOp.getLoc(), offsets);982 983    auto layout = matrixOp.getLayoutAttr();984    if (!layout)985      return rewriter.notifyMatchFailure(986          matrixOp, "the matrix operation lacks layout attribute");987 988    FailureOr<VectorType> distPayloadByWarpOpOrFailure =989        getDistVecTypeBasedOnLaneLayout(layout, sgPayloadTy);990    if (failed(distPayloadByWarpOpOrFailure))991      return rewriter.notifyMatchFailure(992          matrixOp, "Failed to distribute matrix op payload based on layout.");993 994    SmallVector<Value> operands = {matrixOp.getMemDesc()};995    const unsigned offsetsStartIdx = operands.size();996    operands.append(offsetsAsValues);997 998    SmallVector<Type> operandTypes = llvm::to_vector(999        llvm::map_range(operands, [](Value v) { return v.getType(); }));1000 1001    SmallVector<size_t> newRetIndices;1002    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1003        rewriter, warpOp, operands, operandTypes, newRetIndices);1004    SmallVector<Value> newOperands = llvm::map_to_vector(1005        newRetIndices, [&](size_t idx) { return newWarpOp.getResult(idx); });1006 1007    SmallVector<int64_t> newConstOffsets(matrixOp.getConstOffsets().size(),1008                                         ShapedType::kDynamic);1009    DenseI64ArrayAttr newConstOffsetsAttr =1010        rewriter.getDenseI64ArrayAttr(newConstOffsets);1011    ValueRange currentOffsets =1012        ValueRange(newOperands).drop_front(offsetsStartIdx);1013 1014    SmallVector<Value> newCoords = currentOffsets;1015    rewriter.setInsertionPointAfter(newWarpOp);1016 1017    if (!matrixOp.getSubgroupBlockIoAttr()) {1018      newCoords = computeDistributedCoordinatesForMatrixOp(1019          rewriter, loc, layout, newWarpOp.getLaneid(), sgPayloadTy.getShape(),1020          currentOffsets);1021    }1022    xegpu::LoadMatrixOp newOp = xegpu::LoadMatrixOp::create(1023        rewriter, newWarpOp.getLoc(), *distPayloadByWarpOpOrFailure,1024        newOperands[0], ValueRange(newCoords), newConstOffsetsAttr,1025        matrixOp.getSubgroupBlockIoAttr(), xegpu::DistributeLayoutAttr{});1026    // Resolve the output type and replace all uses.1027    rewriter.replaceAllUsesWith(1028        newWarpOp.getResult(operandIdx),1029        resolveDistributedTy(newOp.getResult(), warpResultTy, rewriter));1030    return success();1031  }1032};1033 1034/// Pattern for distributing xegpu::StoreMatrixOp.1035struct StoreMatrixDistribution final : public gpu::WarpDistributionPattern {1036  using gpu::WarpDistributionPattern::WarpDistributionPattern;1037  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1038                                PatternRewriter &rewriter) const override {1039    gpu::YieldOp yield = warpOp.getTerminator();1040    Operation *lastNode = yield->getPrevNode();1041    auto matrixOp = dyn_cast_or_null<xegpu::StoreMatrixOp>(lastNode);1042    if (!matrixOp)1043      return failure();1044 1045    VectorType sgPayloadTy = dyn_cast<VectorType>(matrixOp.getData().getType());1046    if (!sgPayloadTy)1047      return rewriter.notifyMatchFailure(1048          matrixOp, "the matrix op payload must be a vector type");1049 1050    auto loc = matrixOp.getLoc();1051    auto offsets = matrixOp.getMixedOffsets();1052    if (offsets.empty())1053      return rewriter.notifyMatchFailure(matrixOp,1054                                         "the store op must have offsets");1055    SmallVector<Value> offsetsAsValues =1056        vector::getAsValues(rewriter, matrixOp.getLoc(), offsets);1057 1058    auto layout = matrixOp.getLayoutAttr();1059    if (!layout)1060      return rewriter.notifyMatchFailure(1061          matrixOp, "the matrix operation lacks layout attribute");1062 1063    FailureOr<VectorType> distPayloadByWarpOpOrFailure =1064        getDistVecTypeBasedOnLaneLayout(layout, sgPayloadTy);1065    if (failed(distPayloadByWarpOpOrFailure))1066      return rewriter.notifyMatchFailure(1067          matrixOp, "Failed to distribute matrix op payload based on layout.");1068 1069    SmallVector<Value> operands = {matrixOp.getData(), matrixOp.getMemDesc()};1070    const unsigned offsetsStartIdx = operands.size();1071    operands.append(offsetsAsValues);1072 1073    SmallVector<Type> operandTypes = llvm::to_vector(1074        llvm::map_range(operands, [](Value v) { return v.getType(); }));1075    operandTypes[0] = *distPayloadByWarpOpOrFailure;1076 1077    SmallVector<size_t> newRetIndices;1078    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1079        rewriter, warpOp, operands, operandTypes, newRetIndices);1080    SmallVector<Value> newOperands = llvm::map_to_vector(1081        newRetIndices, [&](size_t idx) { return newWarpOp.getResult(idx); });1082 1083    SmallVector<int64_t> newConstOffsets(matrixOp.getConstOffsets().size(),1084                                         ShapedType::kDynamic);1085    DenseI64ArrayAttr newConstOffsetsAttr =1086        rewriter.getDenseI64ArrayAttr(newConstOffsets);1087    ValueRange currentOffsets =1088        ValueRange(newOperands).drop_front(offsetsStartIdx);1089 1090    SmallVector<Value> newCoords = currentOffsets;1091    rewriter.setInsertionPointAfter(newWarpOp);1092 1093    if (!matrixOp.getSubgroupBlockIoAttr()) {1094      newCoords = computeDistributedCoordinatesForMatrixOp(1095          rewriter, loc, layout, newWarpOp.getLaneid(), sgPayloadTy.getShape(),1096          currentOffsets);1097    }1098 1099    xegpu::StoreMatrixOp::create(1100        rewriter, loc, TypeRange{}, newOperands[0], newOperands[1],1101        ValueRange(newCoords), newConstOffsetsAttr,1102        matrixOp.getSubgroupBlockIoAttr(), xegpu::DistributeLayoutAttr{});1103    rewriter.eraseOp(matrixOp);1104    return success();1105  }1106};1107 1108/// Distribute a scattered load op. The logic and requirements are the same as1109/// for the scattered store distribution. The warpOp's payload vector is1110/// expected to be distributed by the load's result consumer.1111/// Example 1 (no chunk size):1112///    %mask = producer_op : vector<16xi1>1113///    %offset = producer_op : vector<16xindex>1114///    %0 = xegpu.load %payload, %src[%offset], %mask : memref<256xf16>,1115///    vector<16xindex>, vector<16xi1> -> vector<16xf16>1116/// To1117///    %mask = producer_op : vector<1xi1>1118///    %offset = producer_op : vector<1xindex>1119///    %0 = xegpu.load %payload, %src[%offset], %mask : memref<256xf16>,1120///     vector<1xindex>, vector<1xi1> -> vector<1xf16>1121/// Example 2 (chunk size, same mask and offsets):1122///    %0 = xegpu.load %payload, %src[%offset], %mask <{chunk_size=8}> :1123///     memref<256xf16>, vector<16xindex>, vector<16xi1> -> vector<16x8xf16>1124/// To1125///    %0 = xegpu.load %payload, %src[%offset], %mask <{chunk_size=8}> :1126///     memref<256xf16>, vector<1xindex>, vector<1xi1> -> vector<8xf16>1127struct LoadDistribution final : public gpu::WarpDistributionPattern {1128  using gpu::WarpDistributionPattern::WarpDistributionPattern;1129  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1130                                PatternRewriter &rewriter) const override {1131    OpOperand *producedByLastLoad = getWarpResult(warpOp, [&](Operation *op) {1132      // Check if the yield operand that was produced by the *last* scattered1133      // load op to avoid sinking it before barriers (maintain memory order).1134      return isa<xegpu::LoadGatherOp>(op) &&1135             warpOp.getTerminator()->getPrevNode() == op;1136    });1137    if (!producedByLastLoad)1138      return rewriter.notifyMatchFailure(1139          warpOp, "The last op is not xegpu::LoadGatherOp");1140 1141    auto loadGatherOp =1142        producedByLastLoad->get().getDefiningOp<xegpu::LoadGatherOp>();1143    auto offsets = loadGatherOp.getOffsets();1144    if (!offsets || !isa<VectorType>(offsets.getType()) ||1145        !isa<VectorType>(loadGatherOp.getMask().getType()))1146      return rewriter.notifyMatchFailure(1147          loadGatherOp,1148          "Load op must have a vector arguments for offsets and mask");1149    VectorType offsetsTy = cast<VectorType>(offsets.getType());1150    VectorType maskTy = cast<VectorType>(loadGatherOp.getMask().getType());1151    if (offsetsTy.getRank() != 1 || maskTy.getRank() != 1)1152      return rewriter.notifyMatchFailure(loadGatherOp,1153                                         "Expected 1D offsets and mask vector");1154    // Assume offset and mask producers will be distributed as well.1155    std::string layoutOffsetsName =1156        xegpu::getLayoutName(loadGatherOp->getOpOperand(1));1157    std::string layoutMaskName =1158        xegpu::getLayoutName(loadGatherOp->getOpOperand(2));1159 1160    xegpu::LayoutAttr layoutOffsets =1161        loadGatherOp->getAttrOfType<xegpu::LayoutAttr>(layoutOffsetsName);1162    xegpu::LayoutAttr layoutMask =1163        loadGatherOp->getAttrOfType<xegpu::LayoutAttr>(layoutMaskName);1164 1165    FailureOr<VectorType> distOffsetsByWarpOpOrFailure =1166        getDistVecTypeBasedOnLaneLayout(layoutOffsets, offsetsTy);1167    FailureOr<VectorType> distMaskByWarpOpOrFailure =1168        getDistVecTypeBasedOnLaneLayout(layoutMask, maskTy);1169    if (failed(distOffsetsByWarpOpOrFailure) ||1170        failed(distMaskByWarpOpOrFailure)) {1171      return rewriter.notifyMatchFailure(1172          loadGatherOp,1173          "Some vector operands have no layouts, using defaults instead.");1174    }1175 1176    SmallVector<size_t> newRetIndices;1177    SmallVector<Value> operands = loadGatherOp->getOperands();1178    SmallVector<Type> operandTypesToYield = {1179        operands[0].getType(), distOffsetsByWarpOpOrFailure.value(),1180        distMaskByWarpOpOrFailure.value()};1181 1182    const unsigned operandIdx = producedByLastLoad->getOperandNumber();1183    VectorType distResultTy =1184        cast<VectorType>(warpOp.getResult(operandIdx).getType());1185    // Distributed load op will always be 1D.1186    VectorType loadVecTy = VectorType::get({distResultTy.getNumElements()},1187                                           distResultTy.getElementType());1188 1189    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1190        rewriter, warpOp, operands, operandTypesToYield, newRetIndices);1191 1192    SmallVector<Value> newLoadGatherOperands = llvm::map_to_vector(1193        newRetIndices, [&](size_t idx) { return newWarpOp.getResult(idx); });1194 1195    rewriter.setInsertionPointAfter(newWarpOp);1196    xegpu::LoadGatherOp newOp = xegpu::LoadGatherOp::create(1197        rewriter, newWarpOp.getLoc(), loadVecTy, newLoadGatherOperands,1198        loadGatherOp->getAttrs());1199    xegpu::removeLayoutAttrs(newOp);1200    Value distributedVal = newWarpOp.getResult(operandIdx);1201    // Resolve the output type and replace all uses.1202    rewriter.replaceAllUsesWith(1203        distributedVal,1204        resolveDistributedTy(newOp.getResult(), distResultTy, rewriter));1205    return success();1206  }1207};1208 1209/// Helper to rewrite a 2D VectorMultiReductionOp into a sequence of 1D1210/// VectorReductionOps. We also insert layouts for the newly created ops.1211static Value lowerToVectorReductions(TypedValue<VectorType> src,1212                                     TypedValue<VectorType> acc,1213                                     vector::CombiningKind kind,1214                                     int64_t reductionDim, Location loc,1215                                     PatternRewriter &rewriter) {1216  // Expecting a 2D source vector.1217  assert(src.getType().getRank() == 2 && "expected a 2D source vector");1218  VectorType sourceType = src.getType();1219  int64_t sourceH = sourceType.getShape()[0];1220  int64_t sourceW = sourceType.getShape()[1];1221  int nSlices = (reductionDim == 0) ? sourceW : sourceH;1222  // Create a constant vector to hold the result of the reduction.1223  TypedAttr zeroAttr = rewriter.getZeroAttr(sourceType.getElementType());1224  Value reductionResult = arith::ConstantOp::create(1225      rewriter, loc, acc.getType(),1226      DenseElementsAttr::get(acc.getType(), zeroAttr));1227  // Reduction result should have the same layout as the accumulator.1228  xegpu::setDistributeLayoutAttr(cast<OpResult>(reductionResult),1229                                 xegpu::getDistributeLayoutAttr(acc));1230  // For each slice of the source, extract the slice vector, do a reduction1231  // and, insert the reduced value back to the result vector.1232  for (int i = 0; i < nSlices; ++i) {1233    SmallVector<int64_t, 2> sliceOffsets, sliceSizes;1234    if (reductionDim == 1) {1235      sliceOffsets = {i, 0};1236      sliceSizes = {1, sourceW};1237    } else {1238      sliceOffsets = {0, i};1239      sliceSizes = {sourceH, 1};1240    }1241    vector::ExtractStridedSliceOp extractOp =1242        vector::ExtractStridedSliceOp::create(rewriter, loc, src, sliceOffsets,1243                                              sliceSizes, {1, 1});1244    int64_t nSliceElements = extractOp.getResult().getType().getNumElements();1245    vector::ShapeCastOp slice = vector::ShapeCastOp::create(1246        rewriter, loc,1247        VectorType::get({nSliceElements}, sourceType.getElementType()),1248        extractOp.getResult());1249    // Shape cast is currently handled in xegpu side. So layouts must be1250    // retained during lowering. Shape cast output has the same layout as the1251    // accumulator. Shape cast source has the same layout as the original1252    // reduction source.1253    // TODO: other ops generated here may also need layout attributes.1254    xegpu::setDistributeLayoutAttr(slice->getOpOperand(0),1255                                   xegpu::getDistributeLayoutAttr(src));1256    xegpu::setDistributeLayoutAttr(slice->getOpResult(0),1257                                   xegpu::getDistributeLayoutAttr(acc));1258    // Extract and reduction results in scalars, so no result layout is needed.1259    Value accExtract = vector::ExtractOp::create(rewriter, loc, acc, i);1260    Value reduction = vector::ReductionOp::create(1261        rewriter, loc, kind, slice.getResult(), accExtract);1262    reductionResult =1263        vector::InsertOp::create(rewriter, loc, reduction, reductionResult, i);1264  }1265  return reductionResult;1266}1267 1268/// This patterns distribute the `vector.multi_reduction` operation across1269/// lanes in a warp. Currently only 2D to 1D reductions are supported. Given1270/// layouts for the source and accumulator vectors,1271/// * If the reduction dimension is distributed across lanes, the reduction is1272///   non-lane-local and the reduction is done using warp shuffles. Here we1273///   simply rewrite the MultiDimReductionOp to a sequence of ReductionOps in1274///   the warp op body.1275/// * If the reduction dimension is not distributed across lanes, the reduction1276///   is lane-local. In this case, we yield the source and accumulator vectors1277///   from the warp op and perform the lane-local reduction outside the warp op1278///   using a sequence of ReductionOps.1279/// Example 1 (Reduction is lane-local):1280/// ```1281/// %r = gpu.warp_execute_on_lane_0(%laneid)[32] -> (vector<1xf32>) {1282///   %0 = "some_def"() : () -> (vector<16x32xf32>)1283///   %acc = "some_def"() : () -> (vector<32xf32>)1284///   %1 = vector.multi_reduction <add>, %0, %acc [0] : vector<16x32xf32> to1285///   vector<32xf32> gpu.yield %1 : vector<32xf32>1286/// }1287/// ```1288/// is lowered to:1289/// ```1290/// %r:2 = gpu.warp_execute_on_lane_0(%laneid)[32] -> (vector<16x1xf32>,1291/// vector<1xf32>) {1292///   %0 = "some_def"() : () -> (vector<16x32xf32>)1293///   %acc = "some_def"() : () -> (vector<32xf32>)1294///   gpu.yield %0, %acc : vector<16x32xf32>, vector<32xf32>1295/// }1296/// %c = arith.constant dense<0.0> : vector<1xf32>1297/// %1 = vector.shape_cast %r#0 : vector<16x1xf32> to vector<16xf32>1298/// %2 = vector.reduction <add>, %1, %r#1 : vector<16xf32> to f321299/// %3 = vector.insert %2, %c[0] : f32 into vector<1xf32>1300/// ```1301/// Example 2 (Reduction is non-lane-local):1302/// ```1303/// %r = gpu.warp_execute_on_lane_0(%laneid)[32] -> (vector<2xf32>) {1304///   %0 = "some_def"() : () -> (vector<2x32xf32>)1305///   %acc = "some_def"() : () -> (vector<2xf32>)1306///   %1 = vector.multi_reduction <add>, %0, %acc [1] : vector<2x32xf32> to1307///   vector<2xf32>1308///   gpu.yield %1 : vector<2xf32>1309/// }1310/// ```1311/// is lowered to:1312/// ```1313/// %r = gpu.warp_execute_on_lane_0(%laneid)[32] -> (vector<2xf32>) {1314///   %0 = "some_def"() : () -> (vector<2x32xf32>)1315///   %acc = "some_def"() : () -> (vector<2xf32>)1316///   %1 = arith.constant dense<0.0> : vector<2xf32>1317///   %2 = vector.extract %0[0] : vector<32xf32> from <vector<2x32xf32>>1318///   %3 = ("warp.reduction %2") : f321319///   %4 = vector.insert %3, %1[0] : f32 into vector<2xf32>1320///   ... repeat for row 11321///   gpu.yield %1 : vector<2xf32>1322/// }1323struct VectorMultiReductionDistribution : public gpu::WarpDistributionPattern {1324  using gpu::WarpDistributionPattern::WarpDistributionPattern;1325  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1326                                PatternRewriter &rewriter) const override {1327    OpOperand *yieldOperand =1328        getWarpResult(warpOp, llvm::IsaPred<vector::MultiDimReductionOp>);1329    if (!yieldOperand)1330      return failure();1331    auto reductionOp =1332        cast<vector::MultiDimReductionOp>(yieldOperand->get().getDefiningOp());1333    unsigned operandIdx = yieldOperand->getOperandNumber();1334    VectorType sourceType = reductionOp.getSourceVectorType();1335    // Only 2D vectors are supported.1336    if (sourceType.getRank() != 2)1337      return rewriter.notifyMatchFailure(warpOp,1338                                         "Only 2D reductions are supported.");1339    ArrayRef<int64_t> reductionDims = reductionOp.getReductionDims();1340    // Only 1 reduction dimension supported. This also ensures that the result1341    // is vector type.1342    if (reductionDims.size() != 1)1343      return rewriter.notifyMatchFailure(1344          warpOp, "Only 1 reduction dimension is supported.");1345    int64_t reductionDim = reductionDims[0];1346    VectorType distributedResultType =1347        cast<VectorType>(warpOp.getResult(operandIdx).getType());1348    VectorType resultType = cast<VectorType>(reductionOp.getType());1349    xegpu::DistributeLayoutAttr sourceLayout =1350        xegpu::getDistributeLayoutAttr(reductionOp.getSource());1351 1352    FailureOr<VectorType> sourceDistTypeOrFailure =1353        getDistVecTypeBasedOnLaneLayout(sourceLayout, sourceType);1354    if (failed(sourceDistTypeOrFailure))1355      return rewriter.notifyMatchFailure(1356          warpOp, "Failed to distribute the source vector type.");1357    VectorType sourceDistType = sourceDistTypeOrFailure.value();1358    // Only single dimension distribution is supported.1359    bool dim0Distributed =1360        sourceDistType.getShape()[0] != sourceType.getShape()[0];1361    bool dim1Distributed =1362        sourceDistType.getShape()[1] != sourceType.getShape()[1];1363    if (dim0Distributed && dim1Distributed)1364      return rewriter.notifyMatchFailure(1365          warpOp, "Expecting source to be distributed in a single dimension.");1366    int64_t sourceDistDim = dim0Distributed ? 0 : (dim1Distributed ? 1 : -1);1367    if (sourceDistDim == -1)1368      return rewriter.notifyMatchFailure(1369          warpOp, "Expecting a distributed source vector.");1370    bool resultDistributed =1371        distributedResultType.getNumElements() < resultType.getNumElements();1372    // If the lane owns all the data required for reduction (i.e. reduction is1373    // fully parallel accross lanes), then each lane owns part of the result1374    // (i.e. result is distributed). If the reduction require cross-lane1375    // shuffling, then the result is shared among all lanes (broadcasted).1376    // Therefore we expect following cases:1377    //1378    // | Source vector        | Reduction dim  | Result vector  |1379    // |----------------------|----------------|----------------|1380    // |  dim-0 distributed   |       0        | broadcasted    |1381    // |  dim-0 distributed   |       1        | distributed    |1382    // |  dim-1 distributed   |       0        | distributed    |1383    // |  dim-1 distributed   |       1        | broadcasted    |1384 1385    bool isReductionLaneLocal = (sourceDistDim == 0 && reductionDim == 1) ||1386                                (sourceDistDim == 1 && reductionDim == 0);1387    if (isReductionLaneLocal && !resultDistributed)1388      return rewriter.notifyMatchFailure(1389          warpOp, "Expecting a distributed result for lane-local reduction.");1390 1391    if (!isReductionLaneLocal && resultDistributed)1392      return rewriter.notifyMatchFailure(1393          warpOp,1394          "Expecting a broadcasted result for non-lane-local reduction.");1395 1396    // Handle lane-local reduction case. In this case we fully distribute the1397    // reduction result.1398    if (isReductionLaneLocal) {1399      // Yield the source and acc vectors from the WarpOp.1400      SmallVector<size_t> newRetIndices;1401      auto newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1402          rewriter, warpOp, {reductionOp.getSource(), reductionOp.getAcc()},1403          {sourceDistType, distributedResultType}, newRetIndices);1404      rewriter.setInsertionPointAfter(newWarpOp);1405      Value result = lowerToVectorReductions(1406          cast<TypedValue<VectorType>>(newWarpOp->getResult(newRetIndices[0])),1407          cast<TypedValue<VectorType>>(newWarpOp->getResult(newRetIndices[1])),1408          reductionOp.getKind(), reductionDim, reductionOp.getLoc(), rewriter);1409      // Replace the warp op result with the final result.1410      rewriter.replaceAllUsesWith(newWarpOp.getResult(operandIdx), result);1411      return success();1412    }1413    // For non-lane-local case, we simply rewrite the MultiReductionOp in terms1414    // of multiple ReductionOps. Actual distribution is done by the1415    // WarpOpReduction pattern.1416    rewriter.setInsertionPointAfter(reductionOp);1417    Value result = lowerToVectorReductions(1418        cast<TypedValue<VectorType>>(reductionOp.getSource()),1419        cast<TypedValue<VectorType>>(reductionOp.getAcc()),1420        reductionOp.getKind(), reductionDim, reductionOp.getLoc(), rewriter);1421    // Replace the warp op result with the final result.1422    rewriter.replaceAllUsesWith(reductionOp.getResult(), result);1423    return success();1424  }1425};1426 1427/// Distribute a `vector.shape_cast` op feeding into yield op of an enclosing1428/// `gpu.warp_execute_on_lane_0` region.1429struct VectorShapeCastDistribution : public gpu::WarpDistributionPattern {1430  using gpu::WarpDistributionPattern::WarpDistributionPattern;1431  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1432                                PatternRewriter &rewriter) const override {1433    OpOperand *yieldOperand =1434        getWarpResult(warpOp, llvm::IsaPred<vector::ShapeCastOp>);1435    if (!yieldOperand)1436      return failure();1437    auto shapeCastOp =1438        cast<vector::ShapeCastOp>(yieldOperand->get().getDefiningOp());1439    unsigned operandNumber = yieldOperand->getOperandNumber();1440    auto resultDistTy =1441        cast<VectorType>(warpOp.getResult(operandNumber).getType());1442    xegpu::DistributeLayoutAttr sourceLayout =1443        xegpu::getDistributeLayoutAttr(shapeCastOp->getOpOperand(0));1444    xegpu::DistributeLayoutAttr resultLayout =1445        xegpu::getDistributeLayoutAttr(shapeCastOp.getResult());1446    if (!sourceLayout || !resultLayout)1447      return rewriter.notifyMatchFailure(1448          warpOp,1449          "the source or result of shape_cast op lacks distribution layout");1450 1451    // For rank reducing or increasing shape_cast ops, the lower rank layout1452    // must be a slice of higher rank layout.1453    int64_t sourceRank = shapeCastOp.getSourceVectorType().getRank();1454    int64_t resultRank = shapeCastOp.getResultVectorType().getRank();1455    if (sourceRank < resultRank && !sourceLayout.isSliceOf(resultLayout))1456      return rewriter.notifyMatchFailure(1457          warpOp, "shape_cast is rank reducing but source layout is not a "1458                  "slice of result layout");1459    if (sourceRank > resultRank && !resultLayout.isSliceOf(sourceLayout))1460      return rewriter.notifyMatchFailure(1461          warpOp, "shape_cast is rank increasing but result layout is not a "1462                  "slice of source layout");1463 1464    FailureOr<VectorType> sourceDistTypeOrFailure =1465        getDistVecTypeBasedOnLaneLayout(sourceLayout,1466                                        shapeCastOp.getSourceVectorType());1467    if (failed(sourceDistTypeOrFailure))1468      return rewriter.notifyMatchFailure(1469          warpOp, "failed to get distributed vector type for source");1470    VectorType sourceDistType = sourceDistTypeOrFailure.value();1471    // Create a new warp op that yields the source of the shape_cast op.1472    SmallVector<size_t> newRetIndices;1473    auto newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1474        rewriter, warpOp, {shapeCastOp.getSource()}, {sourceDistType},1475        newRetIndices);1476    rewriter.setInsertionPointAfter(newWarpOp);1477    Value source = newWarpOp.getResult(newRetIndices[0]);1478    // Create a new shape_cast op outside the warp op.1479    Value newShapeCast = vector::ShapeCastOp::create(1480        rewriter, shapeCastOp.getLoc(), resultDistTy, source);1481    rewriter.replaceAllUsesWith(newWarpOp.getResult(operandNumber),1482                                newShapeCast);1483    return success();1484  }1485};1486 1487// Distribute a `vector.extract_strided_slice` op feeding into yield op of an1488// enclosing `gpu.warp_execute_on_lane_0` region. This pattern covers1489// advanced cases where the distributed dimension is partially extracted and1490// currently not supported by the generic vector distribution patterns.1491struct VectorExtractStridedSliceDistribution1492    : public gpu::WarpDistributionPattern {1493  using gpu::WarpDistributionPattern::WarpDistributionPattern;1494  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1495                                PatternRewriter &rewriter) const override {1496    OpOperand *operand =1497        getWarpResult(warpOp, llvm::IsaPred<vector::ExtractStridedSliceOp>);1498    if (!operand)1499      return failure();1500    auto extractOp =1501        cast<vector::ExtractStridedSliceOp>(operand->get().getDefiningOp());1502    unsigned operandIdx = operand->getOperandNumber();1503    auto distributedType =1504        cast<VectorType>(warpOp.getResult(operandIdx).getType());1505    // Find the distributed dimensions.1506    auto extractResultType = cast<VectorType>(operand->get().getType());1507    auto distributedDims =1508        getDistributedDims(extractResultType, distributedType);1509    // Collect updated source type, sizes and offsets. They may be adjusted1510    // later if the data is distributed to lanes (as opposed to being owned by1511    // all lanes uniformly).1512    VectorType updatedSourceType = extractOp.getSourceVectorType();1513    SmallVector<Attribute> updatedSizes = llvm::map_to_vector(1514        extractOp.getSizes(), [](Attribute attr) { return attr; });1515    SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(1516        extractOp.getOffsets(), [](Attribute attr) { return attr; });1517    // If the result is distributed, it must be distributed in exactly one1518    // dimension. In this case, we adjust the sourceDistType, distributedSizes1519    // and distributedOffsets accordingly.1520    if (distributedDims.size() > 0) {1521      if (distributedDims.size() != 1)1522        return rewriter.notifyMatchFailure(1523            warpOp, "Source can not be distributed in multiple dimensions.");1524      int64_t distributedDim = distributedDims[0];1525      int sourceDistrDimSize =1526          extractOp.getSourceVectorType().getShape()[distributedDim];1527      auto sourceLayout =1528          xegpu::getDistributeLayoutAttr(extractOp->getOpOperand(0));1529      if (!sourceLayout || sourceLayout.getEffectiveLaneLayoutAsInt().empty())1530        return rewriter.notifyMatchFailure(1531            warpOp, "the source of extract_strided_slice op lacks distribution "1532                    "layout");1533      auto sourceLaneLayout = sourceLayout.getEffectiveLaneLayoutAsInt();1534      // Because only single dimension distribution is supported, lane layout1535      // size at the distributed dim must be the subgroup size.1536      int subgroupSize = sourceLaneLayout[distributedDim];1537      // Check if the source size in the distributed dimension is a multiple of1538      // subgroup size.1539      if (sourceDistrDimSize % subgroupSize != 0)1540        return rewriter.notifyMatchFailure(1541            warpOp,1542            "Source size along distributed dimension is not a multiple of "1543            "subgroup size.");1544      auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();1545      // We expect lane data to be all ones in this case.1546      if (!llvm::all_of(sourceLaneData, [](int64_t v) { return v == 1; }))1547        return rewriter.notifyMatchFailure(1548            warpOp, "Expecting unit lane data in source layout");1549      // The offsets in the distributed dimention must be a multiple of subgroup1550      // size.1551      int64_t distrDimOffset =1552          cast<IntegerAttr>(extractOp.getOffsets()[distributedDim]).getInt();1553      if (distrDimOffset % subgroupSize != 0)1554        return rewriter.notifyMatchFailure(1555            warpOp, "Offset along distributed dimension "1556                    "is not a multiple of subgroup size.");1557      updatedSourceType = getDistVecTypeBasedOnLaneLayout(1558                              sourceLayout, extractOp.getSourceVectorType())1559                              .value();1560      // Update the distributed sizes to match the distributed type.1561      updatedSizes[distributedDim] = rewriter.getI64IntegerAttr(1562          distributedType.getDimSize(distributedDim));1563      // Update the distributed offsets to match round robin distribution (i.e.1564      // each lane owns data at `subgroupSize` stride given unit lane data).1565      updatedOffsets[distributedDim] =1566          rewriter.getI64IntegerAttr(distrDimOffset / subgroupSize);1567    }1568    // Do the distribution by yielding the source of the extract op from1569    // the warp op and creating a new extract op outside the warp op.1570    SmallVector<size_t> newRetIndices;1571    auto newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1572        rewriter, warpOp, {extractOp.getSource()}, {updatedSourceType},1573        newRetIndices);1574    rewriter.setInsertionPointAfter(newWarpOp);1575    Value source = newWarpOp.getResult(newRetIndices[0]);1576    // Create a new extract op outside the warp op.1577    Value newExtractOp = vector::ExtractStridedSliceOp::create(1578        rewriter, extractOp.getLoc(), distributedType, source,1579        ArrayAttr::get(rewriter.getContext(), updatedOffsets),1580        ArrayAttr::get(rewriter.getContext(), updatedSizes),1581        extractOp.getStrides());1582    rewriter.replaceAllUsesWith(newWarpOp.getResult(operandIdx), newExtractOp);1583    return success();1584  }1585};1586 1587/// Distribute a `vector.insert_strided_slice` op feeding into yield op of an1588/// enclosing `gpu.warp_execute_on_lane_0` region. This pattern covers1589/// advanced cases where the distributed dimension is partially inserted and1590/// currently not supported by the generic vector distribution patterns.1591struct VectorInsertStridedSliceDistribution1592    : public gpu::WarpDistributionPattern {1593  using gpu::WarpDistributionPattern::WarpDistributionPattern;1594  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1595                                PatternRewriter &rewriter) const override {1596    OpOperand *operand =1597        getWarpResult(warpOp, llvm::IsaPred<vector::InsertStridedSliceOp>);1598    if (!operand)1599      return failure();1600    unsigned int operandNumber = operand->getOperandNumber();1601    auto insertOp =1602        operand->get().getDefiningOp<vector::InsertStridedSliceOp>();1603    auto distributedType =1604        cast<VectorType>(warpOp.getResult(operandNumber).getType());1605    // Find the distributed dimensions of the dest vector.1606    auto insertResultType = cast<VectorType>(operand->get().getType());1607    auto destDistributedDims =1608        getDistributedDims(insertResultType, distributedType);1609    // Collect updated offsets, source type and dest type. They may be adjusted1610    // later if the data is distributed to lanes (as opposed to being owned by1611    // all lanes uniformly).1612    SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(1613        insertOp.getOffsets(), [](Attribute attr) { return attr; });1614    VectorType updatedSourceType = insertOp.getSourceVectorType();1615    VectorType updatedDestType = insertOp.getDestVectorType();1616    if (destDistributedDims.size() > 0) {1617      // Only single dimension distribution is supported.1618      if (destDistributedDims.size() != 1)1619        return rewriter.notifyMatchFailure(1620            warpOp,1621            "Expecting source to be distributed in a single dimension.");1622      int64_t destDistributedDim = destDistributedDims[0];1623 1624      VectorType srcType = insertOp.getSourceVectorType();1625      VectorType destType = insertOp.getDestVectorType();1626      // Currently we require that both source (kD) and dest (nD) vectors are1627      // distributed. This requires that distributedDim (d) is contained in the1628      // last k dims of the dest vector (d >= n - k).1629      int64_t sourceDistributedDim =1630          destDistributedDim - (destType.getRank() - srcType.getRank());1631      if (sourceDistributedDim < 0)1632        return rewriter.notifyMatchFailure(1633            insertOp,1634            "distributed dimension must be in the last k (i.e. source "1635            "rank) dims of dest vector");1636      int64_t srcDistrDimSize = srcType.getDimSize(sourceDistributedDim);1637      // Obtain the source and dest layouts.1638      auto destLayout =1639          xegpu::getDistributeLayoutAttr(insertOp->getOpOperand(1));1640      auto sourceLayout =1641          xegpu::getDistributeLayoutAttr(insertOp->getOpOperand(0));1642      if (!destLayout || !sourceLayout ||1643          destLayout.getEffectiveLaneLayoutAsInt().empty() ||1644          sourceLayout.getEffectiveLaneLayoutAsInt().empty())1645        return rewriter.notifyMatchFailure(1646            warpOp, "the source or dest of insert_strided_slice op lacks "1647                    "distribution layout");1648      // Because only single dimension distribution is supported, lane layout1649      // size at the distributed dim must be the subgroup size.1650      int subgroupSize =1651          destLayout.getEffectiveLaneLayoutAsInt()[destDistributedDim];1652      // We require that source and dest lane data are all ones to ensure1653      // uniform round robin distribution.1654      auto destLaneData = destLayout.getEffectiveLaneDataAsInt();1655      auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();1656      if (!llvm::all_of(destLaneData, [](int64_t v) { return v == 1; }) ||1657          !llvm::all_of(sourceLaneData, [](int64_t v) { return v == 1; }))1658        return rewriter.notifyMatchFailure(1659            warpOp, "Expecting unit lane data in source and dest layouts");1660      // Source distributed dim size must be multiples of subgroup size.1661      if (srcDistrDimSize % subgroupSize != 0)1662        return rewriter.notifyMatchFailure(1663            warpOp, "Distributed dimension size in source is not a multiple of "1664                    "subgroup size.");1665      // Offsets in the distributed dimension must be multiples of subgroup1666      // size.1667      int64_t destDistrDimOffset =1668          cast<IntegerAttr>(insertOp.getOffsets()[destDistributedDim]).getInt();1669      if (destDistrDimOffset % subgroupSize != 0)1670        return rewriter.notifyMatchFailure(1671            warpOp,1672            "Offset along distributed dimension in dest is not a multiple of "1673            "subgroup size.");1674      // Update the source and dest types based on their layouts.1675      updatedSourceType = getDistVecTypeBasedOnLaneLayout(1676                              sourceLayout, insertOp.getSourceVectorType())1677                              .value();1678      updatedDestType = getDistVecTypeBasedOnLaneLayout(1679                            destLayout, insertOp.getDestVectorType())1680                            .value();1681      // Update the distributed offsets to match round robin distribution (i.e.1682      // each lane owns data at `subgroupSize` stride given unit lane data).1683      updatedOffsets[destDistributedDim] =1684          rewriter.getI64IntegerAttr(destDistrDimOffset / subgroupSize);1685    }1686    // Do the distribution by yielding the source and dest of the insert op1687    // from the warp op and creating a new insert op outside the warp op.1688    SmallVector<size_t> newRetIndices;1689    auto newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1690        rewriter, warpOp, {insertOp.getValueToStore(), insertOp.getDest()},1691        {updatedSourceType, updatedDestType}, newRetIndices);1692    rewriter.setInsertionPointAfter(newWarpOp);1693 1694    Value valueToStore = newWarpOp.getResult(newRetIndices[0]);1695    Value dest = newWarpOp.getResult(newRetIndices[1]);1696    // Create a new insert op outside the warp op.1697    Value newInsertOp = vector::InsertStridedSliceOp::create(1698        rewriter, insertOp.getLoc(), updatedDestType, valueToStore, dest,1699        ArrayAttr::get(rewriter.getContext(), updatedOffsets),1700        insertOp.getStrides());1701    rewriter.replaceAllUsesWith(newWarpOp.getResult(operandNumber),1702                                newInsertOp);1703    return success();1704  }1705};1706 1707/// Sink a memref::ExtractAlignedPointerAsIndex op feeding into yield op of an1708/// enclosing `gpu.warp_execute_on_lane_0` region. This will simply move the op1709/// outside of the warp op.1710struct MemrefExtractAlignedPointerAsIndexDistribution final1711    : public gpu::WarpDistributionPattern {1712  using gpu::WarpDistributionPattern::WarpDistributionPattern;1713  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1714                                PatternRewriter &rewriter) const override {1715    OpOperand *operand = getWarpResult(1716        warpOp, llvm::IsaPred<memref::ExtractAlignedPointerAsIndexOp>);1717    if (!operand)1718      return rewriter.notifyMatchFailure(1719          warpOp,1720          "warp result is not a memref::MemrefExtractAlignedPointerAsIndex op");1721    auto extractOp =1722        operand->get().getDefiningOp<memref::ExtractAlignedPointerAsIndexOp>();1723    unsigned operandIdx = operand->getOperandNumber();1724    SmallVector<size_t> newRetIndices;1725    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1726        rewriter, warpOp, extractOp.getSource(),1727        TypeRange{extractOp.getSource().getType()}, newRetIndices);1728    rewriter.setInsertionPointAfter(newWarpOp);1729    auto newExtractOp = memref::ExtractAlignedPointerAsIndexOp::create(1730        rewriter, newWarpOp.getLoc(), extractOp.getType(),1731        newWarpOp.getResult(newRetIndices[0]));1732    Value distributedVal = newWarpOp.getResult(operandIdx);1733    rewriter.replaceAllUsesWith(distributedVal, newExtractOp.getResult());1734    return success();1735  }1736};1737 1738/// Distribute a vector::BitCastOp feeding into yield op of an enclosing1739/// `gpu.warp_execute_on_lane_0` region. Bitcast only impacts the innermost1740/// diemension of the source/result vectors. Equivalent vector::BitCastOp is1741/// created outside of the warp op with distributed source vector type (computed1742/// using assigned layout).1743struct VectorBitcastDistribution final : public gpu::WarpDistributionPattern {1744  using gpu::WarpDistributionPattern::WarpDistributionPattern;1745  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1746                                PatternRewriter &rewriter) const override {1747    OpOperand *operand =1748        getWarpResult(warpOp, llvm::IsaPred<vector::BitCastOp>);1749    if (!operand)1750      return rewriter.notifyMatchFailure(1751          warpOp, "warp result is not a vector::BitCast op");1752    auto bitcastOp = operand->get().getDefiningOp<vector::BitCastOp>();1753    unsigned operandIdx = operand->getOperandNumber();1754    VectorType distributedSourceType =1755        getDistVecTypeBasedOnLaneLayout(1756            xegpu::getDistributeLayoutAttr(bitcastOp.getSource()),1757            bitcastOp.getSourceVectorType())1758            .value_or(VectorType());1759    if (!distributedSourceType)1760      return rewriter.notifyMatchFailure(1761          bitcastOp, "Failed to distribute the source vector type in "1762                     "vector::BitCast op");1763    VectorType distributedResultType =1764        cast<VectorType>(warpOp.getResult(operandIdx).getType());1765    SmallVector<size_t> newRetIndices;1766    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1767        rewriter, warpOp, bitcastOp.getSource(),1768        TypeRange{distributedSourceType}, newRetIndices);1769    rewriter.setInsertionPointAfter(newWarpOp);1770    auto newBitcastOp = vector::BitCastOp::create(1771        rewriter, newWarpOp.getLoc(), distributedResultType,1772        newWarpOp.getResult(newRetIndices[0]));1773    Value distributedVal = newWarpOp.getResult(operandIdx);1774    rewriter.replaceAllUsesWith(distributedVal, newBitcastOp.getResult());1775    return success();1776  }1777};1778 1779/// Distribute a vector::TransposeOp feeding into yield op of an enclosing1780/// `gpu.warp_execute_on_lane_0` region. Currently only 2D transposes are1781/// supported. In most cases, transpose is a no op because it is entirely1782/// handled using the layouts (e.g. 16x1 -> 1x16). However, if each lane owns1783/// multiple slices of data after distribution (e.g. 16x2 -> 2x16), a lane-local1784/// transpose (i.e. shuffle) is needed. Therefore, we create an equivalent1785/// vector::TransposeOp outside of the warp op with distributed source vector1786/// type (computed using assigned layout).1787struct VectorTransposeDistribution final : public gpu::WarpDistributionPattern {1788  using gpu::WarpDistributionPattern::WarpDistributionPattern;1789  LogicalResult matchAndRewrite(gpu::WarpExecuteOnLane0Op warpOp,1790                                PatternRewriter &rewriter) const override {1791    OpOperand *operand =1792        getWarpResult(warpOp, llvm::IsaPred<vector::TransposeOp>);1793    if (!operand)1794      return rewriter.notifyMatchFailure(1795          warpOp, "warp result is not a vector::Transpose op");1796    auto transposeOp = operand->get().getDefiningOp<vector::TransposeOp>();1797    unsigned operandIdx = operand->getOperandNumber();1798    xegpu::DistributeLayoutAttr sourceLayout =1799        xegpu::getDistributeLayoutAttr(transposeOp.getVector());1800    xegpu::DistributeLayoutAttr resultLayout =1801        xegpu::getDistributeLayoutAttr(transposeOp.getResult());1802    if (!sourceLayout || !resultLayout)1803      return rewriter.notifyMatchFailure(1804          transposeOp,1805          "the source or result vector of the transpose op lacks layout "1806          "attribute");1807    int64_t sourceRank = transposeOp.getSourceVectorType().getRank();1808    int64_t resultRank = transposeOp.getResultVectorType().getRank();1809    // Only 2D transposes are supported for now.1810    // TODO: Support nD transposes.1811    if (sourceRank != 2 || resultRank != 2)1812      return rewriter.notifyMatchFailure(1813          transposeOp, "the source or result vector of the transpose op "1814                       "does not have 2D layout");1815    ArrayRef<int64_t> perm = transposeOp.getPermutation();1816    // Result layout must be a transpose of source layout.1817    if (!resultLayout.isTransposeOf(sourceLayout, perm))1818      return rewriter.notifyMatchFailure(1819          transposeOp,1820          "the source or result vector layouts must be 2D transposes of each "1821          "other");1822    FailureOr<VectorType> distributedSourceTypeOrFailure =1823        getDistVecTypeBasedOnLaneLayout(sourceLayout,1824                                        transposeOp.getSourceVectorType());1825    if (failed(distributedSourceTypeOrFailure))1826      return rewriter.notifyMatchFailure(1827          transposeOp, "Failed to distribute the source vector type in "1828                       "vector::Transpose op");1829    SmallVector<size_t> newRetIndices;1830    gpu::WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(1831        rewriter, warpOp, transposeOp.getVector(),1832        TypeRange{distributedSourceTypeOrFailure.value()}, newRetIndices);1833    rewriter.setInsertionPointAfter(newWarpOp);1834    auto newTransposeOp = vector::TransposeOp::create(1835        rewriter, newWarpOp.getLoc(), newWarpOp.getResult(newRetIndices[0]),1836        perm);1837    Value distributedVal = newWarpOp.getResult(operandIdx);1838    rewriter.replaceAllUsesWith(distributedVal, newTransposeOp.getResult());1839    return success();1840  }1841};1842 1843} // namespace1844 1845namespace {1846struct XeGPUSubgroupDistributePass final1847    : public xegpu::impl::XeGPUSubgroupDistributeBase<1848          XeGPUSubgroupDistributePass> {1849  void runOnOperation() override;1850};1851} // namespace1852 1853void xegpu::populateXeGPUSubgroupDistributePatterns(1854    RewritePatternSet &patterns) {1855  patterns.add<CreateNdDescDistribution, StoreNdDistribution,1856               LoadNdDistribution, DpasDistribution, PrefetchNdDistribution,1857               GpuBarrierDistribution, VectorMultiReductionDistribution,1858               LoadDistribution, StoreDistribution, VectorTransposeDistribution,1859               VectorBitcastDistribution, LoadMatrixDistribution,1860               StoreMatrixDistribution,1861               MemrefExtractAlignedPointerAsIndexDistribution>(1862      patterns.getContext(),1863      /*pattern benefit=*/regularPatternBenefit);1864  // For following patterns, we need to override the regular vector distribution1865  // patterns. Therefore, assign higher benefit.1866  patterns1867      .add<VectorShapeCastDistribution, VectorExtractStridedSliceDistribution,1868           VectorInsertStridedSliceDistribution>(1869          patterns.getContext(),1870          /*pattern benefit=*/highPatternBenefit);1871}1872 1873void xegpu::populateXeGPUMoveFuncBodyToWarpOpPatterns(1874    RewritePatternSet &patterns) {1875  patterns.add<MoveFuncBodyToWarpOp>(patterns.getContext());1876}1877 1878void XeGPUSubgroupDistributePass::runOnOperation() {1879  // Step 1: Attach layouts to op operands.1880  // TODO: Following assumptions are made:1881  // 1) It is assumed that there are no layout conflicts.1882  // 2) Any existing layout attributes attached to the operands are ignored.1883  Operation *op = getOperation();1884  op->walk([&](Operation *op) {1885    for (OpOperand &operand : op->getOpOperands()) {1886      // Layouts are needed for vector type only.1887      if (!isa<VectorType>(operand.get().getType()))1888        continue;1889      if (isa<xegpu::LoadMatrixOp, xegpu::StoreMatrixOp>(op))1890        continue;1891 1892      auto layout = xegpu::getDistributeLayoutAttr(operand.get());1893      if (!layout) {1894        op->emitError("Could not find layout attribute for operand ")1895            << operand.getOperandNumber() << " of operation " << op->getName();1896        signalPassFailure();1897        return;1898      }1899      xegpu::setDistributeLayoutAttr(operand, layout);1900    }1901  });1902  // Step 2: Move all operations of a GPU function inside1903  // gpu.warp_execute_on_lane_0 operation.1904  {1905    RewritePatternSet patterns(&getContext());1906    xegpu::populateXeGPUMoveFuncBodyToWarpOpPatterns(patterns);1907 1908    if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {1909      signalPassFailure();1910      return;1911    }1912    // At this point, we have moved the entire function body inside the1913    // warpOp. Now move any scalar uniform code outside of the warpOp (like1914    // GPU index ops, scalar constants, etc.). This will simplify the1915    // later lowering and avoid custom patterns for these ops.1916    getOperation()->walk([&](Operation *op) {1917      if (auto warpOp = dyn_cast<gpu::WarpExecuteOnLane0Op>(op))1918        vector::moveScalarUniformCode(warpOp);1919    });1920  }1921  // Step 3: Apply subgroup to workitem distribution patterns.1922  RewritePatternSet patterns(&getContext());1923  xegpu::populateXeGPUSubgroupDistributePatterns(patterns);1924  // distributionFn is used by vector distribution patterns to determine the1925  // distributed vector type for a given vector value. In XeGPU subgroup1926  // distribution context, we compute this based on lane layout.1927  auto distributionFn = [](Value val) {1928    VectorType vecType = dyn_cast<VectorType>(val.getType());1929    int64_t vecRank = vecType ? vecType.getRank() : 0;1930    if (vecRank == 0)1931      return AffineMap::get(val.getContext());1932    // Get the layout of the vector type.1933    xegpu::DistributeLayoutAttr layout = xegpu::getDistributeLayoutAttr(val);1934    // If no layout is specified, that means no distribution.1935    if (!layout)1936      return AffineMap::getMultiDimMapWithTargets(vecRank, {},1937                                                  val.getContext());1938    // Expecting vector and layout rank to match.1939    assert(layout.getRank() == vecRank &&1940           "Expecting vector and layout rank to match");1941    // A dimension is distributed only if layout suggests there are1942    // multiple lanes assigned for this dimension and the shape can be evenly1943    // distributed to those lanes.1944    SmallVector<unsigned int> distributedDims;1945    for (auto [i, v] : llvm::enumerate(layout.getEffectiveLaneLayoutAsInt())) {1946      if (v > 1 && vecType.getShape()[i] % v == 0)1947        distributedDims.push_back(i);1948    }1949    return AffineMap::getMultiDimMapWithTargets(vecRank, distributedDims,1950                                                val.getContext());1951  };1952  // TODO: shuffleFn is not used.1953  auto shuffleFn = [](Location loc, OpBuilder &builder, Value val, Value srcIdx,1954                      int64_t warpSz) { return Value(); };1955 1956  auto warpReduction = [](Location loc, OpBuilder &builder, Value input,1957                          vector::CombiningKind kind, uint32_t size) {1958    // First reduce on a single thread to get per lane reduction value.1959    Value laneVal = vector::ReductionOp::create(builder, loc, kind, input);1960    // Parallel reduction using butterfly shuffles.1961    for (uint64_t i = 1; i < size; i <<= 1) {1962      Value shuffled = gpu::ShuffleOp::create(builder, loc, laneVal, i,1963                                              /*width=*/size,1964                                              /*mode=*/gpu::ShuffleMode::XOR)1965                           .getShuffleResult();1966      laneVal = makeArithReduction(builder, loc, kind, laneVal, shuffled);1967    }1968    return laneVal;1969  };1970 1971  vector::populateDistributeReduction(1972      patterns, warpReduction,1973      /*pattern benefit=*/regularPatternBenefit);1974 1975  vector::populatePropagateWarpVectorDistributionPatterns(1976      patterns, distributionFn, shuffleFn,1977      /*pattern benefit=*/regularPatternBenefit);1978  if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {1979    signalPassFailure();1980    return;1981  }1982 1983  // Step 4: Finally, clean up UnrealizedConversionCastOps that were inserted1984  // due to tensor desc type mismatches created by using upstream distribution1985  // patterns (scf.for). This cleanup should only be done if all the ops are1986  // distributed successfully, if some ops are still not distributed and remains1987  // inside any WarpExecuteOnLane0Op we avoid this simplication step to avoid1988  // breaking the IR.1989  bool foundWarpOp = false;1990  getOperation()->walk([&](gpu::WarpExecuteOnLane0Op warpOp) {1991    // Look for WarpOps that are not trivially dead.1992    if (isOpTriviallyDead(warpOp))1993      return WalkResult::advance();1994    foundWarpOp = true;1995    return WalkResult::interrupt();1996  });1997  if (foundWarpOp)1998    return;1999 2000  getOperation()->walk([&](mlir::UnrealizedConversionCastOp op) {2001    // We are only interested in UnrealizedConversionCastOps there were added2002    // for resolving SIMT type mismatches.2003    if (!op->getAttr(resolveSIMTTypeMismatch))2004      return WalkResult::skip();2005 2006    Value input = op.getOperand(0);2007    Value output = op.getResult(0);2008 2009    // Both input and output must have tensor descriptor types.2010    xegpu::TensorDescType inputDescType =2011        mlir::dyn_cast<xegpu::TensorDescType>(input.getType());2012    xegpu::TensorDescType outputDescType =2013        mlir::dyn_cast<xegpu::TensorDescType>(output.getType());2014    assert(inputDescType && outputDescType &&2015           "Unrealized conversion cast must have tensor descriptor types");2016 2017    // tensor_desc<shape, layout> -> tensor_desc<shape> Type of conversions.2018    // This occurs inside scf.for body to resolve the block argument type to2019    // SIMT type.2020    if (inputDescType.getLayout()) {2021      auto argument = mlir::dyn_cast<mlir::BlockArgument>(input);2022      if (argument) {2023        argument.setType(output.getType());2024        output.replaceAllUsesWith(argument);2025        if (auto loopOp = mlir::dyn_cast<mlir::LoopLikeOpInterface>(2026                argument.getOwner()->getParentOp())) {2027          auto result = loopOp.getTiedLoopResult(argument);2028          result.setType(output.getType());2029        }2030      }2031    }2032 2033    // tensor_desc<shape> -> tensor_desc<shape, layout> Type of2034    // conversions. This occurs at the yield op of scf.for body to go back2035    // from SIMT type to original type.2036    if (outputDescType.getLayout())2037      output.replaceAllUsesWith(input);2038 2039    if (op->use_empty())2040      op->erase();2041    return WalkResult::advance();2042  });2043}2044