[Vulkan] Accept foreign semaphores in queue operations (#25010)

Queue operations rejected any semaphore not created by the same Vulkan
device with UNIMPLEMENTED. Use host deferral instead.

---------

Signed-off-by: Tobias Fuchs <fuchs@roofline.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/runtime/src/iree/hal/drivers/vulkan/queue.c b/runtime/src/iree/hal/drivers/vulkan/queue.c
index 2df43da..268df93 100644
--- a/runtime/src/iree/hal/drivers/vulkan/queue.c
+++ b/runtime/src/iree/hal/drivers/vulkan/queue.c
@@ -2889,9 +2889,11 @@
   return iree_ok_status();
 }
 
+// Validates a queue submission semaphore list
+// Foreign semaphores are explicitly allowed
+// see resolve_waits and publish_signals for how they are handled
 static iree_status_t iree_hal_vulkan_queue_validate_semaphore_list(
-    iree_hal_vulkan_queue_t* queue, iree_hal_semaphore_list_t semaphore_list,
-    iree_string_view_t usage) {
+    iree_hal_semaphore_list_t semaphore_list, iree_string_view_t usage) {
   if (semaphore_list.count != 0 &&
       (!semaphore_list.semaphores || !semaphore_list.payload_values)) {
     return iree_make_status(
@@ -2901,13 +2903,6 @@
         (int)usage.size, usage.data, semaphore_list.count);
   }
   for (iree_host_size_t i = 0; i < semaphore_list.count; ++i) {
-    iree_hal_semaphore_t* semaphore = semaphore_list.semaphores[i];
-    if (!iree_hal_vulkan_semaphore_is_local(semaphore, queue->device)) {
-      return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
-                              "Vulkan queue %.*s semaphore %" PRIhsz
-                              " is not a local Vulkan semaphore",
-                              (int)usage.size, usage.data, i);
-    }
     if (semaphore_list.payload_values[i] > IREE_HAL_SEMAPHORE_MAX_VALUE) {
       return iree_make_status(
           IREE_STATUS_OUT_OF_RANGE,
@@ -4602,6 +4597,19 @@
     IREE_RETURN_IF_ERROR(iree_hal_semaphore_query(semaphore, &current_value));
     if (current_value >= minimum_value) continue;
 
+    // For foreign semaphores the submission must be held until the host
+    // observes the signal
+    if (!iree_hal_vulkan_semaphore_is_local(semaphore, queue->device)) {
+      if (allow_software_deferral) {
+        resolution->needs_deferral = true;
+        return iree_ok_status();
+      }
+      return iree_make_status(IREE_STATUS_FAILED_PRECONDITION,
+                              "Vulkan queue wait on foreign semaphore %" PRIhsz
+                              " requires software deferral",
+                              i);
+    }
+
     iree_hal_vulkan_last_signal_flags_t signal_flags = 0;
     iree_async_axis_t producer_axis = 0;
     uint64_t producer_epoch = 0;
@@ -4675,9 +4683,15 @@
       iree_async_fixed_frontier_as_const_frontier(&submission->frontier);
   for (iree_host_size_t i = 0; i < submission->signal_semaphore_list.count;
        ++i) {
-    iree_hal_vulkan_semaphore_publish_signal(
-        submission->signal_semaphore_list.semaphores[i], queue->axis, frontier,
-        submission->epoch, submission->signal_semaphore_list.payload_values[i]);
+    // Update last-signal cache of local semaphores
+    // Foreign semaphores observe the host-side signal at completion
+    iree_hal_semaphore_t* semaphore =
+        submission->signal_semaphore_list.semaphores[i];
+    if (iree_hal_vulkan_semaphore_is_local(semaphore, queue->device)) {
+      iree_hal_vulkan_semaphore_publish_signal(
+          semaphore, queue->axis, frontier, submission->epoch,
+          submission->signal_semaphore_list.payload_values[i]);
+    }
   }
 }
 
@@ -6484,10 +6498,10 @@
   IREE_TRACE_ZONE_BEGIN(z0);
 
   iree_status_t status = iree_hal_vulkan_queue_validate_semaphore_list(
-      queue, wait_semaphore_list, IREE_SV("wait"));
+      wait_semaphore_list, IREE_SV("wait"));
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_hal_vulkan_queue_pending_submission_t* submission = NULL;
@@ -6596,12 +6610,12 @@
     }
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_hal_buffer_t* buffer = NULL;
@@ -6691,10 +6705,10 @@
   }
 
   iree_status_t status = iree_hal_vulkan_queue_validate_semaphore_list(
-      queue, wait_semaphore_list, IREE_SV("wait"));
+      wait_semaphore_list, IREE_SV("wait"));
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_hal_vulkan_queue_pending_submission_t* submission = NULL;
@@ -6764,12 +6778,12 @@
                               "too many Vulkan sparse buffer binds");
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   VkSparseMemoryBind* copied_binds = NULL;
@@ -6859,12 +6873,12 @@
         IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE);
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_hal_vulkan_queue_pending_submission_t* submission = NULL;
@@ -6956,12 +6970,12 @@
         IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE);
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_hal_vulkan_queue_pending_submission_t* submission = NULL;
@@ -7855,12 +7869,12 @@
         IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE);
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
   if (iree_status_is_ok(status) && length != 0) {
     status = iree_hal_vulkan_queue_validate_recordable_backing(
@@ -7970,12 +7984,12 @@
         source_file, source_offset, length, IREE_SV("read"));
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
   if (iree_status_is_ok(status) && length == 0) {
     IREE_TRACE_ZONE_END(z0);
@@ -8064,12 +8078,12 @@
         target_file, target_offset, length, IREE_SV("write"));
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
   if (iree_status_is_ok(status) && length == 0) {
     IREE_TRACE_ZONE_END(z0);
@@ -8974,12 +8988,12 @@
         &config.workgroup_count_ref);
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
 
   iree_host_size_t dispatch_payload_storage_length = 0;
@@ -9171,12 +9185,12 @@
         "Vulkan queue execute requires a concrete profile event type");
   }
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
   if (iree_status_is_ok(status) &&
       binding_table.count < command_buffer->binding_count) {
@@ -9290,12 +9304,12 @@
   iree_status_t status =
       iree_hal_vulkan_queue_validate_host_call(call, args, flags);
   if (iree_status_is_ok(status)) {
-    status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, wait_semaphore_list, IREE_SV("wait"));
+    status = iree_hal_vulkan_queue_validate_semaphore_list(wait_semaphore_list,
+                                                           IREE_SV("wait"));
   }
   if (iree_status_is_ok(status)) {
     status = iree_hal_vulkan_queue_validate_semaphore_list(
-        queue, signal_semaphore_list, IREE_SV("signal"));
+        signal_semaphore_list, IREE_SV("signal"));
   }
   if (iree_status_is_ok(status) && wait_semaphore_list.count > UINT32_MAX) {
     status = iree_make_status(IREE_STATUS_OUT_OF_RANGE,