Porting iree/base/internal/file_io.h to C. (#5699)

Moved the old std::string-based GetFileContents out to vm_util.h. Future
changes will remove the use entirely but it's not worth cleaning up the
users here.
diff --git a/iree/base/internal/BUILD b/iree/base/internal/BUILD
index 3736bf9..50b3fc3 100644
--- a/iree/base/internal/BUILD
+++ b/iree/base/internal/BUILD
@@ -114,7 +114,7 @@
 
 cc_library(
     name = "file_io",
-    srcs = ["file_io.cc"],
+    srcs = ["file_io.c"],
     hdrs = ["file_io.h"],
     deps = [
         "//iree/base",
@@ -161,6 +161,7 @@
     srcs = ["flags.c"],
     hdrs = ["flags.h"],
     deps = [
+        ":file_io",
         ":internal",
         "//iree/base",
         "//iree/base:tracing",
diff --git a/iree/base/internal/CMakeLists.txt b/iree/base/internal/CMakeLists.txt
index d01ba7c..67298a8 100644
--- a/iree/base/internal/CMakeLists.txt
+++ b/iree/base/internal/CMakeLists.txt
@@ -97,7 +97,7 @@
   HDRS
     "file_io.h"
   SRCS
-    "file_io.cc"
+    "file_io.c"
   DEPS
     iree::base
     iree::base::core_headers
@@ -149,6 +149,7 @@
   SRCS
     "flags.c"
   DEPS
+    ::file_io
     ::internal
     iree::base
     iree::base::tracing
diff --git a/iree/base/internal/file_io.c b/iree/base/internal/file_io.c
new file mode 100644
index 0000000..f44a859
--- /dev/null
+++ b/iree/base/internal/file_io.c
@@ -0,0 +1,128 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "iree/base/internal/file_io.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "iree/base/target_platform.h"
+#include "iree/base/tracing.h"
+
+iree_status_t iree_file_exists(const char* path) {
+  IREE_ASSERT_ARGUMENT(path);
+  IREE_TRACE_ZONE_BEGIN(z0);
+
+  struct stat stat_buf;
+  iree_status_t status =
+      stat(path, &stat_buf) == 0
+          ? iree_ok_status()
+          : iree_make_status(IREE_STATUS_NOT_FOUND, "'%s'", path);
+
+  IREE_TRACE_ZONE_END(z0);
+  return status;
+}
+
+static iree_status_t iree_file_read_contents_impl(
+    FILE* file, iree_allocator_t allocator, iree_byte_span_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 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)");
+  }
+
+  // Allocate +1 to force a trailing \0 in case this is a string.
+  char* contents = NULL;
+  IREE_RETURN_IF_ERROR(
+      iree_allocator_malloc(allocator, file_size + 1, (void**)&contents));
+
+  // Attempt to read the file into memory.
+  if (fread(contents, file_size, 1, file) != 1) {
+    iree_allocator_free(allocator, contents);
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "unable to read entire %zu file bytes", file_size);
+  }
+
+  // Add trailing NUL to make the contents C-string compatible.
+  contents[file_size] = 0;  // NUL
+  *out_contents = iree_make_byte_span(contents, file_size);
+  return iree_ok_status();
+}
+
+iree_status_t iree_file_read_contents(const char* path,
+                                      iree_allocator_t allocator,
+                                      iree_byte_span_t* out_contents) {
+  IREE_ASSERT_ARGUMENT(path);
+  IREE_ASSERT_ARGUMENT(out_contents);
+  IREE_TRACE_ZONE_BEGIN(z0);
+  *out_contents = iree_make_byte_span(NULL, 0);
+
+  FILE* file = fopen(path, "rb");
+  if (file == NULL) {
+    IREE_TRACE_ZONE_END(z0);
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "failed to open file '%s'", path);
+  }
+
+  // Read the file contents into memory.
+  iree_status_t status =
+      iree_file_read_contents_impl(file, allocator, out_contents);
+  if (!iree_status_is_ok(status)) {
+    status = iree_status_annotate_f(status, "reading file '%s'", path);
+  }
+
+  fclose(file);
+
+  IREE_TRACE_ZONE_END(z0);
+  return status;
+}
+
+iree_status_t iree_file_write_contents(const char* path,
+                                       iree_const_byte_span_t content) {
+  IREE_ASSERT_ARGUMENT(path);
+  IREE_TRACE_ZONE_BEGIN(z0);
+
+  FILE* file = fopen(path, "wb");
+  if (file == NULL) {
+    IREE_TRACE_ZONE_END(z0);
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "failed to open file '%s'", path);
+  }
+
+  int ret = fwrite((char*)content.data, content.data_length, 1, file);
+  iree_status_t status = iree_ok_status();
+  if (ret != 1) {
+    status =
+        iree_make_status(IREE_STATUS_DATA_LOSS,
+                         "unable to write file contents of %zu bytes to '%s'",
+                         content.data_length, path);
+  }
+
+  fclose(file);
+
+  IREE_TRACE_ZONE_END(z0);
+  return status;
+}
diff --git a/iree/base/internal/file_io.cc b/iree/base/internal/file_io.cc
deleted file mode 100644
index 288ac76..0000000
--- a/iree/base/internal/file_io.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "iree/base/internal/file_io.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "iree/base/target_platform.h"
-#include "iree/base/tracing.h"
-
-namespace iree {
-namespace file_io {
-
-iree_status_t FileExists(const char* path) {
-  IREE_TRACE_ZONE_BEGIN(z0);
-  struct stat stat_buf;
-  iree_status_t status =
-      stat(path, &stat_buf) == 0
-          ? iree_ok_status()
-          : iree_make_status(IREE_STATUS_NOT_FOUND, "'%s'", path);
-  IREE_TRACE_ZONE_END(z0);
-  return status;
-}
-
-iree_status_t GetFileContents(const char* path, std::string* out_contents) {
-  IREE_TRACE_ZONE_BEGIN(z0);
-  *out_contents = std::string();
-  FILE* file = fopen(path, "rb");
-  if (file == NULL) {
-    IREE_TRACE_ZONE_END(z0);
-    return iree_make_status(iree_status_code_from_errno(errno),
-                            "failed to open file '%s'", path);
-  }
-  iree_status_t status = iree_ok_status();
-  if (fseek(file, 0, SEEK_END) == -1) {
-    status = iree_make_status(iree_status_code_from_errno(errno), "seek (end)");
-  }
-  size_t file_size = 0;
-  if (iree_status_is_ok(status)) {
-    file_size = ftell(file);
-    if (file_size == -1L) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno), "size query");
-    }
-  }
-  if (iree_status_is_ok(status)) {
-    if (fseek(file, 0, SEEK_SET) == -1) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno), "seek (beg)");
-    }
-  }
-  std::string contents;
-  if (iree_status_is_ok(status)) {
-    contents.resize(file_size);
-    if (fread((char*)contents.data(), file_size, 1, file) != 1) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno),
-                           "unable to read entire file contents of '%s'", path);
-    }
-  }
-  if (iree_status_is_ok(status)) {
-    *out_contents = std::move(contents);
-  }
-  fclose(file);
-  IREE_TRACE_ZONE_END(z0);
-  return status;
-}
-
-iree_status_t SetFileContents(const char* path,
-                              iree_const_byte_span_t content) {
-  IREE_TRACE_ZONE_BEGIN(z0);
-  FILE* file = fopen(path, "wb");
-  if (file == NULL) {
-    IREE_TRACE_ZONE_END(z0);
-    return iree_make_status(iree_status_code_from_errno(errno),
-                            "failed to open file '%s'", path);
-  }
-  int ret = fwrite((char*)content.data, content.data_length, 1, file);
-  iree_status_t status = iree_ok_status();
-  if (ret != 1) {
-    status =
-        iree_make_status(IREE_STATUS_DATA_LOSS,
-                         "unable to write entire file contents of '%s'", path);
-  }
-  fclose(file);
-  IREE_TRACE_ZONE_END(z0);
-  return status;
-}
-
-}  // namespace file_io
-}  // namespace iree
diff --git a/iree/base/internal/file_io.h b/iree/base/internal/file_io.h
index 144204c..1e964cc 100644
--- a/iree/base/internal/file_io.h
+++ b/iree/base/internal/file_io.h
@@ -15,26 +15,35 @@
 #ifndef IREE_BASE_INTERNAL_FILE_IO_H_
 #define IREE_BASE_INTERNAL_FILE_IO_H_
 
-#include <string>
-
 #include "iree/base/api.h"
 
-namespace iree {
-namespace file_io {
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-// Checks if a file exists at the provided path.
+// Checks if a file exists at the provided |path|.
 //
-// Returns an OK status if the file definitely exists.
-// Errors can include PermissionDeniedError, NotFoundError, etc.
-iree_status_t FileExists(const char* path);
+// Returns an OK status if the file definitely exists. An OK status does not
+// indicate that attempts to read or write the file will succeed.
+// Returns IREE_STATUS_NOT_FOUND if the file does not exist.
+iree_status_t iree_file_exists(const char* path);
 
-// Synchronously reads a file's contents into a string.
-iree_status_t GetFileContents(const char* path, std::string* out_contents);
+// Synchronously reads a file's contents into memory.
+//
+// Returns the contents of the file in |out_contents|.
+// |allocator| is used to allocate the memory and the caller must use the same
+// allocator when freeing it.
+iree_status_t iree_file_read_contents(const char* path,
+                                      iree_allocator_t allocator,
+                                      iree_byte_span_t* out_contents);
 
-// Synchronously writes a string into a file, overwriting its contents.
-iree_status_t SetFileContents(const char* path, iree_const_byte_span_t content);
+// Synchronously writes a byte buffer into a file.
+// Existing contents are overwritten.
+iree_status_t iree_file_write_contents(const char* path,
+                                       iree_const_byte_span_t content);
 
-}  // namespace file_io
-}  // namespace iree
+#ifdef __cplusplus
+}  // extern "C"
+#endif
 
 #endif  // IREE_BASE_INTERNAL_FILE_IO_H_
diff --git a/iree/base/internal/file_io_test.cc b/iree/base/internal/file_io_test.cc
index e628fa9..d48c474 100644
--- a/iree/base/internal/file_io_test.cc
+++ b/iree/base/internal/file_io_test.cc
@@ -39,18 +39,34 @@
   return std::string("Test with name ") + unique_name + "\n";
 }
 
-TEST(FileIo, GetSetContents) {
-  constexpr const char* kUniqueName = "GetSetContents";
+TEST(FileIO, ReadWriteContents) {
+  constexpr const char* kUniqueName = "ReadWriteContents";
   auto path = GetUniquePath(kUniqueName);
-  ASSERT_THAT(FileExists(path.c_str()), StatusIs(StatusCode::kNotFound));
-  auto to_write = GetUniqueContents(kUniqueName);
 
-  IREE_ASSERT_OK(SetFileContents(
+  // File must not exist.
+  ASSERT_THAT(Status(iree_file_exists(path.c_str())),
+              StatusIs(StatusCode::kNotFound));
+
+  // Generate file contents.
+  auto write_contents = GetUniqueContents(kUniqueName);
+
+  // Write the contents to disk.
+  IREE_ASSERT_OK(iree_file_write_contents(
       path.c_str(),
-      iree_make_const_byte_span(to_write.data(), to_write.size())));
-  std::string read;
-  IREE_ASSERT_OK(GetFileContents(path.c_str(), &read));
-  EXPECT_EQ(to_write, read);
+      iree_make_const_byte_span(write_contents.data(), write_contents.size())));
+
+  // Read the contents from disk.
+  iree_byte_span_t read_contents;
+  IREE_ASSERT_OK(iree_file_read_contents(path.c_str(), iree_allocator_system(),
+                                         &read_contents));
+
+  // Expect the contents are equal.
+  EXPECT_EQ(write_contents.size(), read_contents.data_length);
+  EXPECT_EQ(memcmp(write_contents.data(), read_contents.data,
+                   read_contents.data_length),
+            0);
+
+  iree_allocator_free(iree_allocator_system(), read_contents.data);
 }
 
 }  // namespace
diff --git a/iree/base/internal/flags.c b/iree/base/internal/flags.c
index 0b67591..3092b63 100644
--- a/iree/base/internal/flags.c
+++ b/iree/base/internal/flags.c
@@ -21,11 +21,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#if IREE_FLAGS_ENABLE_CLI == 1
+
 #include "iree/base/internal/debugging.h"
 #include "iree/base/tracing.h"
 
-#if IREE_FLAGS_ENABLE_CLI == 1
-
 //===----------------------------------------------------------------------===//
 // Flag manipulation utilities
 //===----------------------------------------------------------------------===//
@@ -452,58 +452,7 @@
 
 #if IREE_FLAGS_ENABLE_FLAG_FILE == 1
 
-// TODO(benvanik): use this to replace file_io.cc.
-static iree_status_t iree_file_read_contents(const char* path,
-                                             iree_allocator_t allocator,
-                                             iree_byte_span_t* out_contents) {
-  IREE_TRACE_ZONE_BEGIN(z0);
-  *out_contents = iree_make_byte_span(NULL, 0);
-  FILE* file = fopen(path, "rb");
-  if (file == NULL) {
-    IREE_TRACE_ZONE_END(z0);
-    return iree_make_status(iree_status_code_from_errno(errno),
-                            "failed to open file '%s'", path);
-  }
-  iree_status_t status = iree_ok_status();
-  if (fseek(file, 0, SEEK_END) == -1) {
-    status = iree_make_status(iree_status_code_from_errno(errno), "seek (end)");
-  }
-  size_t file_size = 0;
-  if (iree_status_is_ok(status)) {
-    file_size = ftell(file);
-    if (file_size == -1L) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno), "size query");
-    }
-  }
-  if (iree_status_is_ok(status)) {
-    if (fseek(file, 0, SEEK_SET) == -1) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno), "seek (beg)");
-    }
-  }
-  // Allocate +1 to force a trailing \0 in case this is a string.
-  char* contents = NULL;
-  if (iree_status_is_ok(status)) {
-    status = iree_allocator_malloc(allocator, file_size + 1, (void**)&contents);
-  }
-  if (iree_status_is_ok(status)) {
-    if (fread(contents, file_size, 1, file) != 1) {
-      status =
-          iree_make_status(iree_status_code_from_errno(errno),
-                           "unable to read entire file contents of '%s'", path);
-    }
-  }
-  if (iree_status_is_ok(status)) {
-    contents[file_size] = 0;  // NUL
-    *out_contents = iree_make_byte_span(contents, file_size);
-  } else {
-    iree_allocator_free(allocator, contents);
-  }
-  fclose(file);
-  IREE_TRACE_ZONE_END(z0);
-  return status;
-}
+#include "iree/base/internal/file_io.h"
 
 // Parses a newline-separated list of flags from a file.
 static iree_status_t iree_flags_parse_file(iree_string_view_t file_path) {
diff --git a/iree/base/testing/dynamic_library_test.cc b/iree/base/testing/dynamic_library_test.cc
index 559950b..22db908 100644
--- a/iree/base/testing/dynamic_library_test.cc
+++ b/iree/base/testing/dynamic_library_test.cc
@@ -61,7 +61,7 @@
     library_temp_path_ = GetTempFilename(ext);
 
     const auto* file_toc = dynamic_library_test_library_create();
-    IREE_ASSERT_OK(file_io::SetFileContents(
+    IREE_ASSERT_OK(iree_file_write_contents(
         library_temp_path_.c_str(),
         iree_make_const_byte_span(file_toc->data, file_toc->size)));
 
diff --git a/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc b/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc
index 1e3f42b..08b0ce3 100644
--- a/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc
+++ b/iree/testing/vulkan/iree-run-module-vulkan-gui-main.cc
@@ -108,8 +108,7 @@
     *out_contents = std::string{std::istreambuf_iterator<char>(std::cin),
                                 std::istreambuf_iterator<char>()};
   } else {
-    IREE_RETURN_IF_ERROR(
-        file_io::GetFileContents(module_file.c_str(), out_contents));
+    IREE_RETURN_IF_ERROR(GetFileContents(module_file.c_str(), out_contents));
   }
   return OkStatus();
 }
diff --git a/iree/tools/BUILD b/iree/tools/BUILD
index 0d62597..b3bcab2 100644
--- a/iree/tools/BUILD
+++ b/iree/tools/BUILD
@@ -72,6 +72,7 @@
         "//iree/base:status",
         "//iree/base/internal:file_io",
         "//iree/schemas:bytecode_module_def_c_fbs",
+        "//iree/tools/utils:vm_util",
     ],
 )
 
diff --git a/iree/tools/CMakeLists.txt b/iree/tools/CMakeLists.txt
index 4998715..25720aa 100644
--- a/iree/tools/CMakeLists.txt
+++ b/iree/tools/CMakeLists.txt
@@ -113,6 +113,7 @@
     iree::base::internal::file_io
     iree::base::status
     iree::schemas::bytecode_module_def_c_fbs
+    iree::tools::utils::vm_util
 )
 
 iree_cc_binary(
diff --git a/iree/tools/iree-benchmark-module-main.cc b/iree/tools/iree-benchmark-module-main.cc
index f5f9611..76662c2 100644
--- a/iree/tools/iree-benchmark-module-main.cc
+++ b/iree/tools/iree-benchmark-module-main.cc
@@ -132,8 +132,7 @@
     *out_contents = std::string{std::istreambuf_iterator<char>(std::cin),
                                 std::istreambuf_iterator<char>()};
   } else {
-    IREE_RETURN_IF_ERROR(
-        file_io::GetFileContents(module_file.c_str(), out_contents));
+    IREE_RETURN_IF_ERROR(GetFileContents(module_file.c_str(), out_contents));
   }
   return OkStatus();
 }
diff --git a/iree/tools/iree-check-module-main.cc b/iree/tools/iree-check-module-main.cc
index aee13e1..71eaf45 100644
--- a/iree/tools/iree-check-module-main.cc
+++ b/iree/tools/iree-check-module-main.cc
@@ -96,7 +96,7 @@
                               std::istreambuf_iterator<char>()};
   } else {
     IREE_RETURN_IF_ERROR(
-        file_io::GetFileContents(module_file_path.c_str(), &module_data));
+        GetFileContents(module_file_path.c_str(), &module_data));
   }
 
   iree_vm_module_t* input_module = nullptr;
diff --git a/iree/tools/iree-dump-module-main.cc b/iree/tools/iree-dump-module-main.cc
index 273246a..362d523 100644
--- a/iree/tools/iree-dump-module-main.cc
+++ b/iree/tools/iree-dump-module-main.cc
@@ -19,6 +19,7 @@
 #include "iree/base/internal/file_io.h"
 #include "iree/base/status.h"
 #include "iree/schemas/bytecode_module_def_json_printer.h"
+#include "iree/tools/utils/vm_util.h"
 
 // Today we just print to JSON. We could do something more useful (size
 // analysis, etc), but JSON should be enough.
@@ -32,7 +33,7 @@
     return 1;
   }
   std::string module_contents;
-  IREE_CHECK_OK(iree::file_io::GetFileContents(argv[1], &module_contents));
+  IREE_CHECK_OK(iree::GetFileContents(argv[1], &module_contents));
 
   // Print direct to stdout.
   flatcc_json_printer_t printer;
diff --git a/iree/tools/iree-run-module-main.cc b/iree/tools/iree-run-module-main.cc
index 24d4934..8f6b53c 100644
--- a/iree/tools/iree-run-module-main.cc
+++ b/iree/tools/iree-run-module-main.cc
@@ -76,8 +76,7 @@
     *out_contents = std::string{std::istreambuf_iterator<char>(std::cin),
                                 std::istreambuf_iterator<char>()};
   } else {
-    IREE_RETURN_IF_ERROR(
-        file_io::GetFileContents(module_file.c_str(), out_contents));
+    IREE_RETURN_IF_ERROR(GetFileContents(module_file.c_str(), out_contents));
   }
   return OkStatus();
 }
diff --git a/iree/tools/utils/vm_util.cc b/iree/tools/utils/vm_util.cc
index 3bfbbc9..b43f243 100644
--- a/iree/tools/utils/vm_util.cc
+++ b/iree/tools/utils/vm_util.cc
@@ -28,6 +28,50 @@
 
 namespace iree {
 
+Status GetFileContents(const char* path, std::string* out_contents) {
+  IREE_TRACE_ZONE_BEGIN(z0);
+  *out_contents = std::string();
+  FILE* file = fopen(path, "rb");
+  if (file == NULL) {
+    IREE_TRACE_ZONE_END(z0);
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "failed to open file '%s'", path);
+  }
+  iree_status_t status = iree_ok_status();
+  if (fseek(file, 0, SEEK_END) == -1) {
+    status = iree_make_status(iree_status_code_from_errno(errno), "seek (end)");
+  }
+  size_t file_size = 0;
+  if (iree_status_is_ok(status)) {
+    file_size = ftell(file);
+    if (file_size == -1L) {
+      status =
+          iree_make_status(iree_status_code_from_errno(errno), "size query");
+    }
+  }
+  if (iree_status_is_ok(status)) {
+    if (fseek(file, 0, SEEK_SET) == -1) {
+      status =
+          iree_make_status(iree_status_code_from_errno(errno), "seek (beg)");
+    }
+  }
+  std::string contents;
+  if (iree_status_is_ok(status)) {
+    contents.resize(file_size);
+    if (fread((char*)contents.data(), file_size, 1, file) != 1) {
+      status =
+          iree_make_status(iree_status_code_from_errno(errno),
+                           "unable to read entire file contents of '%s'", path);
+    }
+  }
+  if (iree_status_is_ok(status)) {
+    *out_contents = std::move(contents);
+  }
+  fclose(file);
+  IREE_TRACE_ZONE_END(z0);
+  return status;
+}
+
 Status ValidateFunctionAbi(const iree_vm_function_t& function) {
   // Benchmark functions are always allowed through as they are () -> ().
   // That we are requiring SIP for everything in this util file is bad, and this
diff --git a/iree/tools/utils/vm_util.h b/iree/tools/utils/vm_util.h
index 006961a..2b9bdc0 100644
--- a/iree/tools/utils/vm_util.h
+++ b/iree/tools/utils/vm_util.h
@@ -30,6 +30,9 @@
 
 // TODO(benvanik) Update these when we can use RAII with the C API.
 
+// Synchronously reads a file's contents into a string.
+Status GetFileContents(const char* path, std::string* out_contents);
+
 // Validates the ABI of the specified function is supported by current tooling.
 Status ValidateFunctionAbi(const iree_vm_function_t& function);