Handle large file sizes on Windows in file_io utils. (#10815)

Fixes #10807.
diff --git a/runtime/src/iree/base/internal/file_io.c b/runtime/src/iree/base/internal/file_io.c
index 1db3e5d..7bcf8bb 100644
--- a/runtime/src/iree/base/internal/file_io.c
+++ b/runtime/src/iree/base/internal/file_io.c
@@ -11,7 +11,6 @@
 #if IREE_FILE_IO_ENABLE
 
 #include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -32,6 +31,14 @@
 // be using this method.
 #define IREE_FILE_BASE_ALIGNMENT 4096
 
+#if defined(IREE_PLATFORM_WINDOWS)
+#define iree_fseek64 _fseeki64
+#define iree_ftell64 _ftelli64
+#else
+#define iree_fseek64 fseek
+#define iree_ftell64 ftell
+#endif  // IREE_PLATFORM_WINDOWS
+
 iree_status_t iree_file_exists(const char* path) {
   IREE_ASSERT_ARGUMENT(path);
   IREE_TRACE_ZONE_BEGIN(z0);
@@ -46,6 +53,38 @@
   return status;
 }
 
+iree_status_t iree_file_query_length(FILE* file, uint64_t* out_length) {
+  IREE_ASSERT_ARGUMENT(out_length);
+  *out_length = 0;
+  if (!file) return iree_ok_status();
+
+  // Capture original offset so we can return to it.
+  uint64_t origin = iree_ftell64(file);
+
+  // Seek to the end of the file.
+  if (iree_fseek64(file, 0, SEEK_END) == -1) {
+    return iree_make_status(iree_status_code_from_errno(errno), "seek (end)");
+  }
+
+  // Query the position, telling us the total file length in bytes.
+  uint64_t file_length = iree_ftell64(file);
+  if (file_length == -1L) {
+    return iree_make_status(iree_status_code_from_errno(errno), "size query");
+  }
+
+  // Seek back to the file origin.
+  if (iree_fseek64(file, origin, SEEK_SET) == -1) {
+    return iree_make_status(iree_status_code_from_errno(errno), "seek (beg)");
+  }
+
+  *out_length = file_length;
+  return iree_ok_status();
+}
+
+bool iree_file_is_at(FILE* file, uint64_t position) {
+  return iree_ftell64(file) == position;
+}
+
 iree_status_t iree_file_contents_allocator_ctl(void* self,
                                                iree_allocator_command_t command,
                                                const void* params,
@@ -84,21 +123,17 @@
 static iree_status_t iree_file_read_contents_impl(
     FILE* file, iree_allocator_t allocator,
     iree_file_contents_t** out_contents) {
-  // Seek to the end of the file.
-  if (fseek(file, 0, SEEK_END) == -1) {
-    return iree_make_status(iree_status_code_from_errno(errno), "seek (end)");
+  // Query total file length so we can preallocate storage.
+  // The file size may be larger than the buffer we can allocate (>2GB file on
+  // 32-bit devices) so we check that here early even though it may have false
+  // negatives (not enough virtual address space to allocate, etc).
+  uint64_t file_length = 0;
+  IREE_RETURN_IF_ERROR(iree_file_query_length(file, &file_length));
+  if (file_length > IREE_HOST_SIZE_MAX) {
+    return iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED,
+                            "file length exceeds host address range");
   }
-
-  // Query the position, telling us the total file length in bytes.
-  size_t file_size = ftell(file);
-  if (file_size == -1L) {
-    return iree_make_status(iree_status_code_from_errno(errno), "size query");
-  }
-
-  // Seek back to the file start.
-  if (fseek(file, 0, SEEK_SET) == -1) {
-    return iree_make_status(iree_status_code_from_errno(errno), "seek (beg)");
-  }
+  iree_host_size_t file_size = (iree_host_size_t)file_length;
 
   // Compute total size with alignment padding.
   // We allocate +1 to force a trailing \0 in case this is used as a cstring.
diff --git a/runtime/src/iree/base/internal/file_io.h b/runtime/src/iree/base/internal/file_io.h
index 3418c62..c5f5f8f 100644
--- a/runtime/src/iree/base/internal/file_io.h
+++ b/runtime/src/iree/base/internal/file_io.h
@@ -7,6 +7,9 @@
 #ifndef IREE_BASE_INTERNAL_FILE_IO_H_
 #define IREE_BASE_INTERNAL_FILE_IO_H_
 
+#include <stdint.h>
+#include <stdio.h>
+
 #include "iree/base/api.h"
 
 #ifdef __cplusplus
@@ -20,6 +23,12 @@
 // Returns IREE_STATUS_NOT_FOUND if the file does not exist.
 iree_status_t iree_file_exists(const char* path);
 
+// Returns the remaining length of |file| in bytes from the current position.
+iree_status_t iree_file_query_length(FILE* file, uint64_t* out_length);
+
+// Returns true if |file| position is at |position|.
+bool iree_file_is_at(FILE* file, uint64_t position);
+
 // Loaded file contents.
 typedef struct iree_file_contents_t {
   iree_allocator_t allocator;
diff --git a/runtime/src/iree/tooling/BUILD b/runtime/src/iree/tooling/BUILD
index cca6909..feff3dc 100644
--- a/runtime/src/iree/tooling/BUILD
+++ b/runtime/src/iree/tooling/BUILD
@@ -142,6 +142,7 @@
         ":numpy_io",
         "//runtime/src/iree/base",
         "//runtime/src/iree/base:tracing",
+        "//runtime/src/iree/base/internal:file_io",
         "//runtime/src/iree/hal",
         "//runtime/src/iree/modules/hal",
         "//runtime/src/iree/vm",
diff --git a/runtime/src/iree/tooling/CMakeLists.txt b/runtime/src/iree/tooling/CMakeLists.txt
index 8ed8dff..a91c336 100644
--- a/runtime/src/iree/tooling/CMakeLists.txt
+++ b/runtime/src/iree/tooling/CMakeLists.txt
@@ -160,6 +160,7 @@
   DEPS
     ::numpy_io
     iree::base
+    iree::base::internal::file_io
     iree::base::tracing
     iree::hal
     iree::modules::hal
diff --git a/runtime/src/iree/tooling/vm_util.c b/runtime/src/iree/tooling/vm_util.c
index 7aa0cb7..32b454b 100644
--- a/runtime/src/iree/tooling/vm_util.c
+++ b/runtime/src/iree/tooling/vm_util.c
@@ -11,34 +11,12 @@
 #include <stdio.h>
 
 #include "iree/base/api.h"
+#include "iree/base/internal/file_io.h"
 #include "iree/base/tracing.h"
 #include "iree/hal/api.h"
 #include "iree/modules/hal/module.h"
 #include "iree/tooling/numpy_io.h"
 
-// TODO(benvanik): drop use of stdio and make an iree_io_stream_t.
-#if defined(IREE_PLATFORM_WINDOWS)
-static uint64_t iree_file_query_length(FILE* file) {
-  _fseeki64(file, 0, SEEK_END);
-  uint64_t file_length = _ftelli64(file);
-  _fseeki64(file, 0, SEEK_SET);
-  return file_length;
-}
-static bool iree_file_is_eof(FILE* file, uint64_t file_length) {
-  return _ftelli64(file) == file_length;
-}
-#else
-static uint64_t iree_file_query_length(FILE* file) {
-  fseek(file, 0, SEEK_END);
-  uint64_t file_length = ftell(file);
-  fseek(file, 0, SEEK_SET);
-  return file_length;
-}
-static bool iree_file_is_eof(FILE* file, uint64_t file_length) {
-  return ftell(file) == file_length;
-}
-#endif  // IREE_PLATFORM_*
-
 static iree_status_t iree_allocate_and_copy_cstring_from_view(
     iree_allocator_t allocator, iree_string_view_t view, char** cstring) {
   IREE_RETURN_IF_ERROR(
@@ -62,15 +40,15 @@
                             file_path.data);
   }
 
-  uint64_t file_length = iree_file_query_length(file);
+  uint64_t file_length = 0;
+  iree_status_t status = iree_file_query_length(file, &file_length);
 
   iree_hal_buffer_params_t buffer_params = {0};
   buffer_params.usage = IREE_HAL_BUFFER_USAGE_DEFAULT;
   buffer_params.access = IREE_HAL_MEMORY_ACCESS_READ;
   buffer_params.type = IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL;
 
-  iree_status_t status = iree_ok_status();
-  while (iree_status_is_ok(status) && !iree_file_is_eof(file, file_length)) {
+  while (iree_status_is_ok(status) && !iree_file_is_at(file, file_length)) {
     iree_hal_buffer_view_t* buffer_view = NULL;
     status = iree_numpy_npy_load_ndarray(
         file, IREE_NUMPY_NPY_LOAD_OPTION_DEFAULT, buffer_params,