[LLVMGPU] Add tests for Attention subgroup reduction pipeline (#19401)

Also removes an untested and wrong visitor in VectorLayoutAnalysis and a
fix for 0-d transfer_write distribution.
diff --git a/compiler/src/iree/compiler/Codegen/Common/VectorLayoutAnalysis.cpp b/compiler/src/iree/compiler/Codegen/Common/VectorLayoutAnalysis.cpp
index aeac222..5030020 100644
--- a/compiler/src/iree/compiler/Codegen/Common/VectorLayoutAnalysis.cpp
+++ b/compiler/src/iree/compiler/Codegen/Common/VectorLayoutAnalysis.cpp
@@ -136,9 +136,6 @@
                              RegionBranchPoint branchPoint,
                              MutableArrayRef<OpOperand> operands);
 
-  void visitRegionBranchTerminatorOpInterface(RegionBranchOpInterface branch,
-                                              RegionBranchPoint branchPoint);
-
   DistributionLayout *getLatticeElement(Value val);
 
   MLIRContext *ctx;
@@ -1006,9 +1003,6 @@
   if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
     visitRegionSuccessors(branch, RegionBranchPoint::parent(),
                           branch->getOpOperands());
-
-    // Handle the propagation from scf.for to yield op.
-    visitRegionBranchTerminatorOpInterface(branch, RegionBranchPoint::parent());
     return;
   }
 
@@ -1101,43 +1095,6 @@
   }
 }
 
-void EnforceLayout::visitRegionBranchTerminatorOpInterface(
-    RegionBranchOpInterface branch, RegionBranchPoint branchPoint) {
-  SmallVector<RegionSuccessor> successors;
-  branch.getSuccessorRegions(branchPoint, successors);
-  if (!branch.hasLoop())
-    return;
-  SmallVector<DistributionLayout *> resultLattices;
-  for (Value result : branch->getResults()) {
-    DistributionLayout *resultLattice = getLatticeElement(result);
-    if (resultLattice->isUninitialized())
-      continue;
-    resultLattices.push_back(resultLattice);
-  }
-
-  // We do not support multiple results yet.
-  if (resultLattices.size() != 1)
-    return;
-
-  for (RegionSuccessor successor : successors) {
-    if (Region *succ = successor.getSuccessor()) {
-      Operation *terminator = succ->back().getTerminator();
-      if (scf::YieldOp yieldOp = dyn_cast<scf::YieldOp>(terminator)) {
-        for (Value operand : yieldOp.getOperands()) {
-          if (!isa<VectorType>(operand.getType())) {
-            continue;
-          }
-          DistributionLayout *forwardLattice = getLatticeElement(operand);
-          ChangeResult changed = forwardLattice->resolve(resultLattices[0]);
-          propagateIfChanged(forwardLattice, changed);
-        }
-      }
-    }
-  }
-
-  return;
-}
-
 DistributionLayout *EnforceLayout::getLatticeElement(Value val) {
   // Add dependency of operation on the analysis state.
   assert(isa<VectorType>(val.getType()) && "Lattice value should be a vector");
diff --git a/compiler/src/iree/compiler/Codegen/LLVMGPU/test/ROCDL/pipeline_vector_distribute_reduction_gfx942.mlir b/compiler/src/iree/compiler/Codegen/LLVMGPU/test/ROCDL/pipeline_vector_distribute_reduction_gfx942.mlir
index 6b6bcd8..62e1738 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMGPU/test/ROCDL/pipeline_vector_distribute_reduction_gfx942.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMGPU/test/ROCDL/pipeline_vector_distribute_reduction_gfx942.mlir
@@ -178,3 +178,98 @@
 
 //          CHECK:      scf.yield
 //          CHECK:    vector.transfer_write
+
+// -----
+
+// Tile Sizes:
+// workgroup = [1, 1,  0,   0, 32]
+// reduction = [0, 0,  0, 128,  0]
+// subgroup  = [0, 0,  0,   0,  8]
+// thread    = [0, 0, 32,   4,  4]
+
+// Counts:
+// subgroup  = [1, 1,  1,   1,  4]
+// threads   = [1, 1,  2,  32,  1]
+
+#config = #iree_gpu.lowering_config<{workgroup = [1, 1, 0, 0, 32],
+                                     reduction = [0, 0, 0, 128, 0],
+                                     promote_operands = [1, 2]}>
+
+#qk_config = #iree_gpu.lowering_config<{subgroup_basis = [[1, 1, 1, 1, 4], [0, 1, 2, 3]],
+                                        thread_basis   = [[1, 1, 2, 32, 1], [0, 1, 2, 3]],
+                                        thread         = [0, 0, 32, 4],
+                                        promote_operands = [1]}>
+
+#pv_config = #iree_gpu.lowering_config<{subgroup_basis = [[1, 1, 1, 1, 4], [0, 1, 3, 4]],
+                                        thread_basis   = [[1, 1, 2, 32, 1], [0, 1, 3, 4]],
+                                        thread         = [0, 0, 4, 4],
+                                        promote_operands = [1]}>
+
+#translation = #iree_codegen.translation_info<pipeline = LLVMGPUVectorDistribute
+                                              workgroup_size = [256, 1, 1]
+                                              subgroup_size = 64>
+
+#pipeline_layout = #hal.pipeline.layout<bindings = [
+  #hal.pipeline.binding<storage_buffer>,
+  #hal.pipeline.binding<storage_buffer>,
+  #hal.pipeline.binding<storage_buffer>,
+  #hal.pipeline.binding<storage_buffer>
+]>
+hal.executable private @attention_20x1x64x4096x64 {
+  hal.executable.variant public @rocm_hsaco_fb target(<"rocm", "rocm-hsaco-fb">) {
+    hal.executable.export public @attention_20x1x64x4096x64 ordinal(0) layout(#pipeline_layout) {
+    ^bb0(%arg0: !hal.device):
+      %x, %y, %z = flow.dispatch.workgroup_count_from_slice
+      hal.return %x, %y, %z : index, index, index
+    }
+    builtin.module {
+      func.func @attention_20x1x64x4096x64() attributes {translation_info = #translation} {
+        %cst = arith.constant 1.250000e-01 : f16
+        %c0 = arith.constant 0 : index
+        %0 = hal.interface.binding.subspan layout(#pipeline_layout) binding(0) alignment(64) offset(%c0) flags(ReadOnly) : !flow.dispatch.tensor<readonly:tensor<20x1x64xf16>>
+        %1 = hal.interface.binding.subspan layout(#pipeline_layout) binding(1) alignment(64) offset(%c0) flags(ReadOnly) : !flow.dispatch.tensor<readonly:tensor<20x4096x64xf16>>
+        %2 = hal.interface.binding.subspan layout(#pipeline_layout) binding(2) alignment(64) offset(%c0) flags(ReadOnly) : !flow.dispatch.tensor<readonly:tensor<20x4096x64xf16>>
+        %3 = hal.interface.binding.subspan layout(#pipeline_layout) binding(3) alignment(64) offset(%c0) : !flow.dispatch.tensor<writeonly:tensor<20x1x64xf16>>
+        %4 = flow.dispatch.tensor.load %0, offsets = [0, 0, 0], sizes = [20, 1, 64], strides = [1, 1, 1] : !flow.dispatch.tensor<readonly:tensor<20x1x64xf16>> -> tensor<20x1x64xf16>
+        %5 = flow.dispatch.tensor.load %1, offsets = [0, 0, 0], sizes = [20, 4096, 64], strides = [1, 1, 1] : !flow.dispatch.tensor<readonly:tensor<20x4096x64xf16>> -> tensor<20x4096x64xf16>
+        %6 = flow.dispatch.tensor.load %2, offsets = [0, 0, 0], sizes = [20, 4096, 64], strides = [1, 1, 1] : !flow.dispatch.tensor<readonly:tensor<20x4096x64xf16>> -> tensor<20x4096x64xf16>
+        %7 = tensor.empty() : tensor<20x1x64xf16>
+        %8 = iree_linalg_ext.attention  {indexing_maps = [affine_map<(d0, d1, d2, d3, d4) -> (d0, d1, d2)>,
+                     affine_map<(d0, d1, d2, d3, d4) -> (d0, d3, d2)>,
+                     affine_map<(d0, d1, d2, d3, d4) -> (d0, d3, d4)>,
+                     affine_map<(d0, d1, d2, d3, d4) -> ()>,
+                     affine_map<(d0, d1, d2, d3, d4) -> (d0, d1, d4)>],
+                     lowering_config = #config,
+                     decomposition_config = {
+                      qk_attrs = {lowering_config = #qk_config},
+                      pv_attrs = {lowering_config = #pv_config}
+                     }}
+                     ins(%4, %5, %6, %cst : tensor<20x1x64xf16>, tensor<20x4096x64xf16>, tensor<20x4096x64xf16>, f16) outs(%7 : tensor<20x1x64xf16>) {
+                      ^bb0(%score: f32):
+                        iree_linalg_ext.yield %score : f32
+                     } -> tensor<20x1x64xf16>
+        flow.dispatch.tensor.store %8, %3, offsets = [0, 0, 0], sizes = [20, 1, 64], strides = [1, 1, 1] : tensor<20x1x64xf16> -> !flow.dispatch.tensor<writeonly:tensor<20x1x64xf16>>
+        return
+      }
+    }
+  }
+}
+
+// CHECK-LABEL: func.func @attention_20x1x64x4096x64
+// CHECK:         scf.for %{{.*}} = %c0 to %c4096 step %c128
+// QK Matmul
+// CHECK:           vector.contract
+// CHECK-SAME:      vector<1x1x32xf16>, vector<1x1x1x1x4x32xf16> into vector<1x1x4xf32>
+// CHECK-COUNT-4:   gpu.subgroup_reduce  add
+
+// QK Max
+// CHECK-COUNT-1:   gpu.subgroup_reduce  maximumf
+
+// PV Sum
+// CHECK-COUNT-1:   gpu.subgroup_reduce  add
+
+// PV Matmul
+// CHECK:           vector.contract
+// CHECK-COUNT-8:   gpu.subgroup_reduce  add
+
+// CHECK:           scf.yield