[LLVMCPU] Fix scalable vectorization fallback on SME-only (no `+sve`) targets (#24693)

**Stacked** on #24656.  Follow-up to #24661 / #24660.

This PR aims to removes the TODO introduced in #24656.

#24661 fixed the ArmSME lowering pipeline to not require classic `+sve`
when SME tiling is used. This PR fixes the same underlying problem for
the *fallback* path, which is what's used when the matmul falls back to
it (e.g., `--iree-llvmcpu-disable-arm-sme-tiling`, or op types SME
doesn't support): compiling a scalable-vectorized matmul on a
`+sme`-only target with SME tiling disabled crashed LLVM instruction
selection with `Cannot select: vscale`.

The fix is scoped to a single function:
`getMatmulVectorSizesUsingFillRegisterFileHeuristic` only checked
`hasAnySVEFeature`, so on a `+sme`-only target it always fell back to
non-scalable NEON tiles. It now also picks scalable tiles when `+sme` is
present *and* the user has explicitly passed
`--iree-llvmcpu-force-arm-streaming`.

Net effect: 
1. Non-matmul ops: always Peeling.
4. `linalg.matmul`, `+sme-only`, `disable-arm-sme-tiling=true`, and
`force-arm-streaming=true`: still reaches SSVE (that's the actual fix),
but now goes through `Peeling + masked tail` instead of masking-only (as
in #24661).

Unrelated to #24689

---------

Signed-off-by: Federico Bruzzone <federico.bruzzone.i@gmail.com>
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp b/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
index 83c8444..59e5407 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
@@ -1372,7 +1372,9 @@
   auto targetAttr = IREE::HAL::ExecutableTargetAttr::lookup(op);
   scalableSizeFlags.resize(3, false);
   if (isScalableVectorizationEnabled() &&
-      hasAnySVEFeature(targetAttr.getConfiguration())) {
+      (hasAnySVEFeature(targetAttr.getConfiguration()) ||
+       (hasSMEFeature(targetAttr.getConfiguration()) &&
+        isArmStreamingForced()))) {
     scalableSizeFlags[1] = true;
   }
 }
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/Passes.cpp b/compiler/src/iree/compiler/Codegen/LLVMCPU/Passes.cpp
index 452f2b8..91fa67f 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/Passes.cpp
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/Passes.cpp
@@ -11,6 +11,7 @@
 #include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenAttrs.h"
 #include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenInterfaces.h"
 #include "iree/compiler/Codegen/LLVMCPU/Passes.h"
+#include "iree/compiler/Codegen/Utils/CPUUtils.h"
 #include "iree/compiler/Codegen/Utils/CodegenOptions.h"
 #include "iree/compiler/Dialect/LinalgExt/Transforms/Passes.h"
 #include "iree/compiler/Dialect/Util/Transforms/Passes.h"
@@ -60,19 +61,6 @@
                    "LLVMCPUMmt4dVectorLowering pass."),
     llvm::cl::init(false), llvm::cl::Hidden);
 
-// By default, IREE does not enable the Armv9-A streaming SVE mode in the
-// presence of scalable vectors (even when using `+sme`), as currently there's
-// no cost model of when it could be beneficial. This flag will effectively make
-// IREE/LLVM switch from SVE to SSVE in dispatch regions with supported
-// scalable vector operations.
-static llvm::cl::opt<bool> clForceArmStreaming(
-    "iree-llvmcpu-force-arm-streaming",
-    llvm::cl::desc(
-        "Enables Armv9-A streaming SVE mode for any dispatch region that "
-        "contains supported scalable vector operations (i.e., use SSVE rather "
-        "than SVE). Requires the +sme feature flag."),
-    llvm::cl::init(false), llvm::cl::Hidden);
-
 static llvm::cl::opt<bool> clPatchFuncOps(
     "iree-llvmcpu-debug-patch-func-ops",
     llvm::cl::desc(
@@ -548,7 +536,7 @@
     modulePassManager.addPass(mlir::arm_sme::createVectorLegalizationPass());
     FunctionLikeNest(modulePassManager)
         .addPredicatedPass(
-            clForceArmStreaming,
+            isArmStreamingForced(),
             [] {
               // 1. Enable Armv9-A streaming mode without ZA (i.e., SSVE) for
               // dispatch regions that contain scalable vectors when forced via
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_aarch64_ssve_lowering_strategy.mlir b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_aarch64_ssve_lowering_strategy.mlir
index 840d702..cbbc1be 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_aarch64_ssve_lowering_strategy.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_aarch64_ssve_lowering_strategy.mlir
@@ -1,21 +1,24 @@
 // RUN: iree-opt --pass-pipeline='builtin.module(iree-llvmcpu-select-lowering-strategy)' --iree-llvmcpu-enable-scalable-vectorization=true --split-input-file %s | FileCheck %s --check-prefixes=CHECK,WITH-SME
 // RUN: iree-opt --pass-pipeline='builtin.module(iree-llvmcpu-select-lowering-strategy)' --iree-llvmcpu-enable-scalable-vectorization=true --iree-llvmcpu-disable-arm-sme-tiling --split-input-file %s | FileCheck %s --check-prefixes=CHECK,DISABLE-ARM-SME
+// RUN: iree-opt --pass-pipeline='builtin.module(iree-llvmcpu-select-lowering-strategy)' --iree-llvmcpu-enable-scalable-vectorization=true --iree-llvmcpu-disable-arm-sme-tiling --iree-llvmcpu-force-arm-streaming --split-input-file %s | FileCheck %s --check-prefixes=CHECK,FORCE-STREAMING
 
-// A target with +sme but not +sve (i.e. no non-streaming SVE) still picks
-// scalable SME tile sizes when SME tiling is enabled: matmul lowering does
-// not itself require +sve. Note this goes through a different
-// pre-processing strategy than the +sve,+sme case: peeling, with an extra
-// `cache_parallel` tiling level.
+// A target with +sme but not +sve. Checks the lowering strategy selected for
+// a matmul when SME tiling is disabled, with and without
+// --iree-llvmcpu-force-arm-streaming.
 #executable_target_embedded_elf_arm_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-arm_64", {cpu_features = "+sme", data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", native_vector_size = 16 : index, target_triple = "aarch64-none-elf"}>
 func.func @matmul_tensors_sme_no_sve(%7: tensor<?x?xf32>, %8: tensor<?x?xf32>, %9: tensor<?x?xf32>) -> tensor<?x?xf32> attributes {hal.executable.target = #executable_target_embedded_elf_arm_64_} {
   %10 = linalg.matmul ins(%7, %8 : tensor<?x?xf32>, tensor<?x?xf32>) outs(%9 : tensor<?x?xf32>) -> tensor<?x?xf32>
   return %10 : tensor<?x?xf32>
 }
-// SME tiling disabled: falls back to non-scalable NEON tile sizes, since the
-// SVE fallback heuristic requires +sve (see TODO above).
+// SME tiling disabled, streaming not forced: falls back to non-scalable
+// NEON tile sizes (the default).
 //  DISABLE-ARM-SME-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 64, 0], distribution = [64, 64, 0], vector_common_parallel = [8, 8, 0], vector_reduction = [0, 0, 4]>
-// SME tiling enabled: still picks scalable [8]x[8] SME tile sizes.
+// SME tiling enabled: picks scalable M/N tile sizes regardless of
+// whether streaming is forced.
 //   WITH-SME-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 64, 0], distribution = [64, 64, 0], vector_common_parallel = {{\[}}[8], [8], 0], vector_reduction = [0, 0, 1]>
+// SME tiling disabled, streaming forced explicitly: falls back to the
+// regular SVE-style tiling heuristic, scalable on the N dimension only.
+//   FORCE-STREAMING-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 64, 0], distribution = [64, 64, 0], vector_common_parallel = [8, [8], 0], vector_reduction = [0, 0, 4]>
 //   CHECK-DAG: #[[TRANSLATION:.+]] = #iree_codegen.translation_info<pipeline = #iree_cpu.pipeline<DoubleTilingExpert>, {enable_loop_peeling}>
 //       CHECK: func.func @matmul_tensors_sme_no_sve(
 //  CHECK-SAME:     translation_info = #[[TRANSLATION]]
diff --git a/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.cpp b/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.cpp
index 9b6d3ef..f5cb417 100644
--- a/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.cpp
+++ b/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.cpp
@@ -27,6 +27,19 @@
                    "target (e.g., +sve, +sve2 and/or +sme feature flags)"),
     llvm::cl::init(false));
 
+// By default, IREE does not enable the Armv9-A streaming SVE mode in the
+// presence of scalable vectors (even when using `+sme`), as currently there's
+// no cost model of when it could be beneficial. This flag will effectively make
+// IREE/LLVM switch from SVE to SSVE in dispatch regions with supported
+// scalable vector operations.
+static llvm::cl::opt<bool> clForceArmStreaming(
+    "iree-llvmcpu-force-arm-streaming",
+    llvm::cl::desc(
+        "Enables Armv9-A streaming SVE mode for any dispatch region that "
+        "contains supported scalable vector operations (i.e., use SSVE rather "
+        "than SVE). Requires the +sme feature flag."),
+    llvm::cl::init(false), llvm::cl::Hidden);
+
 static llvm::cl::opt<int> clVscaleFromUser(
     "iree-experimental-vscale-value",
     llvm::cl::desc(
@@ -112,6 +125,8 @@
 
 bool isScalableVectorizationEnabled() { return clEnableScalableVectorization; }
 
+bool isArmStreamingForced() { return clForceArmStreaming; }
+
 unsigned getUserVscaleValue() {
   assert(clVscaleFromUser >= 1 && "Currently, vscale needs to be specified by "
                                   "the user for host-side code!");
diff --git a/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.h b/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.h
index 9ccc64c..cc8bba2 100644
--- a/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.h
+++ b/compiler/src/iree/compiler/Codegen/Utils/CPUUtils.h
@@ -39,6 +39,11 @@
 /// Returns if scalable vectorization is enabled or not.
 bool isScalableVectorizationEnabled();
 
+/// Returns whether Armv9-A streaming SVE mode is forced for dispatch regions
+/// containing scalable vector operations, via
+/// `--iree-llvmcpu-force-arm-streaming`.
+bool isArmStreamingForced();
+
 /// Returns the runtime value of vscale specified by the user. This is not meant
 /// to be used for codegen and is meant to circumvent the current limitation on
 /// host-side querying of this value at runtime. This is temporary until #21317