[rv32] Enable 32-bit mulsi_extended lowering for +zve32f (#12266)

Make sure that the 32-bit lowering is used when +zve32f is used.
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/ConvertToLLVM.cpp b/compiler/src/iree/compiler/Codegen/LLVMCPU/ConvertToLLVM.cpp
index b19f1ca..70715fe 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/ConvertToLLVM.cpp
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/ConvertToLLVM.cpp
@@ -503,11 +503,12 @@
   bool use32BitImpl = false;
   auto targetAttr = IREE::HAL::ExecutableTargetAttr::lookup(module);
   if (isRISCV(targetAttr)) {
-    // Use the 32-bit lowering for RISC-V if 'zve32x' is specified and there is
+    // Use the 32-bit lowering for RISC-V if 'zve32*' is specified and there is
     // no 64-bit integer vector support.
     // TODO(#9440) Simplify logic when 'cpu_features' is simplified.
-    use32BitImpl = hasZve32xFeature(targetAttr) && !hasVFeature(targetAttr) &&
-                   !hasZve64xFeature(targetAttr);
+    use32BitImpl =
+        (hasZve32xFeature(targetAttr) || hasZve32fFeature(targetAttr)) &&
+        !hasVFeature(targetAttr) && !hasZve64xFeature(targetAttr);
   }
   tosa::populateTosaRescaleToArithConversionPatterns(&patterns, use32BitImpl);
 
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/apply_scale_lowering.mlir b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/apply_scale_lowering.mlir
index a86a5af..627c6ed 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/apply_scale_lowering.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/apply_scale_lowering.mlir
@@ -9,7 +9,7 @@
 #executable_target_embedded_elf_riscv_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-riscv_64", {
   cpu_features = "+m,+a,+f,+d,+c",
   data_layout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
-  native_vector_size = 256 : index,
+  native_vector_size = 512 : index,
   target_triple = "riscv64-unknown-unknown-eabi-elf"
 }>
 #map = affine_map<()[s0] -> (s0 ceildiv 2)>
@@ -55,9 +55,9 @@
   ]>
 ]>
 #executable_target_embedded_elf_riscv_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-riscv_64", {
-  cpu_features = "+m,+a,+f,+d,+c,+v",
+  cpu_features = "+m,+a,+f,+d,+c,+zvl512b,+v",
   data_layout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
-  native_vector_size = 256 : index,
+  native_vector_size = 512 : index,
   target_triple = "riscv64-unknown-unknown-eabi-elf"
 }>
 #map = affine_map<()[s0] -> (s0 ceildiv 2)>
@@ -101,9 +101,9 @@
   ]>
 ]>
 #executable_target_embedded_elf_riscv_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-riscv_64", {
-  cpu_features = "+m,+a,+f,+d,+c,+zve64x",
+  cpu_features = "+m,+a,+f,+d,+c,+zvl512b,+zve64x",
   data_layout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
-  native_vector_size = 256 : index,
+  native_vector_size = 512 : index,
   target_triple = "riscv64-unknown-unknown-eabi-elf"
 }>
 #map = affine_map<()[s0] -> (s0 ceildiv 2)>
@@ -147,9 +147,9 @@
   ]>
 ]>
 #executable_target_embedded_elf_riscv_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-riscv_64", {
-  cpu_features = "+m,+a,+f,+d,+c,+zve32x",
+  cpu_features = "+m,+a,+f,+d,+c,+zvl512b,+zve32x",
   data_layout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
-  native_vector_size = 256 : index,
+  native_vector_size = 512 : index,
   target_triple = "riscv64-unknown-unknown-eabi-elf"
 }>
 #map = affine_map<()[s0] -> (s0 ceildiv 2)>
@@ -190,3 +190,56 @@
 //       CHECK:   %[[MULLOW:.*]] = llvm.mul %[[LHS]], %[[RHS]] : vector<2xi32>
 //       CHECK:   %[[SHR:.*]]    = llvm.lshr %[[MULEXT]], %{{.*}} : vector<2xi64>
 //  CHECK-NEXT:   llvm.trunc %[[SHR]] : vector<2xi64> to vector<2xi32>
+
+// -----
+
+#pipeline_layout = #hal.pipeline.layout<push_constants = 0, sets = [
+  #hal.descriptor_set.layout<0, bindings = [
+    #hal.descriptor_set.binding<0, storage_buffer>,
+    #hal.descriptor_set.binding<1, storage_buffer>
+  ]>
+]>
+#executable_target_embedded_elf_riscv_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-riscv_64", {
+  cpu_features = "+m,+a,+f,+d,+c,+zvl512b,+zve32f",
+  data_layout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
+  native_vector_size = 512 : index,
+  target_triple = "riscv64-unknown-unknown-eabi-elf"
+}>
+#map = affine_map<()[s0] -> (s0 ceildiv 2)>
+#translation = #iree_codegen.translation_info<CPUDoubleTilingExpert>
+hal.executable private @apply_scale_zve32f {
+  hal.executable.variant public @embedded_elf_riscv_64, target = #executable_target_embedded_elf_riscv_64_ {
+    hal.executable.export public @apply_scale_zve32f ordinal(0) layout(#pipeline_layout) attributes {translation_info = #translation} {
+    ^bb0(%arg0: !hal.device, %arg1: index, %arg2: index, %arg3: index):
+      %c1 = arith.constant 1 : index
+      %0 = affine.apply #map()[%arg1]
+      hal.return %0, %c1, %c1 : index, index, index
+    }
+    builtin.module {
+      func.func @apply_scale_zve32f() {
+        %cst = arith.constant dense<19689> : vector<2xi32>
+        %cst_0 = arith.constant dense<15> : vector<2xi8>
+        %c0 = arith.constant 0 : index
+        %0 = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) alignment(64) offset(%c0) : memref<2xi32>
+        %1 = hal.interface.binding.subspan set(0) binding(1) type(storage_buffer) alignment(64) offset(%c0) : memref<2xi32>
+        %2 = vector.load %0[%c0] : memref<2xi32>, vector<2xi32>
+        %3 = "tosa.apply_scale"(%2, %cst, %cst_0) {double_round = false} : (vector<2xi32>, vector<2xi32>, vector<2xi8>) -> vector<2xi32>
+        vector.store %3, %1[%c0] : memref<2xi32>, vector<2xi32>
+        return
+      }
+    }
+  }
+}
+
+// 32-bit lowering is used with '+zve32f'. Note that the 32-bit lowering
+// generates 64-bit mul operations that are decomposed into 32-bit operations by
+// the LLVM backend. The backend expects both the low half to be an `llvm.mul` op.
+// CHECK-LABEL: llvm.func @apply_scale_zve32f
+//   CHECK-DAG:   %[[RHS:.+]]    = llvm.mlir.constant(dense<19689> : vector<2xi32>) : vector<2xi32>
+//   CHECK-DAG:   %[[RHSEXT:.+]] = llvm.mlir.constant(dense<19689> : vector<2xi64>) : vector<2xi64>
+//       CHECK:   %[[LHS:.+]]    = llvm.load %{{.+}} {alignment = 4 : i64} : !llvm.ptr<vector<2xi32>>
+//       CHECK:   %[[LHSEXT:.+]] = llvm.sext %[[LHS]] : vector<2xi32> to vector<2xi64>
+//       CHECK:   %[[MULEXT:.*]] = llvm.mul %[[LHSEXT]], %[[RHSEXT]] : vector<2xi64>
+//       CHECK:   %[[MULLOW:.*]] = llvm.mul %[[LHS]], %[[RHS]] : vector<2xi32>
+//       CHECK:   %[[SHR:.*]]    = llvm.lshr %[[MULEXT]], %{{.*}} : vector<2xi64>
+//  CHECK-NEXT:   llvm.trunc %[[SHR]] : vector<2xi64> to vector<2xi32>
diff --git a/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp b/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
index 5192971..c07ce92 100644
--- a/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
+++ b/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
@@ -187,6 +187,10 @@
   return hasFeature(targetAttr, "+zve32x");
 }
 
+bool hasZve32fFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
+  return hasFeature(targetAttr, "+zve32f");
+}
+
 bool hasZve64xFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
   return hasFeature(targetAttr, "+zve64x");
 }
diff --git a/compiler/src/iree/compiler/Codegen/Utils/Utils.h b/compiler/src/iree/compiler/Codegen/Utils/Utils.h
index ca8f43f..3593354 100644
--- a/compiler/src/iree/compiler/Codegen/Utils/Utils.h
+++ b/compiler/src/iree/compiler/Codegen/Utils/Utils.h
@@ -86,6 +86,9 @@
 /// Returns true if the 'targetAttr' contains '+zve32x' in its cpu features.
 bool hasZve32xFeature(IREE::HAL::ExecutableTargetAttr targetAttr);
 
+/// Returns true if the 'targetAttr' contains '+zve32f' in its cpu features.
+bool hasZve32fFeature(IREE::HAL::ExecutableTargetAttr targetAttr);
+
 /// Returns true if the 'targetAttr' contains '+zve64x' in its cpu features.
 bool hasZve64xFeature(IREE::HAL::ExecutableTargetAttr targetAttr);