Consolidate sample utility functions

Extend sample utility functions to support multiple inputs, so
simple_vec_mul can use the same utility functions.

Move the large barcode_float executable out of BUILD_WITH_SPRINGBOK
configuration

Move the input buffer allocation to a separate function so it can be
skipped if the buffer is allocated elsewhere, e.g., load from the
c_embedded data file.

Fix stack size for some models.

Change-Id: I954436bbb19d9707f64d6b1a252e39ddcfa3f7b9
diff --git a/samples/float_model_embedding/CMakeLists.txt b/samples/float_model_embedding/CMakeLists.txt
index 4cc4955..8d3ef0a 100644
--- a/samples/float_model_embedding/CMakeLists.txt
+++ b/samples/float_model_embedding/CMakeLists.txt
@@ -50,22 +50,22 @@
       "-riscv-v-fixed-length-vector-elen-max=32"
     PUBLIC
   )
-endif()
 
-springbok_bytecode_module(
-  NAME
-    barcode_bytecode_module_dylib
-  SRC
-    "$ENV{ROOTDIR}/ml/ml-models/float_models/barcode_float.tflite"
-  C_IDENTIFIER
-    "samples_float_model_embedding_barcode_bytecode_module_dylib"
-  FLAGS
-    "-iree-input-type=tosa"
-    "-riscv-v-vector-bits-min=512"
-    "-riscv-v-fixed-length-vector-lmul-max=8"
-    "-riscv-v-fixed-length-vector-elen-max=32"
-  PUBLIC
-)
+  springbok_bytecode_module(
+    NAME
+      barcode_bytecode_module_dylib
+    SRC
+      "$ENV{ROOTDIR}/ml/ml-models/float_models/barcode_float.tflite"
+    C_IDENTIFIER
+      "samples_float_model_embedding_barcode_bytecode_module_dylib"
+    FLAGS
+      "-iree-input-type=tosa"
+      "-riscv-v-vector-bits-min=512"
+      "-riscv-v-fixed-length-vector-lmul-max=8"
+      "-riscv-v-fixed-length-vector-elen-max=32"
+    PUBLIC
+  )
+endif()
 
 springbok_bytecode_module(
   NAME
@@ -196,19 +196,19 @@
       ::scenenet_v2_bytecode_module_dylib_c
       samples::util::util
   )
-endif()
 
-iree_cc_binary(
-  NAME
-    barcode_embedded_sync
-  SRCS
-    "barcode.c"
-  DEPS
-    ::barcode_bytecode_module_dylib_c
-    samples::util::util
-  LINKOPTS
-    "LINKER:--defsym=__stack_size__=100k"
-)
+  iree_cc_binary(
+    NAME
+      barcode_embedded_sync
+    SRCS
+      "barcode.c"
+    DEPS
+      ::barcode_bytecode_module_dylib_c
+      samples::util::util
+    LINKOPTS
+      "LINKER:--defsym=__stack_size__=100k"
+  )
+endif()
 
 iree_cc_binary(
   NAME
diff --git a/samples/float_model_embedding/barcode.c b/samples/float_model_embedding/barcode.c
index 362e339..31c2d86 100644
--- a/samples/float_model_embedding/barcode.c
+++ b/samples/float_model_embedding/barcode.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/barcode_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 320, 320, 1},
-    .input_length = 320 * 320 * 1,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 320, 320, 1}},
+    .input_length = {320 * 320 * 1},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 12,
     .output_length = {20 * 20 * 36, 20 * 20 * 9, 10 * 10 * 72, 10 * 10 * 18,
                       5 * 5 * 72, 5 * 5 * 18, 3 * 3 * 72, 3 * 3 * 18,
@@ -34,13 +35,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_data(model, buffer);
   // Populate initial value
   srand(768954);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -52,7 +56,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/daredevil.c b/samples/float_model_embedding/daredevil.c
index 488760e..660910a 100644
--- a/samples/float_model_embedding/daredevil.c
+++ b/samples/float_model_embedding/daredevil.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/daredevil_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 3,
-    .input_shape = {1, 96, 64},
-    .input_length = 96 * 64,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {3},
+    .input_shape = {{1, 96, 64}},
+    .input_length = {96 * 64},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {527},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(3689964);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/fssd_25_8bit_v2.c b/samples/float_model_embedding/fssd_25_8bit_v2.c
index c02fa9a..b3cf4aa 100644
--- a/samples/float_model_embedding/fssd_25_8bit_v2.c
+++ b/samples/float_model_embedding/fssd_25_8bit_v2.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/fssd_25_8bit_v2_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 320, 320, 3},
-    .input_length = 320 * 320 * 3,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 320, 320, 3}},
+    .input_length = {320 * 320 * 3},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 2,
     .output_length = {1602, 1602 * 16},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(11111111);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/mnist.c b/samples/float_model_embedding/mnist.c
index 1a680b9..3e7d121 100644
--- a/samples/float_model_embedding/mnist.c
+++ b/samples/float_model_embedding/mnist.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/mnist_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 28, 28, 1},
-    .input_length = 28 * 28 * 1,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 28, 28, 1}},
+    .input_length = {28 * 28 * 1},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {10},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(74886738);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/mobilenet_v1.c b/samples/float_model_embedding/mobilenet_v1.c
index c8e83b1..0472dde 100644
--- a/samples/float_model_embedding/mobilenet_v1.c
+++ b/samples/float_model_embedding/mobilenet_v1.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/mobilenet_v1_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 224, 224, 3},
-    .input_length = 224 * 224 * 3,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 224, 224, 3}},
+    .input_length = {224 * 224 * 3},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {1001},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(33333333);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/person_detection.c b/samples/float_model_embedding/person_detection.c
index c4c8430..19cfb4f 100644
--- a/samples/float_model_embedding/person_detection.c
+++ b/samples/float_model_embedding/person_detection.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/person_detection_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 96, 96, 1},
-    .input_length = 96 * 96 * 1,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 96, 96, 1}},
+    .input_length = {96 * 96 * 1},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {2},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(44444444);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/scenenet_v2.c b/samples/float_model_embedding/scenenet_v2.c
index e820152..6c1b316 100644
--- a/samples/float_model_embedding/scenenet_v2.c
+++ b/samples/float_model_embedding/scenenet_v2.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/scenenet_v2_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 192, 192, 3},
-    .input_length = 192 * 192 * 3,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 192, 192, 3}},
+    .input_length = {192 * 192 * 3},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {170},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(55555555);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/semantic_lift.c b/samples/float_model_embedding/semantic_lift.c
index 8bf1744..7e50f5e 100644
--- a/samples/float_model_embedding/semantic_lift.c
+++ b/samples/float_model_embedding/semantic_lift.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/semantic_lift_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 128, 128, 3},
-    .input_length = 128 * 128 * 3,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 128, 128, 3}},
+    .input_length = {128 * 128 * 3},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 2,
     .output_length = {2, 2},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(66666666);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/float_model_embedding/voice_commands.c b/samples/float_model_embedding/voice_commands.c
index d2b587b..d6357e9 100644
--- a/samples/float_model_embedding/voice_commands.c
+++ b/samples/float_model_embedding/voice_commands.c
@@ -12,10 +12,11 @@
 #include "samples/float_model_embedding/voice_commands_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 3,
-    .input_shape = {1, 198, 32},
-    .input_length = 198 * 32,
-    .input_size_bytes = sizeof(float),
+    .num_input = 1,
+    .num_input_dim = {3},
+    .input_shape = {{1, 198, 32}},
+    .input_length = {198 * 32},
+    .input_size_bytes = {sizeof(float)},
     .num_output = 1,
     .output_length = {63},
     .output_size_bytes = sizeof(float),
@@ -32,13 +33,16 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(77777777);
-  for (int i = 0; i < model->input_length; ++i) {
-    int x = rand();
-    ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      int x = rand();
+      ((float *)*buffer)[i] = (float)x / (float)RAND_MAX;
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/CMakeLists.txt b/samples/quant_model_embedding/CMakeLists.txt
index 052c889..cca899d 100644
--- a/samples/quant_model_embedding/CMakeLists.txt
+++ b/samples/quant_model_embedding/CMakeLists.txt
@@ -193,7 +193,7 @@
     ::person_detection_bytecode_module_dylib_c
     samples::util::util
   LINKOPTS
-    "LINKER:--defsym=__stack_size__=100k"
+    "LINKER:--defsym=__stack_size__=128k"
 )
 
 if(NOT ${BUILD_INTERNAL_MODELS})
@@ -268,4 +268,6 @@
   DEPS
     ::voice_commands_bytecode_module_dylib_c
     samples::util::util
+  LINKOPTS
+    "LINKER:--defsym=__stack_size__=64k"
 )
diff --git a/samples/quant_model_embedding/barcode.c b/samples/quant_model_embedding/barcode.c
index a8119cb..618317a 100644
--- a/samples/quant_model_embedding/barcode.c
+++ b/samples/quant_model_embedding/barcode.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/barcode_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 320, 320, 1},
-    .input_length = 320 * 320 * 1,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 320, 320, 1}},
+    .input_length = {320 * 320 * 1},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 12,
     .output_length = {20 * 20 * 36, 20 * 20 * 9, 10 * 10 * 72, 10 * 10 * 18,
                       5 * 5 * 72, 5 * 5 * 18, 3 * 3 * 72, 3 * 3 * 18,
@@ -34,12 +35,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(768954);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -51,7 +55,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/daredevil.c b/samples/quant_model_embedding/daredevil.c
index 97cc434..ee3325e 100644
--- a/samples/quant_model_embedding/daredevil.c
+++ b/samples/quant_model_embedding/daredevil.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/daredevil_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 3,
-    .input_shape = {1, 96, 64},
-    .input_length = 96 * 64,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {3},
+    .input_shape = {{1, 96, 64}},
+    .input_length = {96 * 64},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 1,
     .output_length = {527},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(3689964);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/fssd_25_8bit_v2.c b/samples/quant_model_embedding/fssd_25_8bit_v2.c
index 3b38938..39d072b 100644
--- a/samples/quant_model_embedding/fssd_25_8bit_v2.c
+++ b/samples/quant_model_embedding/fssd_25_8bit_v2.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/fssd_25_8bit_v2_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 320, 320, 3},
-    .input_length = 320 * 320 * 3,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 320, 320, 3}},
+    .input_length = {320 * 320 * 3},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 8,
     .output_length = {20 * 20 * 48, 20 * 20 * 3, 10 * 10 * 48, 10 * 10 * 3,
                       5 * 5 * 48, 5 * 5 * 3, 3 * 3 * 48, 3 * 3 * 3},
@@ -33,12 +34,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(11111111);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -50,7 +54,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/mobilenet_v1.c b/samples/quant_model_embedding/mobilenet_v1.c
index 778781e..3570a4b 100644
--- a/samples/quant_model_embedding/mobilenet_v1.c
+++ b/samples/quant_model_embedding/mobilenet_v1.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/mobilenet_v1_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 224, 224, 3},
-    .input_length = 224 * 224 * 3,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 224, 224, 3}},
+    .input_length = {224 * 224 * 3},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 1,
     .output_length = {1001},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(33333333);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/mobilenet_v2.c b/samples/quant_model_embedding/mobilenet_v2.c
index 0f0f63c..cb5a9fc 100644
--- a/samples/quant_model_embedding/mobilenet_v2.c
+++ b/samples/quant_model_embedding/mobilenet_v2.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/mobilenet_v2_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 224, 224, 3},
-    .input_length = 224 * 224 * 3,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 224, 224, 3}},
+    .input_length = {224 * 224 * 3},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 1,
     .output_length = {1001},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(33333333);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/person_detection.c b/samples/quant_model_embedding/person_detection.c
index 1a7d4b2..0cd1a09 100644
--- a/samples/quant_model_embedding/person_detection.c
+++ b/samples/quant_model_embedding/person_detection.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/person_detection_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 96, 96, 1},
-    .input_length = 96 * 96 * 1,
-    .input_size_bytes = sizeof(int8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 96, 96, 1}},
+    .input_length = {96 * 96 * 1},
+    .input_size_bytes = {sizeof(int8_t)},
     .num_output = 1,
     .output_length = {2},
     .output_size_bytes = sizeof(int8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(44444444);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((int8_t *)*buffer)[i] = (int8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((int8_t *)*buffer)[i] = (int8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/scenenet_v2.c b/samples/quant_model_embedding/scenenet_v2.c
index ddc5a73..16ccfd5 100644
--- a/samples/quant_model_embedding/scenenet_v2.c
+++ b/samples/quant_model_embedding/scenenet_v2.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/scenenet_v2_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 192, 192, 3},
-    .input_length = 192 * 192 * 3,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 192, 192, 3}},
+    .input_length = {192 * 192 * 3},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 1,
     .output_length = {170},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(55555555);
-  for (int i = 0; i < model->input_length; ++i) {
+  if (iree_status_is_ok(result)) {
+  for (int i = 0; i < model->input_length[0]; ++i) {
     ((uint8_t *)*buffer)[i] = (uint8_t)rand();
   }
-  return iree_ok_status();
+  }
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/semantic_lift.c b/samples/quant_model_embedding/semantic_lift.c
index 727fcf0..0f0888d 100644
--- a/samples/quant_model_embedding/semantic_lift.c
+++ b/samples/quant_model_embedding/semantic_lift.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/semantic_lift_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 4,
-    .input_shape = {1, 128, 128, 3},
-    .input_length = 128 * 128 * 3,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {4},
+    .input_shape = {{1, 128, 128, 3}},
+    .input_length = {128 * 128 * 3},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 3,
     .output_length = {2, 2, 2},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(66666666);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/quant_model_embedding/voice_commands.c b/samples/quant_model_embedding/voice_commands.c
index dab6188..2818338 100644
--- a/samples/quant_model_embedding/voice_commands.c
+++ b/samples/quant_model_embedding/voice_commands.c
@@ -12,10 +12,11 @@
 #include "samples/quant_model_embedding/voice_commands_bytecode_module_dylib_c.h"
 
 const MlModel kModel = {
-    .num_input_dim = 3,
-    .input_shape = {1, 198, 32},
-    .input_length = 198 * 32,
-    .input_size_bytes = sizeof(uint8_t),
+    .num_input = 1,
+    .num_input_dim = {3},
+    .input_shape = {{1, 198, 32}},
+    .input_length = {198 * 32},
+    .input_size_bytes = {sizeof(uint8_t)},
     .num_output = 1,
     .output_length = {63},
     .output_size_bytes = sizeof(uint8_t),
@@ -32,12 +33,15 @@
 }
 
 iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial value
   srand(77777777);
-  for (int i = 0; i < model->input_length; ++i) {
-    ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+  if (iree_status_is_ok(result)) {
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((uint8_t *)*buffer)[i] = (uint8_t)rand();
+    }
   }
-  return iree_ok_status();
+  return result;
 }
 
 iree_status_t check_output_data(const MlModel *model,
@@ -49,7 +53,7 @@
           model->output_length[index_output]) {
     result = iree_make_status(IREE_STATUS_UNKNOWN, "output length mismatches");
   }
-  LOG_INFO("Output #%d data length: %d\n", index_output,
+  LOG_INFO("Output #%d data length: %d", index_output,
            mapped_memory->contents.data_length / model->output_size_bytes);
   return result;
 }
diff --git a/samples/simple_vec_mul/CMakeLists.txt b/samples/simple_vec_mul/CMakeLists.txt
index 32ef4bd..7fc33fc 100644
--- a/samples/simple_vec_mul/CMakeLists.txt
+++ b/samples/simple_vec_mul/CMakeLists.txt
@@ -51,18 +51,9 @@
     simple_float_vec_mul_embedded_sync
   SRCS
     "float_vec.c"
-    "simple_vec_mul.c"
   DEPS
     ::simple_float_mul_bytecode_module_dylib_c
-    iree::base
-    iree::hal
-    iree::hal::local
-    iree::hal::local::loaders::embedded_library_loader
-    iree::hal::local::sync_driver
-    iree::modules::hal
-    iree::vm
-    iree::vm::bytecode_module
-    samples::device::device_embedded_sync
+    samples::util::util
 )
 
 iree_cc_binary(
@@ -70,16 +61,7 @@
     simple_int_vec_mul_embedded_sync
   SRCS
     "int_vec.c"
-    "simple_vec_mul.c"
   DEPS
     ::simple_int_mul_bytecode_module_dylib_c
-    iree::base
-    iree::hal
-    iree::hal::local
-    iree::hal::local::loaders::embedded_library_loader
-    iree::hal::local::sync_driver
-    iree::modules::hal
-    iree::vm
-    iree::vm::bytecode_module
-    samples::device::device_embedded_sync
+    samples::util::util
 )
diff --git a/samples/simple_vec_mul/float_vec.c b/samples/simple_vec_mul/float_vec.c
index 52520e8..3195547 100644
--- a/samples/simple_vec_mul/float_vec.c
+++ b/samples/simple_vec_mul/float_vec.c
@@ -2,10 +2,25 @@
 
 #include "iree/base/api.h"
 #include "iree/hal/api.h"
+#include "samples/util/util.h"
 
 // Compiled module embedded here to avoid file IO:
 #include "samples/simple_vec_mul/simple_float_mul_bytecode_module_dylib_c.h"
 
+const MlModel kModel = {
+    .num_input = 2,
+    .num_input_dim = {1, 1},
+    .input_shape = {{1024}, {1024}},
+    .input_length = {1024, 1024},
+    .input_size_bytes = {sizeof(float), sizeof(float)},
+    .num_output = 1,
+    .output_length = {1024},
+    .output_size_bytes = sizeof(float),
+    .hal_element_type = IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+    .entry_func = "module.simple_mul",
+    .model_name = "simple_float_vec_mul",
+};
+
 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();
@@ -13,59 +28,23 @@
                                    module_file_toc->size);
 }
 
-iree_status_t prepare_input_hal_buffer_views(
-    iree_hal_device_t *device, const int buffer_length, void **arg0_buffer,
-    void **arg1_buffer, iree_hal_buffer_view_t **arg0_buffer_view,
-    iree_hal_buffer_view_t **arg1_buffer_view) {
-  iree_status_t result = iree_ok_status();
-  *arg0_buffer =
-      iree_aligned_alloc(sizeof(uint32_t), sizeof(float) * buffer_length);
-  if (*arg0_buffer == NULL) {
-    result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
-  }
-  if (iree_status_is_ok(result)) {
-    *arg1_buffer =
-        iree_aligned_alloc(sizeof(uint32_t), sizeof(float) * buffer_length);
-    if (*arg1_buffer == NULL) {
-      result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
-    }
-  }
-
+iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial values
   // arg0 = 0, 1/4, 1/2, 3/4... 1023/4
   // arg1 = 0, 1/2, 1, 3/2... 1023/2
-  for (int i = 0; i < buffer_length; ++i) {
-    ((float *)*arg0_buffer)[i] = i / 4.0f;
-    ((float *)*arg1_buffer)[i] = i / 2.0f;
-  }
-  // Wrap buffers in shaped buffer views.
-  // The buffers can be mapped on the CPU and that can also be used
-  // on the device. Not all devices support this, but the ones we have now do.
-  iree_hal_dim_t shape[1] = {buffer_length};
-
-  iree_hal_memory_type_t input_memory_type =
-      IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE;
-
-  result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
-      iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-      IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
-      input_memory_type, IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
-      iree_make_byte_span(*arg0_buffer, sizeof(int32_t) * buffer_length),
-      iree_allocator_null(), arg0_buffer_view);
-
   if (iree_status_is_ok(result)) {
-    result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
-        iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
-        input_memory_type, IREE_HAL_MEMORY_ACCESS_READ,
-        IREE_HAL_BUFFER_USAGE_ALL,
-        iree_make_byte_span(*arg1_buffer, sizeof(int32_t) * buffer_length),
-        iree_allocator_null(), arg1_buffer_view);
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((float *)buffer[0])[i] = i / 4.0f;
+      ((float *)buffer[1])[i] = i / 2.0f;
+    }
   }
   return result;
 }
 
-iree_status_t check_output_data(iree_hal_buffer_mapping_t *mapped_memory) {
+iree_status_t check_output_data(const MlModel *model,
+                                iree_hal_buffer_mapping_t *mapped_memory,
+                                int index_output) {
   iree_status_t result = iree_ok_status();
   for (int i = 0; i < mapped_memory->contents.data_length / sizeof(float);
        ++i) {
diff --git a/samples/simple_vec_mul/int_vec.c b/samples/simple_vec_mul/int_vec.c
index 752a1c3..70385a2 100644
--- a/samples/simple_vec_mul/int_vec.c
+++ b/samples/simple_vec_mul/int_vec.c
@@ -2,10 +2,25 @@
 
 #include "iree/base/api.h"
 #include "iree/hal/api.h"
+#include "samples/util/util.h"
 
 // Compiled module embedded here to avoid file IO:
 #include "samples/simple_vec_mul/simple_int_mul_bytecode_module_dylib_c.h"
 
+const MlModel kModel = {
+    .num_input = 2,
+    .num_input_dim = {1, 1},
+    .input_shape = {{1024}, {1024}},
+    .input_length = {1024, 1024},
+    .input_size_bytes = {sizeof(int32_t), sizeof(int32_t)},
+    .num_output = 1,
+    .output_length = {1024},
+    .output_size_bytes = sizeof(int32_t),
+    .hal_element_type = IREE_HAL_ELEMENT_TYPE_SINT_32,
+    .entry_func = "module.simple_mul",
+    .model_name = "simple_int_vec_mul",
+};
+
 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();
@@ -13,59 +28,23 @@
                                    module_file_toc->size);
 }
 
-iree_status_t prepare_input_hal_buffer_views(
-    iree_hal_device_t *device, const int buffer_length, void **arg0_buffer,
-    void **arg1_buffer, iree_hal_buffer_view_t **arg0_buffer_view,
-    iree_hal_buffer_view_t **arg1_buffer_view) {
-  iree_status_t result = iree_ok_status();
-  *arg0_buffer =
-      iree_aligned_alloc(sizeof(uint32_t), sizeof(int32_t) * buffer_length);
-  if (*arg0_buffer == NULL) {
-    result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
-  }
-  if (iree_status_is_ok(result)) {
-    *arg1_buffer =
-        iree_aligned_alloc(sizeof(uint32_t), sizeof(int32_t) * buffer_length);
-    if (*arg1_buffer == NULL) {
-      result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
-    }
-  }
-
+iree_status_t load_input_data(const MlModel *model, void **buffer) {
+  iree_status_t result = alloc_input_buffer(model, buffer);
   // Populate initial values
   // arg0 = 0, 0, 1, 1,..., 511
   // arg1 = 0, 1, 2, 3,..., 1023
-  for (int i = 0; i < buffer_length; ++i) {
-    ((int32_t *)*arg0_buffer)[i] = i >> 1;
-    ((int32_t *)*arg1_buffer)[i] = i;
-  }
-  // Wrap buffers in shaped buffer views.
-  // The buffers can be mapped on the CPU and that can also be used
-  // on the device. Not all devices support this, but the ones we have now do.
-  iree_hal_dim_t shape[1] = {buffer_length};
-
-  iree_hal_memory_type_t input_memory_type =
-      IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE;
-
-  result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
-      iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-      IREE_HAL_ELEMENT_TYPE_SINT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
-      input_memory_type, IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
-      iree_make_byte_span(*arg0_buffer, sizeof(int32_t) * buffer_length),
-      iree_allocator_null(), arg0_buffer_view);
-
   if (iree_status_is_ok(result)) {
-    result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
-        iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-        IREE_HAL_ELEMENT_TYPE_SINT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
-        input_memory_type, IREE_HAL_MEMORY_ACCESS_READ,
-        IREE_HAL_BUFFER_USAGE_ALL,
-        iree_make_byte_span(*arg1_buffer, sizeof(int32_t) * buffer_length),
-        iree_allocator_null(), arg1_buffer_view);
+    for (int i = 0; i < model->input_length[0]; ++i) {
+      ((int32_t *)buffer[0])[i] = i >> 1;
+      ((int32_t *)buffer[1])[i] = i;
+    }
   }
   return result;
 }
 
-iree_status_t check_output_data(iree_hal_buffer_mapping_t *mapped_memory) {
+iree_status_t check_output_data(const MlModel *model,
+                                iree_hal_buffer_mapping_t *mapped_memory,
+                                int index_output) {
   iree_status_t result = iree_ok_status();
   for (int i = 0; i < mapped_memory->contents.data_length / sizeof(int32_t);
        ++i) {
diff --git a/samples/simple_vec_mul/simple_vec_mul.c b/samples/simple_vec_mul/simple_vec_mul.c
deleted file mode 100644
index 61c86a9..0000000
--- a/samples/simple_vec_mul/simple_vec_mul.c
+++ /dev/null
@@ -1,168 +0,0 @@
-// An example based on iree/samples/simple_embedding. Test a 1024-element int32
-// multiplication with vector extension ISA.
-
-#include <springbok.h>
-#include <stdio.h>
-
-#include "iree/base/api.h"
-#include "iree/hal/api.h"
-#include "iree/modules/hal/module.h"
-#include "iree/vm/api.h"
-#include "iree/vm/bytecode_module.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.
-extern iree_status_t create_sample_device(iree_hal_device_t **device);
-
-extern const iree_const_byte_span_t load_bytecode_module_data();
-
-// Prepare the input buffers and buffer_views based on the data type. They must
-// be released by the caller.
-extern iree_status_t prepare_input_hal_buffer_views(
-    iree_hal_device_t *device, const int buffer_length, void **arg0_buffer,
-    void **arg1_buffer, iree_hal_buffer_view_t **arg0_buffer_view,
-    iree_hal_buffer_view_t **arg1_buffer_view);
-
-extern iree_status_t check_output_data(
-    iree_hal_buffer_mapping_t *mapped_memory);
-
-iree_status_t run() {
-  IREE_RETURN_IF_ERROR(iree_hal_module_register_types());
-
-  iree_vm_instance_t *instance = NULL;
-  iree_status_t result =
-      iree_vm_instance_create(iree_allocator_system(), &instance);
-
-  iree_hal_device_t *device = NULL;
-  if (iree_status_is_ok(result)) {
-    result = create_sample_device(&device);
-  }
-  iree_vm_module_t *hal_module = NULL;
-  if (iree_status_is_ok(result)) {
-    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;
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_bytecode_module_create(module_data, iree_allocator_null(),
-                                            iree_allocator_system(),
-                                            &bytecode_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};
-  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);
-
-  // Lookup the entry point function.
-  // Note that we use the synchronous variant which operates on pure type/shape
-  // erased buffers.
-  const char kMainFunctionName[] = "module.simple_mul";
-  iree_vm_function_t main_function;
-  if (iree_status_is_ok(result)) {
-    result = (iree_vm_context_resolve_function(
-        context, iree_make_cstring_view(kMainFunctionName), &main_function));
-  }
-
-  // Prepare the input buffers.
-  void *arg0_buffer = NULL;
-  void *arg1_buffer = NULL;
-  iree_hal_buffer_view_t *arg0_buffer_view = NULL;
-  iree_hal_buffer_view_t *arg1_buffer_view = NULL;
-  const int kElementCount = 1024;
-  if (iree_status_is_ok(result)) {
-    result = prepare_input_hal_buffer_views(device, kElementCount, &arg0_buffer,
-                                            &arg1_buffer, &arg0_buffer_view,
-                                            &arg1_buffer_view);
-  }
-
-  // Setup call inputs with our buffers.
-  iree_vm_list_t *inputs = NULL;
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_list_create(
-        /*element_type=*/NULL,
-        /*capacity=*/2, iree_allocator_system(), &inputs);
-  }
-  iree_vm_ref_t arg0_buffer_view_ref =
-      iree_hal_buffer_view_move_ref(arg0_buffer_view);
-  iree_vm_ref_t arg1_buffer_view_ref =
-      iree_hal_buffer_view_move_ref(arg1_buffer_view);
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_list_push_ref_move(inputs, &arg0_buffer_view_ref);
-  }
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_list_push_ref_move(inputs, &arg1_buffer_view_ref);
-  }
-
-  // Prepare outputs list to accept the results from the invocation.
-  // The output vm list is allocated statically.
-  iree_vm_list_t *outputs = NULL;
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_list_create(
-        /*element_type=*/NULL,
-        /*capacity=*/1, iree_allocator_system(), &outputs);
-  }
-
-  // Invoke the function.
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_invoke(context, main_function, IREE_VM_CONTEXT_FLAG_NONE,
-                            /*policy=*/NULL, inputs, outputs,
-                            iree_allocator_system());
-  }
-
-  iree_hal_buffer_view_t *ret_buffer_view = NULL;
-  if (iree_status_is_ok(result)) {
-    // Get the result buffers from the invocation.
-    ret_buffer_view = (iree_hal_buffer_view_t *)iree_vm_list_get_ref_deref(
-        outputs, 0, iree_hal_buffer_view_get_descriptor());
-    if (ret_buffer_view == NULL) {
-      result = iree_make_status(IREE_STATUS_NOT_FOUND,
-                                "can't find return buffer view");
-    }
-  }
-  // Read back the results and ensure we got the right values.
-  iree_hal_buffer_mapping_t mapped_memory;
-  if (iree_status_is_ok(result)) {
-    result = iree_hal_buffer_map_range(
-        iree_hal_buffer_view_buffer(ret_buffer_view),
-        IREE_HAL_MEMORY_ACCESS_READ, 0, IREE_WHOLE_BUFFER, &mapped_memory);
-  }
-  if (iree_status_is_ok(result)) {
-    result = check_output_data(&mapped_memory);
-    iree_hal_buffer_unmap_range(&mapped_memory);
-  }
-
-  iree_vm_list_release(inputs);
-  iree_vm_list_release(outputs);
-  iree_aligned_free(arg0_buffer);
-  iree_aligned_free(arg1_buffer);
-  iree_vm_context_release(context);
-  IREE_IGNORE_ERROR(iree_hal_allocator_statistics_fprint(
-      stdout, iree_hal_device_allocator(device)));
-  iree_hal_device_release(device);
-  iree_vm_instance_release(instance);
-  return result;
-}
-
-int main() {
-  const iree_status_t result = run();
-  int ret = (int)iree_status_code(result);
-  if (!iree_status_is_ok(result)) {
-    iree_status_fprint(stderr, result);
-    iree_status_free(result);
-  } else {
-    LOG_INFO("simple_vec_mul finished successfully");
-  }
-
-  return ret;
-}
diff --git a/samples/util/CMakeLists.txt b/samples/util/CMakeLists.txt
index b700268..f32025a 100644
--- a/samples/util/CMakeLists.txt
+++ b/samples/util/CMakeLists.txt
@@ -1,6 +1,8 @@
 iree_cc_library(
   NAME
     util
+  HDRS
+    "util.h"
   SRCS
     "util.c"
   DEPS
@@ -13,4 +15,5 @@
     iree::vm
     iree::vm::bytecode_module
     samples::device::device_embedded_sync
+  PUBLIC
 )
diff --git a/samples/util/util.c b/samples/util/util.c
index 7e7f59a..ed0a98c 100644
--- a/samples/util/util.c
+++ b/samples/util/util.c
@@ -29,17 +29,13 @@
 // 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(
-    const MlModel *model, iree_hal_device_t *device, void **arg0_buffer,
-    iree_hal_buffer_view_t **arg0_buffer_view) {
+    const MlModel *model, iree_hal_device_t *device, void **arg_buffers,
+    iree_hal_buffer_view_t **arg_buffer_views) {
   iree_status_t result = iree_ok_status();
-  *arg0_buffer = iree_aligned_alloc(
-      sizeof(uint32_t), model->input_size_bytes * model->input_length);
-  if (*arg0_buffer == NULL) {
-    result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
-  }
 
-  // Populate initial value
-  load_input_data(model, arg0_buffer);
+  // Prepare the input buffer, and populate the initial value.
+  // The input buffer must be released by the caller.
+  result = load_input_data(model, arg_buffers);
 
   // Wrap buffers in shaped buffer views.
   // The buffers can be mapped on the CPU and that can also be used
@@ -47,15 +43,18 @@
 
   iree_hal_memory_type_t input_memory_type =
       IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE;
-
-  result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
-      iree_hal_device_allocator(device), model->input_shape,
-      model->num_input_dim, model->hal_element_type, input_memory_type,
-      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, IREE_HAL_MEMORY_ACCESS_READ,
-      IREE_HAL_BUFFER_USAGE_ALL,
-      iree_make_byte_span(*arg0_buffer,
-                          model->input_size_bytes * model->input_length),
-      iree_allocator_null(), arg0_buffer_view);
+  for (int i = 0; i < model->num_input; ++i) {
+    if (iree_status_is_ok(result)) {
+      result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
+          iree_hal_device_allocator(device), model->input_shape[i],
+          model->num_input_dim[i], model->hal_element_type, input_memory_type,
+          IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, IREE_HAL_MEMORY_ACCESS_READ,
+          IREE_HAL_BUFFER_USAGE_ALL,
+          iree_make_byte_span(arg_buffers[i], model->input_size_bytes[i] *
+                                                  model->input_length[i]),
+          iree_allocator_null(), &(arg_buffer_views[i]));
+    }
+  }
   return result;
 }
 
@@ -106,24 +105,26 @@
   }
 
   // Prepare the input buffers.
-  void *arg0_buffer = NULL;
-  iree_hal_buffer_view_t *arg0_buffer_view = NULL;
+  void *arg_buffers[MAX_MODEL_INPUT_NUM] = {NULL};
+  iree_hal_buffer_view_t *arg_buffer_views[MAX_MODEL_INPUT_NUM] = {NULL};
   if (iree_status_is_ok(result)) {
-    result = prepare_input_hal_buffer_views(model, device, &arg0_buffer,
-                                            &arg0_buffer_view);
+    result = prepare_input_hal_buffer_views(model, device, arg_buffers,
+                                            arg_buffer_views);
   }
 
   // Setup call inputs with our buffers.
   iree_vm_list_t *inputs = NULL;
   if (iree_status_is_ok(result)) {
     result = iree_vm_list_create(
-        /*element_type=*/NULL,
-        /*capacity=*/1, iree_allocator_system(), &inputs);
+        /*element_type=*/NULL, /*capacity=*/model->num_input,
+        iree_allocator_system(), &inputs);
   }
-  iree_vm_ref_t arg0_buffer_view_ref =
-      iree_hal_buffer_view_move_ref(arg0_buffer_view);
-  if (iree_status_is_ok(result)) {
-    result = iree_vm_list_push_ref_move(inputs, &arg0_buffer_view_ref);
+  iree_vm_ref_t arg_buffer_view_ref;
+  for (int i = 0; i < model->num_input; ++i) {
+    arg_buffer_view_ref = iree_hal_buffer_view_move_ref(arg_buffer_views[i]);
+    if (iree_status_is_ok(result)) {
+      result = iree_vm_list_push_ref_move(inputs, &arg_buffer_view_ref);
+    }
   }
 
   // Prepare outputs list to accept the results from the invocation.
@@ -168,7 +169,11 @@
 
   iree_vm_list_release(inputs);
   iree_vm_list_release(outputs);
-  iree_aligned_free(arg0_buffer);
+  for (int i = 0; i < model->num_input; ++i) {
+    if (arg_buffers[i] != NULL) {
+      iree_aligned_free(arg_buffers[i]);
+    }
+  }
   iree_vm_context_release(context);
   IREE_IGNORE_ERROR(iree_hal_allocator_statistics_fprint(
       stdout, iree_hal_device_allocator(device)));
diff --git a/samples/util/util.h b/samples/util/util.h
index 6ad7e66..cae9682 100644
--- a/samples/util/util.h
+++ b/samples/util/util.h
@@ -1,5 +1,5 @@
-#ifndef SW_VEC_IREE_SAMPLES_UTIL_H_
-#define SW_VEC_IREE_SAMPLES_UTIL_H_
+#ifndef SW_VEC_IREE_SAMPLES_UTIL_UTIL_H_
+#define SW_VEC_IREE_SAMPLES_UTIL_UTIL_H_
 
 #include "iree/base/api.h"
 #include "iree/hal/api.h"
@@ -7,16 +7,17 @@
 #include "iree/vm/api.h"
 #include "iree/vm/bytecode_module.h"
 
+#define MAX_MODEL_INPUT_NUM 2
 #define MAX_MODEL_INPUT_DIM 4
 #define MAX_MODEL_OUTPUTS 12
 #define MAX_ENTRY_FUNC_NAME 20
 
-
 typedef struct {
-  int num_input_dim;
-  iree_hal_dim_t input_shape[MAX_MODEL_INPUT_DIM];
-  int input_length;
-  int input_size_bytes;
+  int num_input;
+  int num_input_dim[MAX_MODEL_INPUT_NUM];
+  iree_hal_dim_t input_shape[MAX_MODEL_INPUT_NUM][MAX_MODEL_INPUT_DIM];
+  int input_length[MAX_MODEL_INPUT_NUM];
+  int input_size_bytes[MAX_MODEL_INPUT_NUM];
   int num_output;
   int output_length[MAX_MODEL_OUTPUTS];
   int output_size_bytes;
@@ -25,4 +26,23 @@
   char model_name[];
 } MlModel;
 
-#endif  // SW_VEC_IREE_SAMPLES_UTIL_H_
+// Allocate the input buffer w.r.t the model config.
+// The buffer must be released by the external caller.
+static inline iree_status_t alloc_input_buffer(const MlModel *model,
+                                               void **buffer) {
+  iree_status_t result = iree_ok_status();
+  for (int i = 0; i < model->num_input; ++i) {
+    if (iree_status_is_ok(result)) {
+      buffer[i] =
+          iree_aligned_alloc(
+              sizeof(uint32_t),
+              model->input_size_bytes[i] * model->input_length[i]);
+      if (buffer[i] == NULL) {
+        result = iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED);
+      }
+    }
+  }
+  return result;
+}
+
+#endif  // SW_VEC_IREE_SAMPLES_UTIL_UTIL_H_