@@ -1026,10 +1026,16 @@ struct JoinConverter : public OpConversionPattern<triton::JoinOp> {
10261026 }
10271027};
10281028
1029- // Lowers `tt.gather` to a `linalg.generic` that reads `indices` through the
1030- // identity affine map and, for each output position, extracts the gathered
1031- // element out of `src` via `tensor.extract` at a dynamically-computed
1032- // coordinate.
1029+ // Lowers `tt.gather` to `tensor.gather`. `tt.gather`'s `indices` has the same
1030+ // shape as the output, with only the `axis` coordinate varying per element,
1031+ // whereas `tensor.gather` expects a coordinate tuple per output element
1032+ // covering every gathered dimension. We bridge the two by gathering along
1033+ // *all* source dimensions: a coordinate tensor of shape
1034+ // `indicesShape ++ [rank]` is built where the `axis` slot holds `indices`
1035+ // (cast to index) and every other slot `d` holds that element's own index
1036+ // along dimension `d` (an identity/iota broadcast). With every dimension
1037+ // listed in `gather_dims`, the rank-reduced result shape collapses to exactly
1038+ // `indicesShape`, matching `tt.gather`'s output shape.
10331039struct GatherConverter : public OpConversionPattern <triton::GatherOp> {
10341040 using OpConversionPattern<triton::GatherOp>::OpConversionPattern;
10351041
@@ -1050,43 +1056,62 @@ struct GatherConverter : public OpConversionPattern<triton::GatherOp> {
10501056
10511057 int64_t axis = op.getAxis ();
10521058 int64_t rank = idxType.getRank ();
1059+ ArrayRef<int64_t > idxShape = idxType.getShape ();
1060+ Type indexElemType = rewriter.getIndexType ();
1061+
1062+ SmallVector<int64_t > coordsShape (idxShape.begin (), idxShape.end ());
1063+ coordsShape.push_back (rank);
1064+ Value coords =
1065+ tensor::EmptyOp::create (rewriter, loc, coordsShape, indexElemType);
1066+
1067+ auto idxIndexType = RankedTensorType::get (idxShape, indexElemType);
1068+ Value axisComponent =
1069+ arith::IndexCastOp::create (rewriter, loc, idxIndexType, indices);
1070+
1071+ SmallVector<OpFoldResult> offsets (rank + 1 , rewriter.getIndexAttr (0 ));
1072+ SmallVector<OpFoldResult> strides (rank + 1 , rewriter.getIndexAttr (1 ));
1073+ SmallVector<OpFoldResult> sizes;
1074+ sizes.reserve (rank + 1 );
1075+ for (int64_t dim : idxShape)
1076+ sizes.push_back (rewriter.getIndexAttr (dim));
1077+ sizes.push_back (rewriter.getIndexAttr (1 ));
1078+
1079+ SmallVector<AffineMap> iotaIndexingMaps (
1080+ 1 , rewriter.getMultiDimIdentityMap (rank));
1081+
1082+ for (int64_t dim = 0 ; dim < rank; ++dim) {
1083+ Value component;
1084+ if (dim == axis) {
1085+ component = axisComponent;
1086+ } else {
1087+ Value iotaInit =
1088+ tensor::EmptyOp::create (rewriter, loc, idxShape, indexElemType);
1089+ auto iotaOp = linalg::GenericOp::create (
1090+ rewriter, loc, TypeRange{iotaInit.getType ()}, ValueRange{},
1091+ ValueRange{iotaInit}, iotaIndexingMaps,
1092+ getNParallelLoopsAttrs (rank),
1093+ [&](OpBuilder &nestedBuilder, Location nestedLoc, ValueRange) {
1094+ Value idx =
1095+ linalg::IndexOp::create (nestedBuilder, nestedLoc, dim);
1096+ linalg::YieldOp::create (nestedBuilder, nestedLoc, idx);
1097+ });
1098+ component = iotaOp.getResult (0 );
1099+ }
10531100
1054- // indices and the output share the same shape, so both use the identity
1055- // map; src is captured below rather than driven through an ins() operand.
1056- SmallVector<AffineMap> indexingMaps (
1057- /* indices*/ 1 + /* output*/ 1 ,
1058- rewriter.getMultiDimIdentityMap (rank));
1101+ offsets.back () = rewriter.getIndexAttr (dim);
1102+ coords = tensor::InsertSliceOp::create (rewriter, loc, component, coords,
1103+ offsets, sizes, strides);
1104+ }
10591105
1060- Value init = tensor::EmptyOp::create (rewriter, loc, resType.getShape (),
1061- resType.getElementType ());
1106+ SmallVector<int64_t > gatherDims;
1107+ gatherDims.reserve (rank);
1108+ for (int64_t dim = 0 ; dim < rank; ++dim)
1109+ gatherDims.push_back (dim);
10621110
1063- auto linalgOp = linalg::GenericOp::create (
1064- rewriter, loc, op->getResultTypes (), ValueRange{indices},
1065- ValueRange{init}, indexingMaps, getNParallelLoopsAttrs (rank),
1066- [&](OpBuilder &nestedBuilder, Location nestedLoc,
1067- ValueRange blockArgs) {
1068- Value idxScalar = blockArgs[0 ];
1069- Value idxAsIndex = arith::IndexCastOp::create (
1070- nestedBuilder, nestedLoc, nestedBuilder.getIndexType (),
1071- idxScalar);
1072-
1073- SmallVector<Value> coords;
1074- coords.reserve (rank);
1075- for (int64_t dim = 0 ; dim < rank; ++dim) {
1076- if (dim == axis) {
1077- coords.push_back (idxAsIndex);
1078- } else {
1079- coords.push_back (
1080- linalg::IndexOp::create (nestedBuilder, nestedLoc, dim));
1081- }
1082- }
1111+ auto gatherOp = tensor::GatherOp::create (
1112+ rewriter, loc, resType, src, coords, ArrayRef<int64_t >(gatherDims));
10831113
1084- Value gathered =
1085- tensor::ExtractOp::create (nestedBuilder, nestedLoc, src, coords);
1086- linalg::YieldOp::create (nestedBuilder, nestedLoc, gathered);
1087- });
1088-
1089- rewriter.replaceOp (op, linalgOp->getResults ());
1114+ rewriter.replaceOp (op, gatherOp);
10901115 return success ();
10911116 }
10921117};
0 commit comments