Skip to content

Commit 1dd9952

Browse files
authored
[Torch] optimize repeat singletons (#4572)
Optimize aten.repeat decomposition for singleton input dimensions. When an existing input dim has size 1 and its repeat value is greater than 1, the repeated dim can be represented directly in the aten.broadcast_to shape. This avoids the pattern of inserting an extra dimension, broadcasting, and then folding it back with flatten. Non-singleton repeated dims keep the existing decomposition path. Added a lit test covering mixed repeated dims, where singleton dims use direct broadcast and a non-singleton repeated dim still uses the existing reshape path.
1 parent 3da108c commit 1dd9952

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

lib/Dialect/Torch/Transforms/DecomposeComplexOps.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5096,6 +5096,17 @@ class DecomposeAtenRepeatOp : public OpRewritePattern<AtenRepeatOp> {
50965096
repeatInts.push_back(repeat);
50975097
}
50985098

5099+
// Track repeated singleton dims that can be materialized with broadcast.
5100+
llvm::SmallVector<int64_t> selfSizes(selfTy.getSizes().begin(),
5101+
selfTy.getSizes().end());
5102+
llvm::SmallVector<bool> broadcastRepeatedSingletonDims(repeats.size(),
5103+
false);
5104+
for (int i = batch, s = repeats.size(); i < s; ++i) {
5105+
int64_t inputDim = i - batch;
5106+
broadcastRepeatedSingletonDims[i] =
5107+
selfSizes[inputDim] == 1 && repeatInts[i] > 1;
5108+
}
5109+
50995110
// Unsqueeze all newly created dims
51005111
llvm::SmallVector<int> unsqueezeDims;
51015112
for (int i = 0; i < batch; ++i) {
@@ -5106,9 +5117,9 @@ class DecomposeAtenRepeatOp : public OpRewritePattern<AtenRepeatOp> {
51065117
unsqueezeDims.push_back(i);
51075118
}
51085119

5109-
// Unsqueeze any non-unary repeats for existing dims
5120+
// Unsqueeze non-unary repeats, except singleton dims handled by broadcast.
51105121
for (int i = batch, s = repeats.size(); i < s; ++i) {
5111-
if (repeatInts[i] == 1)
5122+
if (repeatInts[i] == 1 || broadcastRepeatedSingletonDims[i])
51125123
continue;
51135124
int64_t dim = i + unsqueezeDims.size() - batch;
51145125
Value iv =
@@ -5127,6 +5138,12 @@ class DecomposeAtenRepeatOp : public OpRewritePattern<AtenRepeatOp> {
51275138
}
51285139

51295140
for (int i = batch, s = repeats.size(); i < s; ++i) {
5141+
if (broadcastRepeatedSingletonDims[i]) {
5142+
lengths.push_back(repeats[i]);
5143+
expandShape.push_back(repeatInts[i]);
5144+
continue;
5145+
}
5146+
51305147
if (repeatInts[i] != 1) {
51315148
lengths.push_back(repeats[i]);
51325149
expandShape.push_back(repeatInts[i]);
@@ -5149,7 +5166,7 @@ class DecomposeAtenRepeatOp : public OpRewritePattern<AtenRepeatOp> {
51495166

51505167
auto outShape = cast<ValueTensorType>(op.getResult().getType()).getSizes();
51515168
for (int i = batch, s = repeats.size(); i < s; ++i) {
5152-
if (repeatInts[i] == 1)
5169+
if (repeatInts[i] == 1 || broadcastRepeatedSingletonDims[i])
51535170
continue;
51545171

51555172
auto selfShape = selfTy.getSizes();

test/Dialect/Torch/decompose-complex-ops.mlir

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,3 +1090,49 @@ func.func @torch.aten.mish$f8E8M0FNU(%arg0: !torch.vtensor<[2,3],f8E8M0FNU>) ->
10901090
%0 = torch.aten.mish %arg0 : !torch.vtensor<[2,3],f8E8M0FNU> -> !torch.vtensor<[2,3],f8E8M0FNU>
10911091
return %0 : !torch.vtensor<[2,3],f8E8M0FNU>
10921092
}
1093+
1094+
// -----
1095+
1096+
// CHECK-LABEL: func.func @repeat_mixed_dims_broadcast_singletons
1097+
// CHECK-SAME: (%[[ARG0:.*]]: !torch.vtensor<[1,2,1],f32>) -> !torch.vtensor<[3,8,5],f32>
1098+
// CHECK-DAG: %[[CNEG1:.*]] = torch.constant.int -1
1099+
// CHECK-DAG: %[[C1:.*]] = torch.constant.int 1
1100+
// CHECK-DAG: %[[C2:.*]] = torch.constant.int 2
1101+
// CHECK-DAG: %[[C3:.*]] = torch.constant.int 3
1102+
// CHECK-DAG: %[[C4:.*]] = torch.constant.int 4
1103+
// CHECK-DAG: %[[C5:.*]] = torch.constant.int 5
1104+
// CHECK: %[[UNSQUEEZE:.*]] = torch.aten.unsqueeze %[[ARG0]], %[[C1]] : !torch.vtensor<[1,2,1],f32>, !torch.int -> !torch.vtensor<[1,1,2,1],f32>
1105+
// CHECK: %[[SHAPE:.*]] = torch.prim.ListConstruct %[[C3]], %[[C4]], %[[C2]], %[[C5]] : (!torch.int, !torch.int, !torch.int, !torch.int) -> !torch.list<int>
1106+
// CHECK: %[[BROADCAST:.*]] = torch.aten.broadcast_to %[[UNSQUEEZE]], %[[SHAPE]] : !torch.vtensor<[1,1,2,1],f32>, !torch.list<int> -> !torch.vtensor<[3,4,2,5],f32>
1107+
// CHECK: %[[VIEW_SHAPE:.*]] = torch.prim.ListConstruct %[[C3]], %[[CNEG1]], %[[C5]] : (!torch.int, !torch.int, !torch.int) -> !torch.list<int>
1108+
// CHECK: %[[VIEW:.*]] = torch.aten.view %[[BROADCAST]], %[[VIEW_SHAPE]] : !torch.vtensor<[3,4,2,5],f32>, !torch.list<int> -> !torch.vtensor<[3,8,5],f32>
1109+
// CHECK: return %[[VIEW]] : !torch.vtensor<[3,8,5],f32>
1110+
func.func @repeat_mixed_dims_broadcast_singletons(%arg0: !torch.vtensor<[1,2,1],f32>) -> !torch.vtensor<[3,8,5],f32> {
1111+
%int3 = torch.constant.int 3
1112+
%int4 = torch.constant.int 4
1113+
%int5 = torch.constant.int 5
1114+
%0 = torch.prim.ListConstruct %int3, %int4, %int5 : (!torch.int, !torch.int, !torch.int) -> !torch.list<int>
1115+
%1 = torch.aten.repeat %arg0, %0 : !torch.vtensor<[1,2,1],f32>, !torch.list<int> -> !torch.vtensor<[3,8,5],f32>
1116+
return %1 : !torch.vtensor<[3,8,5],f32>
1117+
}
1118+
1119+
// -----
1120+
1121+
// CHECK-LABEL: func @repeat_broadcasts_static_singleton_dims
1122+
func.func @repeat_broadcasts_static_singleton_dims(%arg0: !torch.vtensor<[1,1,6,1,4,4],f32>) -> !torch.vtensor<[4,1,6,2500,4,4],f32> {
1123+
%int4 = torch.constant.int 4
1124+
%int1 = torch.constant.int 1
1125+
%int2500 = torch.constant.int 2500
1126+
// CHECK-DAG: %[[C4:.*]] = torch.constant.int 4
1127+
// CHECK-DAG: %[[C2500:.*]] = torch.constant.int 2500
1128+
// CHECK-DAG: %[[C1:.*]] = torch.constant.int 1
1129+
// CHECK-DAG: %[[C6:.*]] = torch.constant.int 6
1130+
// CHECK: %[[SHAPE:.*]] = torch.prim.ListConstruct %[[C4]], %[[C1]], %[[C6]], %[[C2500]], %[[C4]], %[[C4]] : (!torch.int, !torch.int, !torch.int, !torch.int, !torch.int, !torch.int) -> !torch.list<int>
1131+
// CHECK-NOT: torch.aten.unsqueeze
1132+
// CHECK-NOT: torch.aten.flatten
1133+
// CHECK: %[[BROADCAST:.*]] = torch.aten.broadcast_to %arg0, %[[SHAPE]] : !torch.vtensor<[1,1,6,1,4,4],f32>, !torch.list<int> -> !torch.vtensor<[4,1,6,2500,4,4],f32>
1134+
// CHECK: return %[[BROADCAST]] : !torch.vtensor<[4,1,6,2500,4,4],f32>
1135+
%repeats = torch.prim.ListConstruct %int4, %int1, %int1, %int2500, %int1, %int1 : (!torch.int, !torch.int, !torch.int, !torch.int, !torch.int, !torch.int) -> !torch.list<int>
1136+
%0 = torch.aten.repeat %arg0, %repeats : !torch.vtensor<[1,1,6,1,4,4],f32>, !torch.list<int> -> !torch.vtensor<[4,1,6,2500,4,4],f32>
1137+
return %0 : !torch.vtensor<[4,1,6,2500,4,4],f32>
1138+
}

0 commit comments

Comments
 (0)