Let vm_util be C-only, split C++ part into vm_util_cc (#10563)

diff --git a/runtime/src/iree/hal/buffer_view_util.c b/runtime/src/iree/hal/buffer_view_util.c
index f6a586a..caf1110 100644
--- a/runtime/src/iree/hal/buffer_view_util.c
+++ b/runtime/src/iree/hal/buffer_view_util.c
@@ -572,3 +572,24 @@
   IREE_TRACE_ZONE_END(z0);
   return status;
 }
+
+IREE_API_EXPORT iree_status_t iree_hal_buffer_view_append_to_builder(
+    iree_hal_buffer_view_t* buffer_view, iree_host_size_t max_element_count,
+    iree_string_builder_t* builder) {
+  // NOTE: we could see how many bytes are available in the builder (capacity -
+  // size) and then pass those in to the initial format - if there's enough
+  // space it'll fill what it needs. We'd need to adjust the string builder
+  // afterward somehow.
+  iree_host_size_t required_length = 0;
+  iree_status_t status = iree_hal_buffer_view_format(
+      buffer_view, max_element_count, /*buffer_capacity=*/0, /*buffer=*/NULL,
+      &required_length);
+  if (!iree_status_is_out_of_range(status)) return status;
+  char* buffer = NULL;
+  IREE_RETURN_IF_ERROR(
+      iree_string_builder_append_inline(builder, required_length, &buffer));
+  if (!buffer) return iree_ok_status();
+  return iree_hal_buffer_view_format(buffer_view, max_element_count,
+                                     required_length + /*NUL=*/1, buffer,
+                                     &required_length);
+}
diff --git a/runtime/src/iree/hal/buffer_view_util.h b/runtime/src/iree/hal/buffer_view_util.h
index 19c0077..26f19e2 100644
--- a/runtime/src/iree/hal/buffer_view_util.h
+++ b/runtime/src/iree/hal/buffer_view_util.h
@@ -141,6 +141,11 @@
     FILE* file, const iree_hal_buffer_view_t* buffer_view,
     iree_host_size_t max_element_count, iree_allocator_t host_allocator);
 
+// Appends to |builder| a buffer view with contents without a trailing newline.
+IREE_API_EXPORT iree_status_t iree_hal_buffer_view_append_to_builder(
+    iree_hal_buffer_view_t* buffer_view, iree_host_size_t max_element_count,
+    iree_string_builder_t* builder);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/runtime/src/iree/tooling/BUILD b/runtime/src/iree/tooling/BUILD
index b4d71c6..cca6909 100644
--- a/runtime/src/iree/tooling/BUILD
+++ b/runtime/src/iree/tooling/BUILD
@@ -62,7 +62,7 @@
     srcs = ["comparison_test.cc"],
     deps = [
         ":comparison",
-        ":vm_util",
+        ":vm_util_cc",
         "//runtime/src/iree/base",
         "//runtime/src/iree/hal",
         "//runtime/src/iree/modules/hal",
@@ -136,18 +136,29 @@
 # TODO(benvanik): fold these into iree/runtime and use that instead.
 cc_library(
     name = "vm_util",
-    srcs = ["vm_util.cc"],
+    srcs = ["vm_util.c"],
     hdrs = ["vm_util.h"],
     deps = [
         ":numpy_io",
         "//runtime/src/iree/base",
-        "//runtime/src/iree/base:cc",
         "//runtime/src/iree/base:tracing",
-        "//runtime/src/iree/base/internal:span",
         "//runtime/src/iree/hal",
         "//runtime/src/iree/modules/hal",
         "//runtime/src/iree/vm",
         "//runtime/src/iree/vm:bytecode_module",
+    ],
+)
+
+cc_library(
+    name = "vm_util_cc",
+    srcs = ["vm_util_cc.cc"],
+    hdrs = ["vm_util_cc.h"],
+    deps = [
+        ":vm_util",
+        "//runtime/src/iree/base",
+        "//runtime/src/iree/base:cc",
+        "//runtime/src/iree/base:tracing",
+        "//runtime/src/iree/base/internal:span",
         "//runtime/src/iree/vm:cc",
     ],
 )
@@ -157,7 +168,7 @@
     srcs = ["vm_util_test.cc"],
     deps = [
         ":device_util",
-        ":vm_util",
+        ":vm_util_cc",
         "//runtime/src/iree/base",
         "//runtime/src/iree/hal",
         "//runtime/src/iree/modules/hal",
diff --git a/runtime/src/iree/tooling/CMakeLists.txt b/runtime/src/iree/tooling/CMakeLists.txt
index c20fade..8ed8dff 100644
--- a/runtime/src/iree/tooling/CMakeLists.txt
+++ b/runtime/src/iree/tooling/CMakeLists.txt
@@ -69,7 +69,7 @@
     "comparison_test.cc"
   DEPS
     ::comparison
-    ::vm_util
+    ::vm_util_cc
     iree::base
     iree::hal
     iree::modules::hal
@@ -156,17 +156,31 @@
   HDRS
     "vm_util.h"
   SRCS
-    "vm_util.cc"
+    "vm_util.c"
   DEPS
     ::numpy_io
     iree::base
-    iree::base::cc
-    iree::base::internal::span
     iree::base::tracing
     iree::hal
     iree::modules::hal
     iree::vm
     iree::vm::bytecode_module
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    vm_util_cc
+  HDRS
+    "vm_util_cc.h"
+  SRCS
+    "vm_util_cc.cc"
+  DEPS
+    ::vm_util
+    iree::base
+    iree::base::cc
+    iree::base::internal::span
+    iree::base::tracing
     iree::vm::cc
   PUBLIC
 )
@@ -178,7 +192,7 @@
     "vm_util_test.cc"
   DEPS
     ::device_util
-    ::vm_util
+    ::vm_util_cc
     iree::base
     iree::hal
     iree::modules::hal
diff --git a/runtime/src/iree/tooling/comparison.cc b/runtime/src/iree/tooling/comparison.cc
index 914bacc..505936f 100644
--- a/runtime/src/iree/tooling/comparison.cc
+++ b/runtime/src/iree/tooling/comparison.cc
@@ -37,28 +37,6 @@
   return equality;
 }
 
-// Prints a buffer view with contents without a trailing newline.
-static iree_status_t iree_tooling_append_buffer_view_string(
-    iree_hal_buffer_view_t* buffer_view, iree_host_size_t max_element_count,
-    iree_string_builder_t* builder) {
-  // NOTE: we could see how many bytes are available in the builder (capacity -
-  // size) and then pass those in to the initial format - if there's enough
-  // space it'll fill what it needs. We'd need to adjust the string builder
-  // afterward somehow.
-  iree_host_size_t required_length = 0;
-  iree_status_t status = iree_hal_buffer_view_format(
-      buffer_view, max_element_count, /*buffer_capacity=*/0, /*buffer=*/NULL,
-      &required_length);
-  if (!iree_status_is_out_of_range(status)) return status;
-  char* buffer = NULL;
-  IREE_RETURN_IF_ERROR(
-      iree_string_builder_append_inline(builder, required_length, &buffer));
-  if (!buffer) return iree_ok_status();
-  return iree_hal_buffer_view_format(buffer_view, max_element_count,
-                                     required_length + /*NUL=*/1, buffer,
-                                     &required_length);
-}
-
 static iree_status_t iree_vm_append_variant_type_string(
     iree_vm_variant_t variant, iree_string_builder_t* builder) {
   if (iree_vm_variant_is_empty(variant)) {
@@ -197,11 +175,11 @@
 
   IREE_CHECK_OK(
       iree_string_builder_append_string(builder, IREE_SV("\n  expected:\n")));
-  IREE_CHECK_OK(iree_tooling_append_buffer_view_string(
+  IREE_CHECK_OK(iree_hal_buffer_view_append_to_builder(
       expected_view, max_element_count, builder));
   IREE_CHECK_OK(
       iree_string_builder_append_string(builder, IREE_SV("\n  actual:\n")));
-  IREE_CHECK_OK(iree_tooling_append_buffer_view_string(
+  IREE_CHECK_OK(iree_hal_buffer_view_append_to_builder(
       actual_view, max_element_count, builder));
   IREE_CHECK_OK(iree_string_builder_append_string(builder, IREE_SV("\n")));
 
diff --git a/runtime/src/iree/tooling/comparison_test.cc b/runtime/src/iree/tooling/comparison_test.cc
index e5e7fd6..b2d2b26 100644
--- a/runtime/src/iree/tooling/comparison_test.cc
+++ b/runtime/src/iree/tooling/comparison_test.cc
@@ -11,7 +11,7 @@
 #include "iree/modules/hal/module.h"
 #include "iree/testing/gtest.h"
 #include "iree/testing/status_matchers.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/ref_cc.h"
 
diff --git a/runtime/src/iree/tooling/vm_util.c b/runtime/src/iree/tooling/vm_util.c
new file mode 100644
index 0000000..f178b35
--- /dev/null
+++ b/runtime/src/iree/tooling/vm_util.c
@@ -0,0 +1,360 @@
+// Copyright 2020 The IREE Authors
+//
+// Licensed under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "iree/tooling/vm_util.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "iree/base/api.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(
+      iree_allocator_malloc(allocator, view.size + 1, (void**)cstring));
+  memcpy(*cstring, view.data, view.size);
+  (*cstring)[view.size] = 0;
+  return iree_ok_status();
+}
+
+static iree_status_t iree_tooling_load_ndarrays_from_file(
+    iree_string_view_t file_path, iree_hal_allocator_t* device_allocator,
+    iree_vm_list_t* variant_list) {
+  char* file_path_cstring = NULL;
+  IREE_RETURN_IF_ERROR(iree_allocate_and_copy_cstring_from_view(
+      iree_allocator_system(), file_path, &file_path_cstring));
+  FILE* file = fopen(file_path_cstring, "rb");
+  iree_allocator_free(iree_allocator_system(), file_path_cstring);
+  if (!file) {
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "failed to open file '%.*s'", (int)file_path.size,
+                            file_path.data);
+  }
+
+  uint64_t file_length = iree_file_query_length(file);
+
+  iree_hal_buffer_params_t buffer_params = {};
+  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)) {
+    iree_hal_buffer_view_t* buffer_view = NULL;
+    status = iree_numpy_npy_load_ndarray(
+        file, IREE_NUMPY_NPY_LOAD_OPTION_DEFAULT, buffer_params,
+        device_allocator, &buffer_view);
+    if (iree_status_is_ok(status)) {
+      iree_vm_ref_t buffer_view_ref =
+          iree_hal_buffer_view_retain_ref(buffer_view);
+      status = iree_vm_list_push_ref_move(variant_list, &buffer_view_ref);
+    }
+    iree_hal_buffer_view_release(buffer_view);
+  }
+
+  fclose(file);
+  return status;
+}
+
+struct iree_create_buffer_from_file_generator_user_data_t {
+  FILE* file;
+};
+
+static iree_status_t iree_create_buffer_from_file_generator_callback(
+    iree_hal_buffer_mapping_t* mapping, void* user_data) {
+  struct iree_create_buffer_from_file_generator_user_data_t* read_params =
+      user_data;
+  size_t bytes_read = fread(mapping->contents.data, 1,
+                            mapping->contents.data_length, read_params->file);
+  if (bytes_read != mapping->contents.data_length) {
+    return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
+                            "file contents truncated; expected %zu bytes "
+                            "based on buffer view size",
+                            mapping->contents.data_length);
+  }
+  return iree_ok_status();
+}
+
+// Creates a HAL buffer view with the given |metadata| and reads the contents
+// from the file at |file_path|.
+//
+// The file contents are directly read in to memory with no processing.
+static iree_status_t iree_create_buffer_view_from_file(
+    iree_string_view_t metadata, iree_string_view_t file_path,
+    iree_hal_allocator_t* device_allocator,
+    iree_hal_buffer_view_t** out_buffer_view) {
+  *out_buffer_view = NULL;
+
+  // Parse shape and element type used to allocate the buffer view.
+  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
+  iree_host_size_t shape_rank = 0;
+  iree_status_t shape_result = iree_hal_parse_shape_and_element_type(
+      metadata, 0, &shape_rank, NULL, &element_type);
+  if (!iree_status_is_ok(shape_result) &&
+      !iree_status_is_out_of_range(shape_result)) {
+    return shape_result;
+  } else if (shape_rank > 128) {
+    return iree_make_status(
+        IREE_STATUS_RESOURCE_EXHAUSTED,
+        "a shape rank of %zu is just a little bit excessive, eh?", shape_rank);
+  }
+  iree_status_ignore(shape_result);
+  iree_hal_dim_t* shape =
+      (iree_hal_dim_t*)iree_alloca(shape_rank * sizeof(iree_hal_dim_t));
+  IREE_RETURN_IF_ERROR(iree_hal_parse_shape_and_element_type(
+      metadata, shape_rank, &shape_rank, shape, &element_type));
+
+  // TODO(benvanik): allow specifying the encoding.
+  iree_hal_encoding_type_t encoding_type =
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+
+  // Open the file for reading.
+  char* file_path_cstring = NULL;
+  IREE_RETURN_IF_ERROR(iree_allocate_and_copy_cstring_from_view(
+      iree_allocator_system(), file_path, &file_path_cstring));
+  FILE* file = fopen(file_path_cstring, "rb");
+  iree_allocator_free(iree_allocator_system(), file_path_cstring);
+  if (!file) {
+    return iree_make_status(iree_status_code_from_errno(errno),
+                            "failed to open file '%.*s'", (int)file_path.size,
+                            file_path.data);
+  }
+
+  iree_hal_buffer_params_t buffer_params = {0};
+  buffer_params.type = IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL;
+  buffer_params.usage = IREE_HAL_BUFFER_USAGE_DEFAULT;
+  struct iree_create_buffer_from_file_generator_user_data_t read_params = {
+      file,
+  };
+  iree_status_t status = iree_hal_buffer_view_generate_buffer(
+      device_allocator, shape_rank, shape, element_type, encoding_type,
+      buffer_params, iree_create_buffer_from_file_generator_callback,
+      &read_params, out_buffer_view);
+
+  fclose(file);
+
+  return status;
+}
+
+iree_status_t iree_create_and_parse_to_variant_list(
+    iree_hal_allocator_t* device_allocator, iree_string_view_t* input_strings,
+    iree_host_size_t input_strings_count, iree_allocator_t host_allocator,
+    iree_vm_list_t** out_list) {
+  IREE_TRACE_ZONE_BEGIN(z0);
+
+  *out_list = NULL;
+  iree_vm_list_t* variant_list = NULL;
+
+  IREE_RETURN_AND_END_ZONE_IF_ERROR(
+      z0, iree_vm_list_create(
+              /*element_type=*/NULL, input_strings_count, host_allocator,
+              &variant_list));
+  iree_status_t status = iree_ok_status();
+  for (size_t i = 0; i < input_strings_count; ++i) {
+    if (!iree_status_is_ok(status)) break;
+    iree_string_view_t input_view = iree_string_view_trim(
+        iree_make_string_view(input_strings[i].data, input_strings[i].size));
+    if (iree_string_view_consume_prefix(&input_view, IREE_SV("@"))) {
+      status = iree_tooling_load_ndarrays_from_file(
+          input_view, device_allocator, variant_list);
+      continue;
+    } else if (iree_string_view_equal(input_view, IREE_SV("(null)")) ||
+               iree_string_view_equal(input_view, IREE_SV("(ignored)"))) {
+      iree_vm_ref_t null_ref = iree_vm_ref_null();
+      status = iree_vm_list_push_ref_retain(variant_list, &null_ref);
+      continue;
+    }
+    bool has_equal =
+        iree_string_view_find_char(input_view, '=', 0) != IREE_STRING_VIEW_NPOS;
+    bool has_x =
+        iree_string_view_find_char(input_view, 'x', 0) != IREE_STRING_VIEW_NPOS;
+    if (has_equal || has_x) {
+      // Buffer view (either just a shape or a shape=value) or buffer.
+      bool is_storage_reference = iree_string_view_consume_prefix(
+          &input_view, iree_make_cstring_view("&"));
+      iree_hal_buffer_view_t* buffer_view = NULL;
+      bool has_at = iree_string_view_find_char(input_view, '@', 0) !=
+                    IREE_STRING_VIEW_NPOS;
+      if (has_at) {
+        // Referencing an external file; split into the portion used to
+        // initialize the buffer view and the file contents.
+        iree_string_view_t metadata, file_path;
+        iree_string_view_split(input_view, '@', &metadata, &file_path);
+        iree_string_view_consume_suffix(&metadata, iree_make_cstring_view("="));
+        status = iree_create_buffer_view_from_file(
+            metadata, file_path, device_allocator, &buffer_view);
+        if (!iree_status_is_ok(status)) break;
+      } else {
+        status = iree_hal_buffer_view_parse(input_view, device_allocator,
+                                            &buffer_view);
+        if (!iree_status_is_ok(status)) {
+          status =
+              iree_status_annotate_f(status, "parsing value '%.*s'",
+                                     (int)input_view.size, input_view.data);
+          break;
+        }
+      }
+      if (is_storage_reference) {
+        // Storage buffer reference; just take the storage for the buffer view -
+        // it'll still have whatever contents were specified (or 0) but we'll
+        // discard the metadata.
+        iree_vm_ref_t buffer_ref = iree_hal_buffer_retain_ref(
+            iree_hal_buffer_view_buffer(buffer_view));
+        iree_hal_buffer_view_release(buffer_view);
+        status = iree_vm_list_push_ref_move(variant_list, &buffer_ref);
+        if (!iree_status_is_ok(status)) break;
+      } else {
+        iree_vm_ref_t buffer_view_ref =
+            iree_hal_buffer_view_move_ref(buffer_view);
+        status = iree_vm_list_push_ref_move(variant_list, &buffer_view_ref);
+        if (!iree_status_is_ok(status)) break;
+      }
+    } else {
+      // Scalar.
+      bool has_dot = iree_string_view_find_char(input_view, '.', 0) !=
+                     IREE_STRING_VIEW_NPOS;
+      iree_vm_value_t val;
+      if (has_dot) {
+        // Float.
+        val = iree_vm_value_make_f32(0.0f);
+        if (!iree_string_view_atof(input_view, &val.f32)) {
+          status = iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+                                    "parsing value '%.*s' as f32",
+                                    (int)input_view.size, input_view.data);
+          break;
+        }
+      } else {
+        // Integer.
+        val = iree_vm_value_make_i32(0);
+        if (!iree_string_view_atoi_int32(input_view, &val.i32)) {
+          status = iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+                                    "parsing value '%.*s' as i32",
+                                    (int)input_view.size, input_view.data);
+          break;
+        }
+      }
+      status = iree_vm_list_push_value(variant_list, &val);
+      if (!iree_status_is_ok(status)) break;
+    }
+  }
+  if (iree_status_is_ok(status)) {
+    *out_list = variant_list;
+  } else {
+    iree_vm_list_release(variant_list);
+  }
+  IREE_TRACE_ZONE_END(z0);
+  return status;
+}
+
+#define IREE_PRINTVARIANT_CASE_I(SIZE, B, V)  \
+  case IREE_VM_VALUE_TYPE_I##SIZE:            \
+    return iree_string_builder_append_format( \
+        B, "i" #SIZE "=%" PRIi##SIZE "\n", (V).i##SIZE);
+
+#define IREE_PRINTVARIANT_CASE_F(SIZE, B, V) \
+  case IREE_VM_VALUE_TYPE_F##SIZE:           \
+    return iree_string_builder_append_format(B, "f" #SIZE "=%g\n", (V).f##SIZE);
+
+// Prints variant description including a trailing newline.
+static iree_status_t iree_print_variant(iree_vm_variant_t variant,
+                                        size_t max_element_count,
+                                        iree_string_builder_t* builder) {
+  if (iree_vm_variant_is_empty(variant)) {
+    return iree_string_builder_append_string(builder, IREE_SV("(null)\n"));
+  } else if (iree_vm_variant_is_value(variant)) {
+    switch (variant.type.value_type) {
+      IREE_PRINTVARIANT_CASE_I(8, builder, variant)
+      IREE_PRINTVARIANT_CASE_I(16, builder, variant)
+      IREE_PRINTVARIANT_CASE_I(32, builder, variant)
+      IREE_PRINTVARIANT_CASE_I(64, builder, variant)
+      IREE_PRINTVARIANT_CASE_F(32, builder, variant)
+      IREE_PRINTVARIANT_CASE_F(64, builder, variant)
+      default:
+        return iree_string_builder_append_string(builder, IREE_SV("?\n"));
+    }
+  } else if (iree_vm_variant_is_ref(variant)) {
+    iree_string_view_t type_name = iree_vm_ref_type_name(variant.type.ref_type);
+    IREE_RETURN_IF_ERROR(iree_string_builder_append_string(builder, type_name));
+    IREE_RETURN_IF_ERROR(
+        iree_string_builder_append_string(builder, IREE_SV("\n")));
+    if (iree_hal_buffer_view_isa(variant.ref)) {
+      iree_hal_buffer_view_t* buffer_view =
+          iree_hal_buffer_view_deref(variant.ref);
+      IREE_RETURN_IF_ERROR(iree_hal_buffer_view_append_to_builder(
+          buffer_view, max_element_count, builder));
+      return iree_string_builder_append_string(builder, IREE_SV("\n"));
+    } else {
+      // TODO(benvanik): a way for ref types to describe themselves.
+      return iree_string_builder_append_string(builder,
+                                               IREE_SV("(no printer)\n"));
+    }
+  } else {
+    return iree_string_builder_append_string(builder, IREE_SV("(null)\n"));
+  }
+  return iree_ok_status();
+}
+
+iree_status_t iree_append_variant_list(iree_vm_list_t* variant_list,
+                                       size_t max_element_count,
+                                       iree_string_builder_t* builder) {
+  IREE_TRACE_ZONE_BEGIN(z0);
+  for (iree_host_size_t i = 0; i < iree_vm_list_size(variant_list); ++i) {
+    iree_vm_variant_t variant = iree_vm_variant_empty();
+    IREE_RETURN_AND_END_ZONE_IF_ERROR(
+        z0, iree_vm_list_get_variant(variant_list, i, &variant),
+        "variant %zu not present", i);
+    iree_string_builder_append_format(builder, "result[%zu]: ", i);
+    IREE_RETURN_AND_END_ZONE_IF_ERROR(
+        z0, iree_print_variant(variant, max_element_count, builder));
+  }
+  IREE_TRACE_ZONE_END(z0);
+  return iree_ok_status();
+}
+
+iree_status_t iree_print_variant_list(iree_vm_list_t* variant_list,
+                                      size_t max_element_count, FILE* file) {
+  iree_string_builder_t builder;
+  iree_string_builder_initialize(iree_allocator_system(), &builder);
+  iree_status_t status =
+      iree_append_variant_list(variant_list, max_element_count, &builder);
+  if (iree_status_is_ok(status)) {
+    size_t written = fwrite(iree_string_builder_buffer(&builder), 1,
+                            iree_string_builder_size(&builder), file);
+    if (written != iree_string_builder_size(&builder)) {
+      status = iree_status_from_code(IREE_STATUS_PERMISSION_DENIED);
+    }
+  }
+  iree_string_builder_deinitialize(&builder);
+  return status;
+}
diff --git a/runtime/src/iree/tooling/vm_util.cc b/runtime/src/iree/tooling/vm_util.cc
deleted file mode 100644
index 1a7a363..0000000
--- a/runtime/src/iree/tooling/vm_util.cc
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright 2020 The IREE Authors
-//
-// Licensed under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "iree/tooling/vm_util.h"
-
-#include <cerrno>
-#include <cstdint>
-#include <cstdio>
-#include <type_traits>
-#include <vector>
-
-#include "iree/base/api.h"
-#include "iree/base/status_cc.h"
-#include "iree/base/tracing.h"
-#include "iree/hal/api.h"
-#include "iree/modules/hal/module.h"
-#include "iree/tooling/numpy_io.h"
-#include "iree/vm/ref_cc.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) {
-  fseeko(file, 0, SEEK_END);
-  uint64_t file_length = ftello(file);
-  fseeko(file, 0, SEEK_SET);
-  return file_length;
-}
-static bool iree_file_is_eof(FILE* file, uint64_t file_length) {
-  return ftello(file) == file_length;
-}
-#endif  // IREE_PLATFORM_*
-
-using namespace iree;
-
-namespace iree {
-
-static iree_status_t iree_tooling_load_ndarrays_from_file(
-    iree_string_view_t file_path, iree_hal_allocator_t* device_allocator,
-    iree_vm_list_t* variant_list) {
-  // Open the file for reading.
-  std::string file_path_str(file_path.data, file_path.size);
-  FILE* file = fopen(file_path_str.c_str(), "rb");
-  if (!file) {
-    return iree_make_status(iree_status_code_from_errno(errno),
-                            "failed to open file '%.*s'", (int)file_path.size,
-                            file_path.data);
-  }
-
-  uint64_t file_length = iree_file_query_length(file);
-
-  iree_hal_buffer_params_t buffer_params = {};
-  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)) {
-    iree_hal_buffer_view_t* buffer_view = NULL;
-    status = iree_numpy_npy_load_ndarray(
-        file, IREE_NUMPY_NPY_LOAD_OPTION_DEFAULT, buffer_params,
-        device_allocator, &buffer_view);
-    if (iree_status_is_ok(status)) {
-      auto buffer_view_ref = iree_hal_buffer_view_retain_ref(buffer_view);
-      status = iree_vm_list_push_ref_move(variant_list, &buffer_view_ref);
-    }
-    iree_hal_buffer_view_release(buffer_view);
-  }
-
-  fclose(file);
-  return status;
-}
-
-// Creates a HAL buffer view with the given |metadata| and reads the contents
-// from the file at |file_path|.
-//
-// The file contents are directly read in to memory with no processing.
-static iree_status_t CreateBufferViewFromFile(
-    iree_string_view_t metadata, iree_string_view_t file_path,
-    iree_hal_allocator_t* device_allocator,
-    iree_hal_buffer_view_t** out_buffer_view) {
-  *out_buffer_view = NULL;
-
-  // Parse shape and element type used to allocate the buffer view.
-  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
-  iree_host_size_t shape_rank = 0;
-  iree_status_t shape_result = iree_hal_parse_shape_and_element_type(
-      metadata, 0, &shape_rank, NULL, &element_type);
-  if (!iree_status_is_ok(shape_result) &&
-      !iree_status_is_out_of_range(shape_result)) {
-    return shape_result;
-  } else if (shape_rank > 128) {
-    return iree_make_status(
-        IREE_STATUS_RESOURCE_EXHAUSTED,
-        "a shape rank of %zu is just a little bit excessive, eh?", shape_rank);
-  }
-  iree_status_ignore(shape_result);
-  iree_hal_dim_t* shape =
-      (iree_hal_dim_t*)iree_alloca(shape_rank * sizeof(iree_hal_dim_t));
-  IREE_RETURN_IF_ERROR(iree_hal_parse_shape_and_element_type(
-      metadata, shape_rank, &shape_rank, shape, &element_type));
-
-  // TODO(benvanik): allow specifying the encoding.
-  iree_hal_encoding_type_t encoding_type =
-      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
-
-  // Open the file for reading.
-  std::string file_path_str(file_path.data, file_path.size);
-  FILE* file = fopen(file_path_str.c_str(), "rb");
-  if (!file) {
-    return iree_make_status(iree_status_code_from_errno(errno),
-                            "failed to open file '%.*s'", (int)file_path.size,
-                            file_path.data);
-  }
-
-  iree_hal_buffer_params_t buffer_params = {0};
-  buffer_params.type = IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL;
-  buffer_params.usage = IREE_HAL_BUFFER_USAGE_DEFAULT;
-  struct read_params_t {
-    FILE* file;
-  } read_params = {
-      file,
-  };
-  iree_status_t status = iree_hal_buffer_view_generate_buffer(
-      device_allocator, shape_rank, shape, element_type, encoding_type,
-      buffer_params,
-      +[](iree_hal_buffer_mapping_t* mapping, void* user_data) {
-        auto* read_params = reinterpret_cast<read_params_t*>(user_data);
-        size_t bytes_read =
-            fread(mapping->contents.data, 1, mapping->contents.data_length,
-                  read_params->file);
-        if (bytes_read != mapping->contents.data_length) {
-          return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
-                                  "file contents truncated; expected %zu bytes "
-                                  "based on buffer view size",
-                                  mapping->contents.data_length);
-        }
-        return iree_ok_status();
-      },
-      &read_params, out_buffer_view);
-
-  fclose(file);
-
-  return status;
-}
-
-Status ParseToVariantList(iree_hal_allocator_t* device_allocator,
-                          iree::span<const std::string> input_strings,
-                          iree_allocator_t host_allocator,
-                          iree_vm_list_t** out_list) {
-  IREE_TRACE_SCOPE();
-
-  *out_list = NULL;
-  vm::ref<iree_vm_list_t> variant_list;
-  IREE_RETURN_IF_ERROR(iree_vm_list_create(
-      /*element_type=*/nullptr, input_strings.size(), host_allocator,
-      &variant_list));
-  for (size_t i = 0; i < input_strings.size(); ++i) {
-    iree_string_view_t input_view = iree_string_view_trim(iree_make_string_view(
-        input_strings[i].data(), input_strings[i].size()));
-    if (iree_string_view_consume_prefix(&input_view, IREE_SV("@"))) {
-      IREE_RETURN_IF_ERROR(iree_tooling_load_ndarrays_from_file(
-          input_view, device_allocator, variant_list.get()));
-      continue;
-    } else if (iree_string_view_equal(input_view, IREE_SV("(null)")) ||
-               iree_string_view_equal(input_view, IREE_SV("(ignored)"))) {
-      iree_vm_ref_t null_ref = iree_vm_ref_null();
-      IREE_RETURN_IF_ERROR(
-          iree_vm_list_push_ref_retain(variant_list.get(), &null_ref));
-      continue;
-    }
-    bool has_equal =
-        iree_string_view_find_char(input_view, '=', 0) != IREE_STRING_VIEW_NPOS;
-    bool has_x =
-        iree_string_view_find_char(input_view, 'x', 0) != IREE_STRING_VIEW_NPOS;
-    if (has_equal || has_x) {
-      // Buffer view (either just a shape or a shape=value) or buffer.
-      bool is_storage_reference = iree_string_view_consume_prefix(
-          &input_view, iree_make_cstring_view("&"));
-      iree_hal_buffer_view_t* buffer_view = nullptr;
-      bool has_at = iree_string_view_find_char(input_view, '@', 0) !=
-                    IREE_STRING_VIEW_NPOS;
-      if (has_at) {
-        // Referencing an external file; split into the portion used to
-        // initialize the buffer view and the file contents.
-        iree_string_view_t metadata, file_path;
-        iree_string_view_split(input_view, '@', &metadata, &file_path);
-        iree_string_view_consume_suffix(&metadata, iree_make_cstring_view("="));
-        IREE_RETURN_IF_ERROR(CreateBufferViewFromFile(
-            metadata, file_path, device_allocator, &buffer_view));
-      } else {
-        IREE_RETURN_IF_ERROR(iree_hal_buffer_view_parse(
-                                 input_view, device_allocator, &buffer_view),
-                             "parsing value '%.*s'", (int)input_view.size,
-                             input_view.data);
-      }
-      if (is_storage_reference) {
-        // Storage buffer reference; just take the storage for the buffer view -
-        // it'll still have whatever contents were specified (or 0) but we'll
-        // discard the metadata.
-        auto buffer_ref = iree_hal_buffer_retain_ref(
-            iree_hal_buffer_view_buffer(buffer_view));
-        iree_hal_buffer_view_release(buffer_view);
-        IREE_RETURN_IF_ERROR(
-            iree_vm_list_push_ref_move(variant_list.get(), &buffer_ref));
-      } else {
-        auto buffer_view_ref = iree_hal_buffer_view_move_ref(buffer_view);
-        IREE_RETURN_IF_ERROR(
-            iree_vm_list_push_ref_move(variant_list.get(), &buffer_view_ref));
-      }
-    } else {
-      // Scalar.
-      bool has_dot = iree_string_view_find_char(input_view, '.', 0) !=
-                     IREE_STRING_VIEW_NPOS;
-      iree_vm_value_t val;
-      if (has_dot) {
-        // Float.
-        val = iree_vm_value_make_f32(0.0f);
-        if (!iree_string_view_atof(input_view, &val.f32)) {
-          return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
-                                  "parsing value '%.*s' as f32",
-                                  (int)input_view.size, input_view.data);
-        }
-      } else {
-        // Integer.
-        val = iree_vm_value_make_i32(0);
-        if (!iree_string_view_atoi_int32(input_view, &val.i32)) {
-          return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
-                                  "parsing value '%.*s' as i32",
-                                  (int)input_view.size, input_view.data);
-        }
-      }
-      IREE_RETURN_IF_ERROR(iree_vm_list_push_value(variant_list.get(), &val));
-    }
-  }
-  *out_list = variant_list.release();
-  return OkStatus();
-}
-
-// Prints a buffer view with contents without a trailing newline.
-static iree_status_t PrintBufferView(iree_hal_buffer_view_t* buffer_view,
-                                     iree_host_size_t max_element_count,
-                                     iree_string_builder_t* builder) {
-  std::string result_str(4096, '\0');
-  iree_status_t status;
-  do {
-    iree_host_size_t actual_length = 0;
-    status = iree_hal_buffer_view_format(buffer_view, max_element_count,
-                                         result_str.size() + 1, &result_str[0],
-                                         &actual_length);
-    result_str.resize(actual_length);
-  } while (iree_status_is_out_of_range(status));
-  IREE_RETURN_IF_ERROR(status);
-  return iree_string_builder_append_string(
-      builder, iree_make_string_view(result_str.data(), result_str.size()));
-}
-
-#define IREE_PRINTVARIANT_CASE_I(SIZE, B, V)  \
-  case IREE_VM_VALUE_TYPE_I##SIZE:            \
-    return iree_string_builder_append_format( \
-        B, "i" #SIZE "=%" PRIi##SIZE "\n", (V).i##SIZE);
-
-#define IREE_PRINTVARIANT_CASE_F(SIZE, B, V) \
-  case IREE_VM_VALUE_TYPE_F##SIZE:           \
-    return iree_string_builder_append_format(B, "f" #SIZE "=%g\n", (V).f##SIZE);
-
-// Prints variant description including a trailing newline.
-static Status PrintVariant(iree_vm_variant_t variant, size_t max_element_count,
-                           iree_string_builder_t* builder) {
-  if (iree_vm_variant_is_empty(variant)) {
-    return iree_string_builder_append_string(builder, IREE_SV("(null)\n"));
-  } else if (iree_vm_variant_is_value(variant)) {
-    switch (variant.type.value_type) {
-      IREE_PRINTVARIANT_CASE_I(8, builder, variant)
-      IREE_PRINTVARIANT_CASE_I(16, builder, variant)
-      IREE_PRINTVARIANT_CASE_I(32, builder, variant)
-      IREE_PRINTVARIANT_CASE_I(64, builder, variant)
-      IREE_PRINTVARIANT_CASE_F(32, builder, variant)
-      IREE_PRINTVARIANT_CASE_F(64, builder, variant)
-      default:
-        return iree_string_builder_append_string(builder, IREE_SV("?\n"));
-    }
-  } else if (iree_vm_variant_is_ref(variant)) {
-    iree_string_view_t type_name = iree_vm_ref_type_name(variant.type.ref_type);
-    IREE_RETURN_IF_ERROR(iree_string_builder_append_string(builder, type_name));
-    IREE_RETURN_IF_ERROR(
-        iree_string_builder_append_string(builder, IREE_SV("\n")));
-    if (iree_hal_buffer_view_isa(variant.ref)) {
-      auto* buffer_view = iree_hal_buffer_view_deref(variant.ref);
-      IREE_RETURN_IF_ERROR(
-          PrintBufferView(buffer_view, max_element_count, builder));
-      return iree_string_builder_append_string(builder, IREE_SV("\n"));
-    } else {
-      // TODO(benvanik): a way for ref types to describe themselves.
-      return iree_string_builder_append_string(builder,
-                                               IREE_SV("(no printer)\n"));
-    }
-  } else {
-    return iree_string_builder_append_string(builder, IREE_SV("(null)\n"));
-  }
-  return OkStatus();
-}
-
-Status PrintVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
-                        iree_string_builder_t* builder) {
-  IREE_TRACE_SCOPE();
-  for (iree_host_size_t i = 0; i < iree_vm_list_size(variant_list); ++i) {
-    iree_vm_variant_t variant = iree_vm_variant_empty();
-    IREE_RETURN_IF_ERROR(iree_vm_list_get_variant(variant_list, i, &variant),
-                         "variant %zu not present", i);
-    iree_string_builder_append_format(builder, "result[%zu]: ", i);
-    IREE_RETURN_IF_ERROR(PrintVariant(variant, max_element_count, builder));
-  }
-  return OkStatus();
-}
-
-Status PrintVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
-                        std::string* out_string) {
-  iree_string_builder_t builder;
-  iree_string_builder_initialize(iree_allocator_system(), &builder);
-  IREE_RETURN_IF_ERROR(
-      PrintVariantList(variant_list, max_element_count, &builder));
-  out_string->assign(iree_string_builder_buffer(&builder),
-                     iree_string_builder_size(&builder));
-  iree_string_builder_deinitialize(&builder);
-  return iree_ok_status();
-}
-
-Status PrintVariantList(iree_vm_list_t* variant_list,
-                        size_t max_element_count) {
-  iree_string_builder_t builder;
-  iree_string_builder_initialize(iree_allocator_system(), &builder);
-  IREE_RETURN_IF_ERROR(
-      PrintVariantList(variant_list, max_element_count, &builder));
-  printf("%.*s", (int)iree_string_builder_size(&builder),
-         iree_string_builder_buffer(&builder));
-  iree_string_builder_deinitialize(&builder);
-  return iree_ok_status();
-}
-
-}  // namespace iree
diff --git a/runtime/src/iree/tooling/vm_util.h b/runtime/src/iree/tooling/vm_util.h
index 76c9df3..728c113 100644
--- a/runtime/src/iree/tooling/vm_util.h
+++ b/runtime/src/iree/tooling/vm_util.h
@@ -7,17 +7,13 @@
 #ifndef IREE_TOOLING_VM_UTIL_H_
 #define IREE_TOOLING_VM_UTIL_H_
 
-#include <string>
-#include <vector>
-
-#include "iree/base/internal/span.h"
-#include "iree/base/status_cc.h"
-#include "iree/base/string_builder.h"
+#include "iree/base/api.h"
 #include "iree/hal/api.h"
 #include "iree/vm/api.h"
-#include "iree/vm/ref_cc.h"
 
-namespace iree {
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 // NOTE: this file is not best-practice and needs to be rewritten; consider this
 // appropriate only for test code.
@@ -30,10 +26,10 @@
 // described in iree/hal/api.h
 // Uses |device_allocator| to allocate the buffers.
 // The returned variant list must be freed by the caller.
-Status ParseToVariantList(iree_hal_allocator_t* device_allocator,
-                          iree::span<const std::string> input_strings,
-                          iree_allocator_t host_allocator,
-                          iree_vm_list_t** out_list);
+iree_status_t iree_create_and_parse_to_variant_list(
+    iree_hal_allocator_t* device_allocator, iree_string_view_t* input_strings,
+    iree_host_size_t input_strings_count, iree_allocator_t host_allocator,
+    iree_vm_list_t** out_list);
 
 // Appends a variant list of VM scalars and buffers to |builder|.
 // Prints scalars in the format:
@@ -42,25 +38,16 @@
 //   [shape]xtype=[value]
 // described in
 // https://github.com/iree-org/iree/tree/main/iree/hal/api.h
-Status AppendVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
-                         iree_string_builder_t* builder);
-inline Status AppendVariantList(iree_vm_list_t* variant_list,
-                                iree_string_builder_t* builder) {
-  return AppendVariantList(variant_list, 1024, builder);
-}
-Status PrintVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
-                        std::string* out_string);
+iree_status_t iree_append_variant_list(iree_vm_list_t* variant_list,
+                                       size_t max_element_count,
+                                       iree_string_builder_t* builder);
 
-// Prints a variant list to |out_string|.
-inline Status PrintVariantList(iree_vm_list_t* variant_list,
-                               std::string* out_string) {
-  return PrintVariantList(variant_list, 1024, out_string);
-}
+// Prints a variant list to a file.
+iree_status_t iree_print_variant_list(iree_vm_list_t* variant_list,
+                                      size_t max_element_count, FILE* file);
 
-// Prints a variant list to stdout.
-Status PrintVariantList(iree_vm_list_t* variant_list,
-                        size_t max_element_count = 1024);
-
-}  // namespace iree
+#ifdef __cplusplus
+}  // extern "C"
+#endif
 
 #endif  // IREE_TOOLING_VM_UTIL_H_
diff --git a/runtime/src/iree/tooling/vm_util_cc.cc b/runtime/src/iree/tooling/vm_util_cc.cc
new file mode 100644
index 0000000..f338b5b
--- /dev/null
+++ b/runtime/src/iree/tooling/vm_util_cc.cc
@@ -0,0 +1,41 @@
+// Copyright 2020 The IREE Authors
+//
+// Licensed under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "iree/tooling/vm_util_cc.h"
+
+#include <vector>
+
+#include "iree/vm/ref_cc.h"
+
+namespace iree {
+
+Status ParseToVariantList(iree_hal_allocator_t* device_allocator,
+                          iree::span<const std::string> input_strings,
+                          iree_allocator_t host_allocator,
+                          iree_vm_list_t** out_list) {
+  std::vector<iree_string_view_t> input_string_views(input_strings.size());
+  for (size_t i = 0; i < input_strings.size(); ++i) {
+    input_string_views[i].data = input_strings[i].data();
+    input_string_views[i].size = input_strings[i].size();
+  }
+  return iree_create_and_parse_to_variant_list(
+      device_allocator, input_string_views.data(), input_string_views.size(),
+      host_allocator, out_list);
+}
+
+Status PrintVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
+                        std::string* out_string) {
+  iree_string_builder_t builder;
+  iree_string_builder_initialize(iree_allocator_system(), &builder);
+  IREE_RETURN_IF_ERROR(
+      iree_append_variant_list(variant_list, max_element_count, &builder));
+  out_string->assign(iree_string_builder_buffer(&builder),
+                     iree_string_builder_size(&builder));
+  iree_string_builder_deinitialize(&builder);
+  return iree_ok_status();
+}
+
+}  // namespace iree
diff --git a/runtime/src/iree/tooling/vm_util_cc.h b/runtime/src/iree/tooling/vm_util_cc.h
new file mode 100644
index 0000000..d39b2c3
--- /dev/null
+++ b/runtime/src/iree/tooling/vm_util_cc.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The IREE Authors
+//
+// Licensed under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef IREE_TOOLING_VM_UTIL_CC_H_
+#define IREE_TOOLING_VM_UTIL_CC_H_
+
+#include <string>
+#include <vector>
+
+#include "iree/base/internal/span.h"
+#include "iree/base/status_cc.h"
+#include "iree/hal/api.h"
+#include "iree/tooling/vm_util.h"
+#include "iree/vm/api.h"
+
+namespace iree {
+
+// NOTE: this file is not best-practice and needs to be rewritten; consider this
+// appropriate only for test code.
+
+// Parses |input_strings| into a variant list of VM scalars and buffers.
+// Scalars should be in the format:
+//   type=value
+// Buffers should be in the IREE standard shaped buffer format:
+//   [shape]xtype=[value]
+// described in iree/hal/api.h
+// Uses |device_allocator| to allocate the buffers.
+// The returned variant list must be freed by the caller.
+Status ParseToVariantList(iree_hal_allocator_t* device_allocator,
+                          iree::span<const std::string> input_strings,
+                          iree_allocator_t host_allocator,
+                          iree_vm_list_t** out_list);
+
+// Prints a variant list to |out_string|.
+Status PrintVariantList(iree_vm_list_t* variant_list, size_t max_element_count,
+                        std::string* out_string);
+
+inline Status PrintVariantList(iree_vm_list_t* variant_list,
+                               std::string* out_string) {
+  return PrintVariantList(variant_list, 1024, out_string);
+}
+
+// Prints a variant list to stdout.
+inline Status PrintVariantList(iree_vm_list_t* variant_list,
+                               size_t max_element_count = 1024) {
+  return iree_print_variant_list(variant_list, max_element_count, stdout);
+}
+
+}  // namespace iree
+
+#endif  // IREE_TOOLING_VM_UTIL_CC_H_
diff --git a/runtime/src/iree/tooling/vm_util_test.cc b/runtime/src/iree/tooling/vm_util_test.cc
index 0992b10..794dad0 100644
--- a/runtime/src/iree/tooling/vm_util_test.cc
+++ b/runtime/src/iree/tooling/vm_util_test.cc
@@ -4,14 +4,13 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#include "iree/tooling/vm_util.h"
-
 #include "iree/base/api.h"
 #include "iree/hal/api.h"
 #include "iree/modules/hal/module.h"
 #include "iree/testing/gtest.h"
 #include "iree/testing/status_matchers.h"
 #include "iree/tooling/device_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/ref_cc.h"
 
diff --git a/tools/BUILD b/tools/BUILD
index 725cffc..aaf20b6 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -33,7 +33,7 @@
         "//runtime/src/iree/hal",
         "//runtime/src/iree/tooling:context_util",
         "//runtime/src/iree/tooling:device_util",
-        "//runtime/src/iree/tooling:vm_util",
+        "//runtime/src/iree/tooling:vm_util_cc",
         "//runtime/src/iree/vm",
         "//runtime/src/iree/vm:cc",
         "@com_google_benchmark//:benchmark",
@@ -74,7 +74,7 @@
         "//runtime/src/iree/modules/check",
         "//runtime/src/iree/testing:gtest",
         "//runtime/src/iree/tooling:context_util",
-        "//runtime/src/iree/tooling:vm_util",
+        "//runtime/src/iree/tooling:vm_util_cc",
         "//runtime/src/iree/vm",
         "//runtime/src/iree/vm:bytecode_module",
     ],
@@ -135,7 +135,7 @@
         "//runtime/src/iree/hal",
         "//runtime/src/iree/tooling:context_util",
         "//runtime/src/iree/tooling:device_util",
-        "//runtime/src/iree/tooling:vm_util",
+        "//runtime/src/iree/tooling:vm_util_cc",
         "//runtime/src/iree/vm",
         "//runtime/src/iree/vm:bytecode_module",
         "//runtime/src/iree/vm:cc",
@@ -161,7 +161,7 @@
         "//runtime/src/iree/modules/hal:types",
         "//runtime/src/iree/tooling:comparison",
         "//runtime/src/iree/tooling:context_util",
-        "//runtime/src/iree/tooling:vm_util",
+        "//runtime/src/iree/tooling:vm_util_cc",
         "//runtime/src/iree/vm",
         "//runtime/src/iree/vm:cc",
     ],
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 0a99dbf..3f8282e 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -53,7 +53,7 @@
     iree::hal
     iree::tooling::context_util
     iree::tooling::device_util
-    iree::tooling::vm_util
+    iree::tooling::vm_util_cc
     iree::vm
     iree::vm::cc
 )
@@ -95,7 +95,7 @@
     iree::modules::check
     iree::testing::gtest
     iree::tooling::context_util
-    iree::tooling::vm_util
+    iree::tooling::vm_util_cc
     iree::vm
     iree::vm::bytecode_module
   TESTONLY
@@ -129,7 +129,7 @@
     iree::modules::hal::types
     iree::tooling::comparison
     iree::tooling::context_util
-    iree::tooling::vm_util
+    iree::tooling::vm_util_cc
     iree::vm
     iree::vm::cc
 )
@@ -259,7 +259,7 @@
       iree::hal
       iree::tooling::context_util
       iree::tooling::device_util
-      iree::tooling::vm_util
+      iree::tooling::vm_util_cc
       iree::vm
       iree::vm::bytecode_module
       iree::vm::cc
diff --git a/tools/android/run_module_app/CMakeLists.txt b/tools/android/run_module_app/CMakeLists.txt
index 1391126..022ff79 100644
--- a/tools/android/run_module_app/CMakeLists.txt
+++ b/tools/android/run_module_app/CMakeLists.txt
@@ -34,7 +34,7 @@
     iree::base::cc
     iree::modules::hal
     iree::tooling::device_util
-    iree::tooling::vm_util
+    iree::tooling::vm_util_cc
     iree::vm
   LINKOPTS
     "-landroid"
diff --git a/tools/android/run_module_app/src/main.cc b/tools/android/run_module_app/src/main.cc
index f8feeee..4fb0e3b 100644
--- a/tools/android/run_module_app/src/main.cc
+++ b/tools/android/run_module_app/src/main.cc
@@ -16,9 +16,10 @@
 #include "iree/base/status_cc.h"
 #include "iree/modules/hal/module.h"
 #include "iree/tooling/device_util.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/bytecode_module.h"
+#include "iree/vm/ref_cc.h"
 
 namespace iree {
 namespace {
diff --git a/tools/iree-benchmark-module-main.cc b/tools/iree-benchmark-module-main.cc
index 73cc4c3..0ff459a 100644
--- a/tools/iree-benchmark-module-main.cc
+++ b/tools/iree-benchmark-module-main.cc
@@ -67,7 +67,7 @@
 #include "iree/base/tracing.h"
 #include "iree/hal/api.h"
 #include "iree/tooling/context_util.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/ref_cc.h"
 
diff --git a/tools/iree-check-module-main.cc b/tools/iree-check-module-main.cc
index f7bf455..333c094 100644
--- a/tools/iree-check-module-main.cc
+++ b/tools/iree-check-module-main.cc
@@ -22,7 +22,7 @@
 #include "iree/testing/gtest.h"
 #include "iree/testing/status_matchers.h"
 #include "iree/tooling/context_util.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/bytecode_module.h"
 
diff --git a/tools/iree-run-mlir-main.cc b/tools/iree-run-mlir-main.cc
index 7c0b465..3f2d9ca 100644
--- a/tools/iree-run-mlir-main.cc
+++ b/tools/iree-run-mlir-main.cc
@@ -51,7 +51,7 @@
 #include "iree/hal/api.h"
 #include "iree/tooling/context_util.h"
 #include "iree/tooling/device_util.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/bytecode_module.h"
 #include "iree/vm/ref_cc.h"
diff --git a/tools/iree-run-module-main.cc b/tools/iree-run-module-main.cc
index 884c43e..08d0687 100644
--- a/tools/iree-run-module-main.cc
+++ b/tools/iree-run-module-main.cc
@@ -20,7 +20,7 @@
 #include "iree/modules/hal/types.h"
 #include "iree/tooling/comparison.h"
 #include "iree/tooling/context_util.h"
-#include "iree/tooling/vm_util.h"
+#include "iree/tooling/vm_util_cc.h"
 #include "iree/vm/api.h"
 #include "iree/vm/ref_cc.h"