Add c_embed_data build tool (#5540)

Add the build tool to generate c embed data for testing. The code is
used to bypass fileIO and it can be used with the C API.

Change the build rule of iree_bytecode_module to include the c embed
codegen. The user needs to assign a non-empty value in the "c_output"
field.

build_tools/embed_data/generate_embed_data_main.cc now can produce
either c code or c++ code, controlled by --c_output flag.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9958c07..92f7dfe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -218,6 +218,7 @@
 include(iree_tablegen_library)
 include(iree_tablegen_doc)
 include(iree_cc_embed_data)
+include(iree_c_embed_data)
 include(iree_bytecode_module)
 include(iree_c_module)
 include(iree_python)
diff --git a/build_tools/bazel_to_cmake/bazel_to_cmake_converter.py b/build_tools/bazel_to_cmake/bazel_to_cmake_converter.py
index 72ce425..5d86fae 100644
--- a/build_tools/bazel_to_cmake/bazel_to_cmake_converter.py
+++ b/build_tools/bazel_to_cmake/bazel_to_cmake_converter.py
@@ -447,6 +447,37 @@
                             f"{flatten_block}"
                             f"  PUBLIC\n)\n\n")
 
+  def c_embed_data(self,
+                   name,
+                   srcs,
+                   c_file_output,
+                   h_file_output,
+                   testonly=None,
+                   strip_prefix=None,
+                   flatten=None,
+                   identifier=None,
+                   **kwargs):
+    if identifier:
+      self._convert_unimplemented_function("c_embed_data",
+                                           name + " has identifier")
+    name_block = _convert_string_arg_block("NAME", name, quote=False)
+    srcs_block = _convert_srcs_block(srcs)
+    c_file_output_block = _convert_string_arg_block("C_FILE_OUTPUT",
+                                                    c_file_output)
+    h_file_output_block = _convert_string_arg_block("H_FILE_OUTPUT",
+                                                    h_file_output)
+    testonly_block = _convert_option_block("TESTONLY", testonly)
+    flatten_block = _convert_option_block("FLATTEN", flatten)
+
+    self.converter.body += (f"iree_c_embed_data(\n"
+                            f"{name_block}"
+                            f"{srcs_block}"
+                            f"{c_file_output_block}"
+                            f"{h_file_output_block}"
+                            f"{testonly_block}"
+                            f"{flatten_block}"
+                            f"  PUBLIC\n)\n\n")
+
   def spirv_kernel_cc_library(self, name, srcs):
     name_block = _convert_string_arg_block("NAME", name, quote=False)
     srcs_block = _convert_srcs_block(srcs)
@@ -462,10 +493,12 @@
                            flags=None,
                            translate_tool=None,
                            cc_namespace=None,
+                           c_output=None,
                            testonly=None):
     name_block = _convert_string_arg_block("NAME", name, quote=False)
     src_block = _convert_string_arg_block("SRC", src)
     namespace_block = _convert_string_arg_block("CC_NAMESPACE", cc_namespace)
+    c_output_block = _convert_option_block("C_OUTPUT", c_output)
     translate_tool_block = _convert_translate_tool_block(translate_tool)
     flags_block = _convert_string_list_block("FLAGS", flags)
     testonly_block = _convert_option_block("TESTONLY", testonly)
@@ -474,6 +507,7 @@
                             f"{name_block}"
                             f"{src_block}"
                             f"{namespace_block}"
+                            f"{c_output_block}"
                             f"{translate_tool_block}"
                             f"{flags_block}"
                             f"{testonly_block}"
diff --git a/build_tools/cmake/iree_bytecode_module.cmake b/build_tools/cmake/iree_bytecode_module.cmake
index 0af567a..e297083 100644
--- a/build_tools/cmake/iree_bytecode_module.cmake
+++ b/build_tools/cmake/iree_bytecode_module.cmake
@@ -26,6 +26,7 @@
 # TRANSLATE_TOOL: Translation tool to invoke (CMake target). The default
 #     tool is "iree-translate".
 # CC_NAMESPACE: Wraps everything in a C++ namespace.
+# C_OUTPUT: Control flag to generate c embed code instead.
 # PUBLIC: Add this so that this library will be exported under ${PACKAGE}::
 #     Also in IDE, target will appear in ${PACKAGE} folder while non PUBLIC
 #     will be in ${PACKAGE}/internal.
@@ -39,7 +40,7 @@
 function(iree_bytecode_module)
   cmake_parse_arguments(
     _RULE
-    "PUBLIC;TESTONLY"
+    "PUBLIC;TESTONLY;C_OUTPUT"
     "NAME;SRC;TRANSLATE_TOOL;CC_NAMESPACE"
     "FLAGS"
     ${ARGN}
@@ -104,4 +105,22 @@
       "${_TESTONLY_ARG}"
     )
   endif()
+
+  if(_RULE_C_OUTPUT)
+    iree_c_embed_data(
+      NAME
+        "${_RULE_NAME}_c"
+      IDENTIFIER
+        "${_RULE_NAME}_c"
+      GENERATED_SRCS
+        "${_RULE_NAME}.vmfb"
+      C_FILE_OUTPUT
+        "${_RULE_NAME}_c.c"
+      H_FILE_OUTPUT
+        "${_RULE_NAME}_c.h"
+      FLATTEN
+      "${_PUBLIC_ARG}"
+      "${_TESTONLY_ARG}"
+    )
+  endif()
 endfunction()
diff --git a/build_tools/cmake/iree_c_embed_data.cmake b/build_tools/cmake/iree_c_embed_data.cmake
new file mode 100644
index 0000000..0037daf
--- /dev/null
+++ b/build_tools/cmake/iree_c_embed_data.cmake
@@ -0,0 +1,97 @@
+# Copyright 2021 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(CMakeParseArguments)
+
+# iree_c_embed_data()
+#
+# CMake function to imitate Bazel's c_embed_data rule.
+#
+# Parameters:
+# NAME: Name of target (see Note).
+# SRCS: List of source files to embed.
+# GENERATED_SRCS: List of generated source files to embed.
+# C_FILE_OUTPUT: The C implementation file to output.
+# H_FILE_OUTPUT: The H header file to output.
+# STRIP_PREFIX: Strips this verbatim prefix from filenames (in the TOC).
+# FLATTEN: Removes all directory components from filenames (in the TOC).
+# IDENTIFIER: The identifier to use in generated names (defaults to name).
+# PUBLIC: Add this so that this library will be exported under ${PACKAGE}::
+# Also in IDE, target will appear in ${PACKAGE} folder while non PUBLIC will be
+# in ${PACKAGE}/internal.
+# TESTONLY: When added, this target will only be built if user passes
+#    -DIREE_BUILD_TESTS=ON to CMake.
+# TODO(scotttodd): Support passing KWARGS down into iree_cc_library?
+#
+function(iree_c_embed_data)
+  cmake_parse_arguments(
+    _RULE
+    "PUBLIC;TESTONLY;FLATTEN"
+    "NAME;IDENTIFIER;STRIP_PREFIX;C_FILE_OUTPUT;H_FILE_OUTPUT"
+    "SRCS;GENERATED_SRCS"
+    ${ARGN}
+  )
+
+  if(_RULE_TESTONLY AND NOT IREE_BUILD_TESTS)
+    return()
+  endif()
+
+  if(DEFINED _RULE_IDENTIFIER)
+    set(_IDENTIFIER ${_RULE_IDENTIFIER})
+  else()
+    set(_IDENTIFIER ${_RULE_NAME})
+  endif()
+
+  set(_ARGS)
+  list(APPEND _ARGS "--output_header=${_RULE_H_FILE_OUTPUT}")
+  list(APPEND _ARGS "--output_impl=${_RULE_C_FILE_OUTPUT}")
+  list(APPEND _ARGS "--identifier=${_IDENTIFIER}")
+  list(APPEND _ARGS "--c_output=true")
+  if(DEFINED _RULE_STRIP_PREFIX})
+    list(APPEND _ARGS "--strip_prefix=${_RULE_STRIP_PREFIX}")
+  endif()
+  if(DEFINED _RULE_FLATTEN})
+    list(APPEND _ARGS "--flatten")
+  endif()
+
+  foreach(SRC ${_RULE_SRCS})
+    list(APPEND _ARGS "${CMAKE_CURRENT_SOURCE_DIR}/${SRC}")
+  endforeach(SRC)
+  foreach(SRC ${_RULE_GENERATED_SRCS})
+    list(APPEND _ARGS "${SRC}")
+  endforeach(SRC)
+
+  iree_get_executable_path(_EXE_PATH generate_embed_data)
+
+  add_custom_command(
+    OUTPUT "${_RULE_H_FILE_OUTPUT}" "${_RULE_C_FILE_OUTPUT}"
+    COMMAND ${_EXE_PATH} ${_ARGS}
+    DEPENDS ${_EXE_PATH} ${_RULE_SRCS} ${_RULE_GENERATED_SRCS}
+  )
+
+  if(_RULE_TESTONLY)
+    set(_TESTONLY_ARG "TESTONLY")
+  endif()
+  if(_RULE_PUBLIC)
+    set(_PUBLIC_ARG "PUBLIC")
+  endif()
+
+  iree_cc_library(
+    NAME ${_RULE_NAME}
+    HDRS "${_RULE_H_FILE_OUTPUT}"
+    SRCS "${_RULE_C_FILE_OUTPUT}"
+    "${_PUBLIC_ARG}"
+    "${_TESTONLY_ARG}"
+  )
+endfunction()
diff --git a/build_tools/cmake/iree_cc_embed_data.cmake b/build_tools/cmake/iree_cc_embed_data.cmake
index 7eeac23..893c2e9 100644
--- a/build_tools/cmake/iree_cc_embed_data.cmake
+++ b/build_tools/cmake/iree_cc_embed_data.cmake
@@ -62,6 +62,7 @@
   list(APPEND _ARGS "--output_header=${_RULE_H_FILE_OUTPUT}")
   list(APPEND _ARGS "--output_impl=${_RULE_CC_FILE_OUTPUT}")
   list(APPEND _ARGS "--identifier=${_IDENTIFIER}")
+  list(APPEND _ARGS "--c_output=false")
   if(DEFINED _RULE_CPP_NAMESPACE)
     list(APPEND _ARGS "--cpp_namespace=${_RULE_CPP_NAMESPACE}")
   endif()
@@ -79,7 +80,7 @@
     list(APPEND _ARGS "${SRC}")
   endforeach(SRC)
 
-  iree_get_executable_path(_EXE_PATH generate_cc_embed_data)
+  iree_get_executable_path(_EXE_PATH generate_embed_data)
 
   add_custom_command(
     OUTPUT "${_RULE_H_FILE_OUTPUT}" "${_RULE_CC_FILE_OUTPUT}"
diff --git a/build_tools/embed_data/BUILD b/build_tools/embed_data/BUILD
index 37097f6..0ade681 100644
--- a/build_tools/embed_data/BUILD
+++ b/build_tools/embed_data/BUILD
@@ -14,7 +14,7 @@
 
 # Generates source files with embedded file contents.
 
-load(":build_defs.bzl", "cc_embed_data")
+load(":build_defs.bzl", "c_embed_data", "cc_embed_data")
 
 package(
     default_visibility = ["//visibility:public"],
@@ -23,8 +23,8 @@
 )
 
 cc_binary(
-    name = "generate_cc_embed_data",
-    srcs = ["generate_cc_embed_data_main.cc"],
+    name = "generate_embed_data",
+    srcs = ["generate_embed_data_main.cc"],
     deps = [
         "@com_google_absl//absl/flags:flag",
         "@com_google_absl//absl/flags:parse",
@@ -66,3 +66,25 @@
         "//iree/testing:gtest_main",
     ],
 )
+
+c_embed_data(
+    name = "testembed1_c",
+    # do not sort
+    srcs = [
+        "file1.txt",
+        "data/file2.txt",
+    ],
+    c_file_output = "testembed1_c.c",
+    flatten = True,
+    h_file_output = "testembed1_c.h",
+)
+
+cc_test(
+    name = "c_embed_data_test",
+    srcs = ["c_embed_data_test.cc"],
+    deps = [
+        ":testembed1_c",
+        "//iree/testing:gtest",
+        "//iree/testing:gtest_main",
+    ],
+)
diff --git a/build_tools/embed_data/CMakeLists.txt b/build_tools/embed_data/CMakeLists.txt
index 7f28f75..92ac014 100644
--- a/build_tools/embed_data/CMakeLists.txt
+++ b/build_tools/embed_data/CMakeLists.txt
@@ -13,16 +13,17 @@
 # limitations under the License.
 
 if(NOT CMAKE_CROSSCOMPILING)
-  add_executable(generate_cc_embed_data)
-  target_sources(generate_cc_embed_data PRIVATE generate_cc_embed_data_main.cc)
-  set_target_properties(generate_cc_embed_data PROPERTIES OUTPUT_NAME generate_cc_embed_data)
+  add_executable(generate_embed_data)
+  target_sources(generate_embed_data PRIVATE generate_embed_data_main.cc)
+  set_target_properties(generate_embed_data PROPERTIES OUTPUT_NAME generate_embed_data)
 
-  target_link_libraries(generate_cc_embed_data
+  target_link_libraries(generate_embed_data
     absl::flags
     absl::flags_parse
     absl::strings
   )
-  install(TARGETS generate_cc_embed_data
-          COMPONENT generate_cc_embed_data
+
+  install(TARGETS generate_embed_data
+          COMPONENT generate_embed_data
           RUNTIME DESTINATION bin)
 endif()
diff --git a/build_tools/embed_data/build_defs.bzl b/build_tools/embed_data/build_defs.bzl
index 872542a..bb16601 100644
--- a/build_tools/embed_data/build_defs.bzl
+++ b/build_tools/embed_data/build_defs.bzl
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Embeds data files into a C++ module."""
+"""Embeds data files into a C or C++ module."""
 
 def cc_embed_data(
         name,
@@ -57,7 +57,7 @@
       identifier: The identifier to use in generated names (defaults to name).
       **kwargs: Args to pass to the cc_library.
     """
-    generator = "//build_tools/embed_data:generate_cc_embed_data"
+    generator = "//build_tools/embed_data:generate_embed_data"
     generator_location = "$(location %s)" % generator
     if identifier == None:
         identifier = name
@@ -66,6 +66,7 @@
         cc_file_output,
     )
     flags += " --identifier='%s'" % (identifier,)
+    flags += " --c_output=false"
     if cpp_namespace != None:
         flags += " --cpp_namespace='%s'" % (cpp_namespace,)
     if strip_prefix != None:
@@ -91,3 +92,87 @@
         testonly = testonly,
         **kwargs
     )
+
+def c_embed_data(
+        name,
+        srcs,
+        c_file_output,
+        h_file_output,
+        testonly = False,
+        strip_prefix = None,
+        flatten = False,
+        identifier = None,
+        **kwargs):
+    """Embeds 'srcs' into a C module.
+
+    Generates a header like:
+        #if __cplusplus
+        extern "C" {
+        #endif // __cplusplus
+        struct FileToc {
+          const char* name;             // the file's original name
+          const char* data;             // beginning of the file
+          size_t size;                  // length of the file
+        };
+        #if __cplusplus
+        }
+        #endif // __cplusplus
+
+        #if __cplusplus
+        extern "C" {
+        #endif // __cplusplus
+        const struct FileToc* this_rule_name__create();
+        #if __cplusplus
+        }
+        #endif // __cplusplus
+
+    The 'this_rule_name()' function will return an array of FileToc
+    structs terminated by one that has NULL 'name' and 'data' fields.
+    The 'data' field always has an extra null terminator at the end (which
+    is not included in the size).
+
+    Args:
+      name: The rule name, which will also be the identifier of the generated
+        code symbol.
+      srcs: List of files to embed.
+      c_file_output: The C implementation file to output.
+      h_file_output: The H header file to output.
+      testonly: If True, only testonly targets can depend on this target.
+      strip_prefix: Strips this verbatim prefix from filenames (in the TOC).
+      flatten: Removes all directory components from filenames (in the TOC).
+      identifier: The identifier to use in generated names (defaults to name).
+      **kwargs: Args to pass to the cc_library.
+    """
+    generator = "//build_tools/embed_data:generate_embed_data"
+    generator_location = "$(location %s)" % generator
+    if identifier == None:
+        identifier = name
+    flags = "--output_header='$(location %s)' --output_impl='$(location %s)'" % (
+        h_file_output,
+        c_file_output,
+    )
+    flags += " --c_output=true"
+    flags += " --identifier='%s'" % (identifier,)
+    if strip_prefix != None:
+        flags += " --strip_prefix='%s'" % (strip_prefix,)
+    if flatten:
+        flags += " --flatten"
+
+    native.genrule(
+        name = name + "__generator",
+        srcs = srcs,
+        outs = [
+            c_file_output,
+            h_file_output,
+        ],
+        tools = [generator],
+        cmd = "%s $(SRCS) %s" % (generator_location, flags),
+        testonly = testonly,
+    )
+    native.cc_library(
+        name = name,
+        hdrs = [h_file_output],
+        srcs = [c_file_output],
+        testonly = testonly,
+        **kwargs
+    )
diff --git a/build_tools/embed_data/c_embed_data_test.cc b/build_tools/embed_data/c_embed_data_test.cc
new file mode 100644
index 0000000..a112907
--- /dev/null
+++ b/build_tools/embed_data/c_embed_data_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 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 "build_tools/embed_data/testembed1_c.h"
+#include "iree/testing/gtest.h"
+
+namespace {
+
+TEST(Generator, TestContents) {
+  auto* toc1 = testembed1_c_create();
+  ASSERT_EQ("file1.txt", std::string(toc1->name));
+  ASSERT_EQ(R"(Are you '"Still"' here?)"
+            "\n",
+            std::string(toc1->data));
+  ASSERT_EQ(24, toc1->size);
+  ASSERT_EQ(0, *(toc1->data + toc1->size));
+
+  ++toc1;
+  ASSERT_EQ("file2.txt", std::string(toc1->name));
+  ASSERT_EQ(R"(¯\_(ツ)_/¯)"
+            "\n",
+            std::string(toc1->data));
+  ASSERT_EQ(14, toc1->size);
+  ASSERT_EQ(0, *(toc1->data + toc1->size));
+
+  ++toc1;
+  ASSERT_EQ(nullptr, toc1->name);
+  ASSERT_EQ(nullptr, toc1->data);
+}
+
+}  // namespace
diff --git a/build_tools/embed_data/generate_cc_embed_data_main.cc b/build_tools/embed_data/generate_embed_data_main.cc
similarity index 68%
rename from build_tools/embed_data/generate_cc_embed_data_main.cc
rename to build_tools/embed_data/generate_embed_data_main.cc
index 0ded0e6..137479c 100644
--- a/build_tools/embed_data/generate_cc_embed_data_main.cc
+++ b/build_tools/embed_data/generate_embed_data_main.cc
@@ -25,12 +25,25 @@
 ABSL_FLAG(std::string, identifier, "resources",
           "name of the resources function");
 ABSL_FLAG(std::string, output_header, "", "output header file");
-ABSL_FLAG(std::string, output_impl, "", "output cc impl file");
+ABSL_FLAG(std::string, output_impl, "", "output impl file");
 ABSL_FLAG(std::string, cpp_namespace, "", "generate in a c++ namespace");
+ABSL_FLAG(bool, c_output, false, "generate a c output");
 ABSL_FLAG(std::string, strip_prefix, "", "strip prefix from filenames");
 ABSL_FLAG(bool, flatten, false,
           "whether to flatten the directory structure (only include basename)");
 
+void GenerateExternCOpen(std::ofstream& f) {
+  f << "\n#if __cplusplus\n";
+  f << "extern \"C\" {\n";
+  f << "#endif // __cplusplus\n";
+}
+
+void GenerateExternCClose(std::ofstream& f) {
+  f << "#if __cplusplus\n";
+  f << "}\n";
+  f << "#endif // __cplusplus\n\n";
+}
+
 void GenerateNamespaceOpen(std::ofstream& f) {
   const auto& ns = absl::GetFlag(FLAGS_cpp_namespace);
   if (ns.empty()) return;
@@ -54,32 +67,58 @@
 }
 
 void GenerateTocStruct(std::ofstream& f) {
+  const auto& c_output = absl::GetFlag(FLAGS_c_output);
   f << "#ifndef IREE_FILE_TOC\n";
   f << "#define IREE_FILE_TOC\n";
-  f << "namespace iree {\n";
+  if (c_output) {
+    GenerateExternCOpen(f);
+  } else {
+    f << "namespace iree {\n";
+  }
   f << "struct FileToc {\n";
   f << "  const char* name;             // the file's original name\n";
   f << "  const char* data;             // beginning of the file\n";
-  f << "  std::size_t size;             // length of the file\n";
-  f << "};\n";
-  f << "}  // namespace iree\n";
+  if (c_output) {
+    f << "  size_t size;                  // length of the file\n";
+    f << "};\n";
+    GenerateExternCClose(f);
+  } else {
+    f << "  std::size_t size;             // length of the file\n";
+    f << "};\n";
+    f << "}  // namespace iree\n";
+  }
   f << "#endif  // IREE_FILE_TOC\n";
 }
 
 bool GenerateHeader(const std::string& header_file,
                     const std::vector<std::string>& toc_files) {
   std::ofstream f(header_file, std::ios::out | std::ios::trunc);
+  const auto& c_output = absl::GetFlag(FLAGS_c_output);
+
   f << "#pragma once\n";  // Pragma once isn't great but is the best we can do.
-  f << "#include <cstddef>\n";
-  GenerateTocStruct(f);
-  GenerateNamespaceOpen(f);
-  f << "extern const struct ::iree::FileToc* "
-    << absl::GetFlag(FLAGS_identifier) << "_create();\n";
-  f << "static inline std::size_t " << absl::GetFlag(FLAGS_identifier)
-    << "_size() { \n";
-  f << "  return " << toc_files.size() << ";\n";
-  f << "}\n";
-  GenerateNamespaceClose(f);
+  if (c_output) {
+    f << "#include <stddef.h>\n";
+    GenerateTocStruct(f);
+    GenerateExternCOpen(f);
+    f << "const struct FileToc* " << absl::GetFlag(FLAGS_identifier)
+      << "_create();\n";
+    f << "static inline size_t " << absl::GetFlag(FLAGS_identifier)
+      << "_size() {\n";
+    f << "  return " << toc_files.size() << ";\n";
+    f << "}\n";
+    GenerateExternCClose(f);
+  } else {
+    f << "#include <cstddef>\n";
+    GenerateTocStruct(f);
+    GenerateNamespaceOpen(f);
+    f << "extern const struct ::iree::FileToc* "
+      << absl::GetFlag(FLAGS_identifier) << "_create();\n";
+    f << "static inline std::size_t " << absl::GetFlag(FLAGS_identifier)
+      << "_size() { \n";
+    f << "  return " << toc_files.size() << ";\n";
+    f << "}\n";
+    GenerateNamespaceClose(f);
+  }
   f.close();
   return f.good();
 }
@@ -109,9 +148,16 @@
                   const std::vector<std::string>& input_files,
                   const std::vector<std::string>& toc_files) {
   std::ofstream f(impl_file, std::ios::out | std::ios::trunc);
-  f << "#include <cstddef>\n";
-  GenerateTocStruct(f);
-  GenerateNamespaceOpen(f);
+  const auto& c_output = absl::GetFlag(FLAGS_c_output);
+  if (c_output) {
+    f << "#include <stddef.h>\n";
+    f << "#include <stdalign.h>\n";
+    GenerateTocStruct(f);
+  } else {
+    f << "#include <cstddef>\n";
+    GenerateTocStruct(f);
+    GenerateNamespaceOpen(f);
+  }
   for (size_t i = 0, e = input_files.size(); i < e; ++i) {
     f << "alignas(alignof(void*)) static char const file_" << i << "[] = {\n";
     std::string contents;
@@ -128,7 +174,11 @@
     }
     f << "};\n";
   }
-  f << "static const struct ::iree::FileToc toc[] = {\n";
+  if (c_output) {
+    f << "static const struct FileToc toc[] = {\n";
+  } else {
+    f << "static const struct ::iree::FileToc toc[] = {\n";
+  }
   assert(input_files.size() == toc_files.size());
   for (size_t i = 0, e = input_files.size(); i < e; ++i) {
     f << "  {\n";
@@ -137,14 +187,22 @@
     f << "    sizeof(file_" << i << ") - 1\n";
     f << "  },\n";
   }
-  f << "  {nullptr, nullptr, 0},\n";
-  f << "};\n";
-  f << "const struct ::iree::FileToc* " << absl::GetFlag(FLAGS_identifier)
-    << "_create() {\n";
+  if (c_output) {
+    f << "  {NULL, NULL, 0},\n";
+    f << "};\n";
+    f << "const struct FileToc* " << absl::GetFlag(FLAGS_identifier)
+      << "_create() {\n";
+  } else {
+    f << "  {nullptr, nullptr, 0},\n";
+    f << "};\n";
+    f << "const struct ::iree::FileToc* " << absl::GetFlag(FLAGS_identifier)
+      << "_create() {\n";
+  }
   f << "  return &toc[0];\n";
   f << "}\n";
-
-  GenerateNamespaceClose(f);
+  if (!c_output) {
+    GenerateNamespaceClose(f);
+  }
   f.close();
   return f.good();
 }
@@ -175,7 +233,12 @@
     }
     toc_files.push_back(toc_file);
   }
-
+  // Can either generate the c or c++ output.
+  if (!absl::GetFlag(FLAGS_cpp_namespace).empty() &&
+      absl::GetFlag(FLAGS_c_output)) {
+    std::cerr << "Can only generate either c or c++ output.\n";
+    return 1;
+  }
   if (!absl::GetFlag(FLAGS_output_header).empty()) {
     if (!GenerateHeader(absl::GetFlag(FLAGS_output_header), toc_files)) {
       std::cerr << "Error generating headers.\n";
diff --git a/iree/tools/compilation.bzl b/iree/tools/compilation.bzl
index b3a85be..159df41 100644
--- a/iree/tools/compilation.bzl
+++ b/iree/tools/compilation.bzl
@@ -14,7 +14,7 @@
 
 """Rules for compiling IREE executables, modules, and archives."""
 
-load("//build_tools/embed_data:build_defs.bzl", "cc_embed_data")
+load("//build_tools/embed_data:build_defs.bzl", "c_embed_data", "cc_embed_data")
 
 # TODO(benvanik): port to a full starlark rule, document, etc.
 def iree_bytecode_module(
@@ -23,6 +23,7 @@
         flags = ["-iree-mlir-to-vm-bytecode-module"],
         translate_tool = "//iree/tools:iree-translate",
         cc_namespace = None,
+        c_output = False,
         **kwargs):
     native.genrule(
         name = name,
@@ -57,3 +58,14 @@
             flatten = True,
             **kwargs
         )
+    # Embed the module for use in C.
+    if c_output:
+        c_embed_data(
+            name = "%s_c" % (name),
+            identifier = "%s_c" % (name),
+            srcs = ["%s.vmfb" % (name)],
+            c_file_output = "%s_c.c" % (name),
+            h_file_output = "%s_c.h" % (name),
+            flatten = True,
+            **kwargs
+        )