[M-146] Don't replace minimum/maximum operators with clamp when input is broadcast

When the rank of the minimum/maximum argument is greater than the input rank the broadcasting behavior is necessary for the following nodes in the graph so replacement with a single-input (non-broadcasting) clamp operator is inappropriate.

(Cherry-picked from 82367b51bf738ce41a28515b348c3434eb6d2060.)

PiperOrigin-RevId: 871408578
Bug: 483445078
Change-Id: I6855afcb77d67e425d45105755164f8c4ade7720
diff --git a/src/subgraph.c b/src/subgraph.c
index 750e2df..e5107bf 100644
--- a/src/subgraph.c
+++ b/src/subgraph.c
@@ -2866,19 +2866,32 @@
     return xnn_status_success;
   }
 
-  // Check that `arg_value` is a static scalar value.
+  // `arg_value` must be a static scalar value, but we can swap the arguments to
+  // make that true.
   struct xnn_value* input_value = &subgraph->values[node->inputs[0]];
   struct xnn_value* arg_value = &subgraph->values[node->inputs[1]];
-  if (!xnn_value_is_const(arg_value->flags) &&
-      !(xnn_shape_multiply_all_dims(&arg_value->shape) == 1 &&
-        xnn_value_is_static(arg_value->allocation_type))) {
-    if (xnn_value_is_const(input_value->flags) ||
-        (xnn_shape_multiply_all_dims(&input_value->shape) == 1 &&
-         xnn_value_is_static(input_value->allocation_type))) {
+
+  const bool input_is_static_scalar =
+      xnn_value_is_const(input_value->flags) ||
+      (xnn_shape_multiply_all_dims(&input_value->shape) == 1 &&
+       xnn_value_is_static(input_value->allocation_type));
+  const bool arg_is_static_scalar =
+      xnn_value_is_const(arg_value->flags) ||
+      (xnn_shape_multiply_all_dims(&arg_value->shape) == 1 &&
+       xnn_value_is_static(arg_value->allocation_type));
+
+  if (input_is_static_scalar) {
+    if (!arg_is_static_scalar) {
       swap_value_pointers(&arg_value, &input_value);
-    } else {
-      return xnn_status_success;
     }
+  } else if (!arg_is_static_scalar) {
+    return xnn_status_success;
+  }
+
+  if (arg_value->shape.num_dims > input_value->shape.num_dims) {
+    // The min or max operator is broadcasting the input to match the scalar's
+    // rank, so we can't replace the operator.
+    return xnn_status_success;
   }
 
   // Extract the min/max argument.
diff --git a/test/subgraph/rewrites.cc b/test/subgraph/rewrites.cc
index 6029c5f..433cd89 100644
--- a/test/subgraph/rewrites.cc
+++ b/test/subgraph/rewrites.cc
@@ -620,9 +620,9 @@
         uint32_t static_min_value_id;
         uint32_t static_max_value_id;
         std::tie(static_min_tensor, static_min_value_id) =
-            add_static_tensor<float>(rng, subgraph, {1});
+            add_static_tensor<float>(rng, subgraph, {});
         std::tie(static_max_tensor, static_max_value_id) =
-            add_static_tensor<float>(rng, subgraph, {1});
+            add_static_tensor<float>(rng, subgraph, {});
 
         // Add the binary `minimum` op.
         uint32_t min_capped_value_id =
@@ -674,9 +674,9 @@
         uint32_t static_min_value_id;
         uint32_t static_max_value_id;
         std::tie(static_min_tensor, static_min_value_id) =
-            add_static_tensor<float>(rng, subgraph, /*shape=*/{1});
+            add_static_tensor<float>(rng, subgraph, /*shape=*/{});
         std::tie(static_max_tensor, static_max_value_id) =
-            add_static_tensor<float>(rng, subgraph, /*shape=*/{1});
+            add_static_tensor<float>(rng, subgraph, /*shape=*/{});
 
         // Add the binary `maximum` op.
         uint32_t max_capped_value_id =