[WebGPU] Retain builtin buffers until submission (#25068)

The builtin fill and unaligned-copy paths destroy their parameter
buffers before submitting the recorded commands. WebGPU then rejects the
submission with `Buffer used in submit while destroyed`.

This keeps those buffers alive until submission and cleans them up on
both success and error paths. I've checked direct execution and recorded
command replay in Chrome/WebGPU.

Signed-off-by: anutosh491 <andersonbhat491@gmail.com>
diff --git a/runtime/src/iree/hal/drivers/webgpu/webgpu_imports.mjs b/runtime/src/iree/hal/drivers/webgpu/webgpu_imports.mjs
index 5ab20c7..78fa12d 100644
--- a/runtime/src/iree/hal/drivers/webgpu/webgpu_imports.mjs
+++ b/runtime/src/iree/hal/drivers/webgpu/webgpu_imports.mjs
@@ -136,6 +136,21 @@
   // |builtins|: {fillPipeline, fillBGL, copyPipeline, copyBGL}.
   function processInstructions(
       device, queue, text, buffers, offsets, builtins) {
+    const temporaryBuffers = [];
+    try {
+      processInstructionsImpl(
+          device, queue, text, buffers, offsets, builtins, temporaryBuffers);
+      return STATUS_OK;
+    } catch {
+      return STATUS_INTERNAL;
+    } finally {
+      // Submission retains these buffers until GPU work completes.
+      for (const buffer of temporaryBuffers) buffer.destroy();
+    }
+  }
+
+  function processInstructionsImpl(
+      device, queue, text, buffers, offsets, builtins, temporaryBuffers) {
     let cursor = 0;
     let encoder = null;
     const pendingCommandBuffers = [];
@@ -252,6 +267,7 @@
               size: 16,
               mappedAtCreation: true,
             });
+            temporaryBuffers.push(params);
             new Uint32Array(params.getMappedRange()).set([
               effectiveSrc, effectiveDst, length, 0
             ]);
@@ -275,7 +291,6 @@
             pass.setBindGroup(0, bindGroup);
             pass.dispatchWorkgroups(workgroups);
             pass.end();
-            params.destroy();
           }
           break;
         }
@@ -295,6 +310,7 @@
             size: 16,
             mappedAtCreation: true,
           });
+          temporaryBuffers.push(params);
           new Uint32Array(params.getMappedRange()).set([
             effectiveOffset, length, pattern, 0
           ]);
@@ -318,7 +334,6 @@
           pass.setBindGroup(0, bindGroup);
           pass.dispatchWorkgroups(workgroups);
           pass.end();
-          params.destroy();
           break;
         }
 
@@ -483,7 +498,7 @@
       }
 
       // Execute the cached instruction stream.
-      processInstructions(
+      const status = processInstructions(
           this.device, queue, this.text, this.buffers, this.offsets,
           this.builtins);
 
@@ -492,6 +507,7 @@
         this.buffers[i] = null;
         this.offsets[i] = 0;
       }
+      return status;
     }
   }
 
@@ -811,8 +827,8 @@
       // Read builtins.
       const builtins = readBuiltins(builtinsPtr);
 
-      processInstructions(device, queue, text, buffers, offsets, builtins);
-      return 0;
+      return processInstructions(
+          device, queue, text, buffers, offsets, builtins);
     },
 
     create_recording(
@@ -845,8 +861,7 @@
       const recording = handles.get(recordingHandle);
       const queue = handles.get(queueHandle);
       if (!recording || !queue) return 1;
-      recording.execute(queue, dynamicBindingTablePtr);
-      return 0;
+      return recording.execute(queue, dynamicBindingTablePtr);
     },
 
     //==========================================================================