Add support for emitc static library
This change is for adding support for emitc static library.
Status:
- simple_vec_mul works properly.
- For more complicated float/quant models, C code and static library can be succesufully generated. However, there is compilation error when building the final executable: undefined reference to `call_0riirii_r_import' and `call_0rriCiD_v_import", caused by upstream bug (https://github.com/google/iree/issues/7842). The targets are thus not being built at this moment.
Change-Id: Iacfd0c5c0d7c494ae6f870cef2fac0d36f8fbf49
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cb26763..fd8264a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -86,6 +86,8 @@
include(${IREE_SOURCE_DIR}/build_tools/cmake/iree_copts.cmake)
include(springbok_bytecode_module)
+include(springbok_c_module)
+include(springbok_modules)
include(iree_model_input)
# Add the included directory here.
add_subdirectory(samples)
diff --git a/cmake/springbok_c_module.cmake b/cmake/springbok_c_module.cmake
new file mode 100644
index 0000000..929f4ad
--- /dev/null
+++ b/cmake/springbok_c_module.cmake
@@ -0,0 +1,136 @@
+include(CMakeParseArguments)
+
+# springbok_c_module()
+#
+# A wrapper for the iree_bytecode_module to apply common iree-translate flags
+# Parameters:
+# NAME: Name of target.
+# SRC: Source file to compile into an emitC module. Support relative path.
+# FLAGS: Flags to pass to the translation tool (list of strings).
+# C_IDENTIFIER: Identifier to use for generate c embed code.
+# If omitted then no C embed code will be generated.
+#
+# Examples:
+# springbok_c_module(
+# NAME
+# dare_devel_c_module_emitc_static
+# SRC
+# "daredevil_quant.tflite"
+# FLAGS
+# "-iree-input-type=tosa"
+# )
+#
+# springbok_c_module(
+# NAME
+# simple_float_mul_c_module_emitc_static
+# SRC
+# "simple_float_mul.mlir"
+# FLAGS
+# "-iree-input-type=mhlo"
+# PUBLIC
+# )
+#
+function(springbok_c_module)
+ cmake_parse_arguments(
+ _RULE
+ "PUBLIC"
+ "NAME;SRC"
+ "FLAGS"
+ ${ARGN}
+ )
+
+ set(_MLIR_SRC "${_RULE_SRC}")
+ string(FIND "${_RULE_SRC}" ".tflite" _IS_TFLITE REVERSE)
+ if(${_IS_TFLITE} GREATER 0)
+ find_program(IREE_IMPORT_TFLITE_TOOL "iree-import-tflite" REQUIRED)
+ set(_MLIR_SRC "${CMAKE_CURRENT_BINARY_DIR}/${_RULE_NAME}.mlir")
+ get_filename_component(_SRC_PATH "${_RULE_SRC}" REALPATH)
+ set(_ARGS "${_SRC_PATH}")
+ list(APPEND _ARGS "-o")
+ list(APPEND _ARGS "${_RULE_NAME}.mlir")
+ # Only add the custom_command here. The output is passed to
+ # iree_bytecode_module as the source.
+ add_custom_command(
+ OUTPUT
+ "${_RULE_NAME}.mlir"
+ COMMAND
+ ${IREE_IMPORT_TFLITE_TOOL}
+ ${_ARGS}
+ DEPENDS
+ ${IREE_IMPORT_TFLITE_TOOL}
+ )
+ endif()
+
+ get_filename_component(_MLIR_SRC "${_MLIR_SRC}" REALPATH)
+ iree_get_executable_path(_TRANSLATE_TOOL_EXECUTABLE "iree-translate")
+ iree_get_executable_path(_LINKER_TOOL_EXECUTABLE "lld")
+
+ # Replace dependencies passed by ::name with iree::package::name
+ iree_package_ns(_PACKAGE_NS)
+ list(TRANSFORM _RULE_DEPS REPLACE "^::" "${_PACKAGE_NS}::")
+
+ # Prefix the library with the package name, so we get: iree_package_name.
+ iree_package_name(_PACKAGE_NAME)
+
+ set(_RULE_C_NAME "${_RULE_NAME}_c")
+ set(_LIB_NAME "${_PACKAGE_NAME}_${_RULE_C_NAME}")
+ set(_GEN_TARGET "${_LIB_NAME}_gen")
+ set(_O_FILE_NAME ${_RULE_C_NAME}.o)
+ set(_H_FILE_NAME ${_RULE_C_NAME}.h)
+ set(_EMITC_FILE_NAME ${_RULE_NAME}_emitc.h)
+
+ ## Example with VM C module.
+ # Setup args for iree-translate.
+ set(_TRANSLATE_ARGS ${_RULE_FLAGS})
+ list(APPEND _TRANSLATE_ARGS "-iree-mlir-to-vm-c-module")
+ list(APPEND _TRANSLATE_ARGS "-iree-hal-target-backends=dylib-llvm-aot")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-target-triple=riscv32-pc-linux-elf")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-target-cpu=generic-rv32")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-target-cpu-features=+m,+f,+experimental-v")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-target-abi=ilp32")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-link-embedded=false")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-link-static")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-system-linker-path=\"${_LINKER_TOOL_EXECUTABLE}\"")
+ list(APPEND _TRANSLATE_ARGS "-iree-llvm-static-library-output-path=${_O_FILE_NAME}")
+ list(APPEND _TRANSLATE_ARGS "${_MLIR_SRC}")
+ list(APPEND _TRANSLATE_ARGS "-o")
+ list(APPEND _TRANSLATE_ARGS "${_EMITC_FILE_NAME}")
+
+ # Custom command for iree-translate to generate static library and C module.
+ add_custom_command(
+ OUTPUT
+ ${_H_FILE_NAME}
+ ${_O_FILE_NAME}
+ ${_EMITC_FILE_NAME}
+ COMMAND ${_TRANSLATE_TOOL_EXECUTABLE} ${_TRANSLATE_ARGS}
+ DEPENDS ${_TRANSLATE_TOOL_EXECUTABLE} ${_MLIR_SRC} ${_LINKER_TOOL_EXECUTABLE}
+ )
+
+ add_custom_target(
+ ${_GEN_TARGET}
+ DEPENDS
+ "${_EMITC_FILE_NAME}"
+ )
+
+ add_library(${_LIB_NAME}
+ STATIC
+ ${_O_FILE_NAME}
+ )
+ add_dependencies(${_LIB_NAME} ${_GEN_TARGET})
+
+ SET_TARGET_PROPERTIES(
+ ${_LIB_NAME}
+ PROPERTIES
+ LINKER_LANGUAGE C
+ )
+
+ # Alias the iree_package_name library to iree::package::name.
+ # This lets us more clearly map to Bazel and makes it possible to
+ # disambiguate the underscores in paths vs. the separators.
+ add_library(${_PACKAGE_NS}::${_RULE_C_NAME} ALIAS ${_LIB_NAME})
+ iree_package_dir(_PACKAGE_DIR)
+ if(${_RULE_C_NAME} STREQUAL ${_PACKAGE_DIR})
+ add_library(${_PACKAGE_NS} ALIAS ${_LIB_NAME})
+ endif()
+
+endfunction()
diff --git a/cmake/springbok_modules.cmake b/cmake/springbok_modules.cmake
new file mode 100644
index 0000000..eee7e94
--- /dev/null
+++ b/cmake/springbok_modules.cmake
@@ -0,0 +1,69 @@
+include(CMakeParseArguments)
+
+# springbok_modules()
+#
+# A wrapper for the springbok_bytecode_module and springbok_c_module to apply common iree-translate flags
+# Parameters:
+# NAME: Name of target.
+# SRC: Source file to compile into a bytecode module. Support relative path.
+# FLAGS: Flags to pass to the translation tool (list of strings).
+# C_IDENTIFIER: Identifier to use for generate c embed code.
+# If omitted then no C embed code will be generated.
+#
+# Examples:
+# springbok_modules(
+# NAME
+# dare_devel
+# SRC
+# "daredevil_quant.tflite"
+# C_IDENTIFIER
+# "daredevil_bytecode_module_dylib"
+# FLAGS
+# "-iree-input-type=tosa"
+# )
+#
+# springbok_modules(
+# NAME
+# simple_float_mul
+# SRC
+# "simple_float_mul.mlir"
+# C_IDENTIFIER
+# "simple_float_mul"
+# FLAGS
+# "-iree-input-type=mhlo"
+# PUBLIC
+# )
+#
+
+function(springbok_modules)
+ cmake_parse_arguments(
+ _RULE
+ "PUBLIC"
+ "NAME;SRC;C_IDENTIFIER"
+ "FLAGS"
+ ${ARGN}
+ )
+
+ springbok_bytecode_module(
+ NAME
+ "${_RULE_NAME}_bytecode_module_dylib"
+ SRC
+ "${_RULE_SRC}"
+ C_IDENTIFIER
+ "${_RULE_C_IDENTIFIER}_bytecode_module_dylib"
+ FLAGS
+ "${_RULE_FLAGS}"
+ PUBLIC
+ )
+
+ springbok_c_module(
+ NAME
+ "${_RULE_NAME}_c_module_static"
+ SRC
+ "${_RULE_SRC}"
+ FLAGS
+ "${_RULE_FLAGS}"
+ PUBLIC
+ )
+
+endfunction()
diff --git a/samples/device/CMakeLists.txt b/samples/device/CMakeLists.txt
index 460b738..cec04b0 100644
--- a/samples/device/CMakeLists.txt
+++ b/samples/device/CMakeLists.txt
@@ -12,3 +12,18 @@
iree::hal::local::loaders::embedded_library_loader
iree::hal::local::sync_driver
)
+
+iree_cc_library(
+ NAME
+ device_static_loader
+ HDRS
+ "device.h"
+ SRCS
+ "device_static_loader.c"
+ DEPS
+ iree::base
+ iree::hal
+ iree::hal::local
+ iree::hal::local::loaders::static_library_loader
+ iree::hal::local::sync_driver
+)
diff --git a/samples/device/device_static_loader.c b/samples/device/device_static_loader.c
new file mode 100644
index 0000000..225712c
--- /dev/null
+++ b/samples/device/device_static_loader.c
@@ -0,0 +1,51 @@
+// Static library loading in IREE.
+
+#include "iree/hal/local/loaders/static_library_loader.h"
+#include "iree/hal/local/sync_device.h"
+#include "iree/modules/hal/module.h"
+#include "samples/device/device.h"
+#include "samples/util/model_api.h"
+
+// A function to create the HAL device from the different backend targets.
+// The HAL device is returned based on the implementation, and it must be
+// released by the caller.
+iree_status_t create_sample_device(iree_allocator_t host_allocator,
+ iree_hal_device_t** out_device) {
+ iree_status_t status = iree_ok_status();
+
+ // Set paramters for the device created in the next step.
+ iree_hal_sync_device_params_t params;
+ iree_hal_sync_device_params_initialize(¶ms);
+
+ // Load the statically embedded library
+ const iree_hal_executable_library_header_t** static_library = library_query(
+ IREE_HAL_EXECUTABLE_LIBRARY_LATEST_VERSION, /*reserved=*/NULL);
+ const iree_hal_executable_library_header_t** libraries[1] = {static_library};
+
+ iree_hal_executable_loader_t* library_loader = NULL;
+ if (iree_status_is_ok(status)) {
+ status = iree_hal_static_library_loader_create(
+ IREE_ARRAYSIZE(libraries), libraries,
+ iree_hal_executable_import_provider_null(), host_allocator,
+ &library_loader);
+ }
+
+ // Use the default host allocator for buffer allocations.
+ iree_string_view_t identifier = iree_make_cstring_view("sync");
+ iree_hal_allocator_t* device_allocator = NULL;
+ if (iree_status_is_ok(status)) {
+ status = iree_hal_allocator_create_heap(identifier, host_allocator,
+ host_allocator, &device_allocator);
+ }
+
+ // Create the device and release the executor and loader afterwards.
+ if (iree_status_is_ok(status)) {
+ status = iree_hal_sync_device_create(
+ identifier, ¶ms, /*loader_count=*/1, &library_loader,
+ device_allocator, host_allocator, out_device);
+ }
+
+ iree_hal_allocator_release(device_allocator);
+ iree_hal_executable_loader_release(library_loader);
+ return status;
+}
diff --git a/samples/simple_vec_mul/CMakeLists.txt b/samples/simple_vec_mul/CMakeLists.txt
index 7fc33fc..f545988 100644
--- a/samples/simple_vec_mul/CMakeLists.txt
+++ b/samples/simple_vec_mul/CMakeLists.txt
@@ -4,13 +4,13 @@
# https://github.com/llvm/llvm-project/blob/0eeab8b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp#L30-L51
#-------------------------------------------------------------------------------
-springbok_bytecode_module(
+springbok_modules(
NAME
- simple_float_mul_bytecode_module_dylib
+ simple_float_mul
SRC
"simple_float_mul.mlir"
C_IDENTIFIER
- "samples_simple_vec_mul_simple_float_mul_bytecode_module_dylib"
+ "samples_simple_vec_mul_simple_float_mul"
FLAGS
"-iree-input-type=mhlo"
"-riscv-v-vector-bits-min=512"
@@ -19,13 +19,13 @@
PUBLIC
)
-springbok_bytecode_module(
+springbok_modules(
NAME
- simple_int_mul_bytecode_module_dylib
+ simple_int_mul
SRC
"simple_int_mul.mlir"
C_IDENTIFIER
- "samples_simple_vec_mul_simple_int_mul_bytecode_module_dylib"
+ "samples_simple_vec_mul_simple_int_mul"
FLAGS
"-iree-input-type=mhlo"
"-riscv-v-vector-bits-min=512"
@@ -58,6 +58,18 @@
iree_cc_binary(
NAME
+ simple_float_vec_mul_emitc_static
+ SRCS
+ "float_vec.c"
+ DEPS
+ ::simple_float_mul_c_module_static_c
+ samples::util::util_emitc
+ COPTS
+ "-DBUILD_EMITC_STATIC"
+)
+
+iree_cc_binary(
+ NAME
simple_int_vec_mul_embedded_sync
SRCS
"int_vec.c"
@@ -65,3 +77,15 @@
::simple_int_mul_bytecode_module_dylib_c
samples::util::util
)
+
+iree_cc_binary(
+ NAME
+ simple_int_vec_mul_emitc_static
+ SRCS
+ "int_vec.c"
+ DEPS
+ ::simple_int_mul_c_module_static_c
+ samples::util::util_emitc
+ COPTS
+ "-DBUILD_EMITC_STATIC"
+)
diff --git a/samples/simple_vec_mul/float_vec.c b/samples/simple_vec_mul/float_vec.c
index 3195547..197684f 100644
--- a/samples/simple_vec_mul/float_vec.c
+++ b/samples/simple_vec_mul/float_vec.c
@@ -5,7 +5,12 @@
#include "samples/util/util.h"
// Compiled module embedded here to avoid file IO:
+#if !defined(BUILD_EMITC_STATIC)
#include "samples/simple_vec_mul/simple_float_mul_bytecode_module_dylib_c.h"
+#else
+#include "samples/simple_vec_mul/simple_float_mul_c_module_static_c.h"
+#include "samples/simple_vec_mul/simple_float_mul_c_module_static_emitc.h"
+#endif
const MlModel kModel = {
.num_input = 2,
@@ -21,12 +26,25 @@
.model_name = "simple_float_vec_mul",
};
+#if !defined(BUILD_EMITC_STATIC)
const iree_const_byte_span_t load_bytecode_module_data() {
const struct iree_file_toc_t *module_file_toc =
samples_simple_vec_mul_simple_float_mul_bytecode_module_dylib_create();
return iree_make_const_byte_span(module_file_toc->data,
module_file_toc->size);
}
+#else
+// Function to create the C module.
+iree_status_t create_c_module(iree_vm_module_t **module) {
+ return module_create(iree_allocator_system(), module);
+}
+
+const iree_hal_executable_library_header_t **library_query(
+ iree_hal_executable_library_version_t max_version, void *reserved) {
+ return simple_mul_dispatch_0_library_query(max_version,
+ /*reserved=*/reserved);
+}
+#endif
iree_status_t load_input_data(const MlModel *model, void **buffer) {
iree_status_t result = alloc_input_buffer(model, buffer);
diff --git a/samples/simple_vec_mul/int_vec.c b/samples/simple_vec_mul/int_vec.c
index 70385a2..824a031 100644
--- a/samples/simple_vec_mul/int_vec.c
+++ b/samples/simple_vec_mul/int_vec.c
@@ -5,7 +5,12 @@
#include "samples/util/util.h"
// Compiled module embedded here to avoid file IO:
+#if !defined(BUILD_EMITC_STATIC)
#include "samples/simple_vec_mul/simple_int_mul_bytecode_module_dylib_c.h"
+#else
+#include "samples/simple_vec_mul/simple_int_mul_c_module_static_c.h"
+#include "samples/simple_vec_mul/simple_int_mul_c_module_static_emitc.h"
+#endif
const MlModel kModel = {
.num_input = 2,
@@ -21,12 +26,25 @@
.model_name = "simple_int_vec_mul",
};
+#if !defined(BUILD_EMITC_STATIC)
const iree_const_byte_span_t load_bytecode_module_data() {
const struct iree_file_toc_t *module_file_toc =
samples_simple_vec_mul_simple_int_mul_bytecode_module_dylib_create();
return iree_make_const_byte_span(module_file_toc->data,
module_file_toc->size);
}
+#else
+// Function to create the C module.
+iree_status_t create_c_module(iree_vm_module_t **module) {
+ return module_create(iree_allocator_system(), module);
+}
+
+const iree_hal_executable_library_header_t **library_query(
+ iree_hal_executable_library_version_t max_version, void *reserved) {
+ return simple_mul_dispatch_0_library_query(max_version,
+ /*reserved=*/reserved);
+}
+#endif
iree_status_t load_input_data(const MlModel *model, void **buffer) {
iree_status_t result = alloc_input_buffer(model, buffer);
diff --git a/samples/util/CMakeLists.txt b/samples/util/CMakeLists.txt
index 6023a18..14d2170 100644
--- a/samples/util/CMakeLists.txt
+++ b/samples/util/CMakeLists.txt
@@ -20,6 +20,28 @@
iree_cc_library(
NAME
+ util_emitc
+ HDRS
+ "util.h"
+ SRCS
+ "util.c"
+ DEPS
+ ::alloc
+ iree::base
+ iree::hal
+ iree::hal::local
+ iree::hal::local::loaders::static_library_loader
+ iree::hal::local::sync_driver
+ iree::modules::hal
+ iree::vm
+ iree::vm::shims_emitc
+ samples::device::device_static_loader
+ COPTS
+ "-DBUILD_EMITC_STATIC"
+)
+
+iree_cc_library(
+ NAME
alloc
HDRS
"alloc.h"
diff --git a/samples/util/model_api.h b/samples/util/model_api.h
index a409019..e4a4f23 100644
--- a/samples/util/model_api.h
+++ b/samples/util/model_api.h
@@ -5,6 +5,7 @@
#include "iree/base/api.h"
#include "iree/hal/api.h"
+#include "iree/hal/local/executable_library.h"
#include "iree/modules/hal/module.h"
#include "iree/vm/api.h"
#include "iree/vm/bytecode_module.h"
@@ -31,6 +32,13 @@
// Load the VM bytecode module from the embedded c library into memory.
const iree_const_byte_span_t load_bytecode_module_data();
+// Load the statically embedded library
+const iree_hal_executable_library_header_t **library_query(
+ iree_hal_executable_library_version_t max_version, void *reserved);
+
+// Function to create the C module.
+iree_status_t create_c_module(iree_vm_module_t **module);
+
// For each ML workload, based on the model configuration, allocate the buffer
// and prepare the data. It can be loaded from a embedded image binary, a
// randomly generated stream, or a pointer from the sensor/ISP output.
diff --git a/samples/util/util.c b/samples/util/util.c
index b0b514a..2a0bec5 100644
--- a/samples/util/util.c
+++ b/samples/util/util.c
@@ -14,6 +14,17 @@
extern const MlModel kModel;
+// Function to create bytecode or C module.
+static iree_status_t create_module(iree_vm_module_t **module) {
+#if !defined(BUILD_EMITC_STATIC)
+ const iree_const_byte_span_t module_data = load_bytecode_module_data();
+ return iree_vm_bytecode_module_create(module_data, iree_allocator_null(),
+ iree_allocator_system(), module);
+#else
+ return create_c_module(module);
+#endif
+}
+
// Prepare the input buffers and buffer_views based on the data type. They must
// be released by the caller.
static iree_status_t prepare_input_hal_buffer_views(
@@ -62,26 +73,22 @@
result =
iree_hal_module_create(device, iree_allocator_system(), &hal_module);
}
- // Load bytecode module from the embedded data.
- const iree_const_byte_span_t module_data = load_bytecode_module_data();
-
- iree_vm_module_t *bytecode_module = NULL;
+ // Load bytecode or C module.
+ iree_vm_module_t *module = NULL;
if (iree_status_is_ok(result)) {
- result = iree_vm_bytecode_module_create(module_data, iree_allocator_null(),
- iree_allocator_system(),
- &bytecode_module);
+ result = create_module(&module);
}
// Allocate a context that will hold the module state across invocations.
iree_vm_context_t *context = NULL;
- iree_vm_module_t *modules[] = {hal_module, bytecode_module};
+ iree_vm_module_t *modules[] = {hal_module, module};
if (iree_status_is_ok(result)) {
result = iree_vm_context_create_with_modules(
instance, IREE_VM_CONTEXT_FLAG_NONE, &modules[0],
IREE_ARRAYSIZE(modules), iree_allocator_system(), &context);
}
iree_vm_module_release(hal_module);
- iree_vm_module_release(bytecode_module);
+ iree_vm_module_release(module);
// Lookup the entry point function.
// Note that we use the synchronous variant which operates on pure type/shape