Эх сурвалжийг харах

Implement multi-module feature and bulk-memory feature (#271)

Refine wasm loader and aot loader
Fix potential issue of os_mmap/os_munmap
Update document
wenyongh 5 жил өмнө
parent
commit
752826a667
57 өөрчлөгдсөн 4085 нэмэгдсэн , 552 устгасан
  1. 48 15
      .clang-format
  2. 3 0
      .gitignore
  3. 7 1
      README.md
  4. 1 0
      assembly-script/.gitignore
  5. 13 1
      build-scripts/config_common.cmake
  6. 1 1
      core/app-framework/app-native-shared/bi-inc/attr_container.h
  7. 0 5
      core/app-framework/app-native-shared/restful_utils.c
  8. 3 3
      core/app-mgr/app-manager/app_manager_host.c
  9. 6 6
      core/app-mgr/app-manager/module_wasm_app.c
  10. 1 1
      core/app-mgr/app-mgr-shared/app_manager_export.h
  11. 10 0
      core/config.h
  12. 12 4
      core/iwasm/aot/aot_loader.c
  13. 25 0
      core/iwasm/aot/aot_reloc.h
  14. 168 76
      core/iwasm/aot/aot_runtime.c
  15. 9 0
      core/iwasm/aot/aot_runtime.h
  16. 1 1
      core/iwasm/common/wasm_native.c
  17. 538 26
      core/iwasm/common/wasm_runtime_common.c
  18. 69 11
      core/iwasm/common/wasm_runtime_common.h
  19. 4 0
      core/iwasm/compilation/aot.c
  20. 6 0
      core/iwasm/compilation/aot.h
  21. 33 0
      core/iwasm/compilation/aot_compiler.c
  22. 50 10
      core/iwasm/compilation/aot_emit_aot_file.c
  23. 287 0
      core/iwasm/compilation/aot_emit_memory.c
  24. 16 0
      core/iwasm/compilation/aot_emit_memory.h
  25. 3 0
      core/iwasm/compilation/aot_llvm.c
  26. 4 0
      core/iwasm/compilation/aot_llvm.h
  27. 1 0
      core/iwasm/include/aot_export.h
  28. 1 1
      core/iwasm/include/lib_export.h
  29. 54 2
      core/iwasm/include/wasm_export.h
  30. 43 3
      core/iwasm/interpreter/wasm.h
  31. 248 19
      core/iwasm/interpreter/wasm_interp_classic.c
  32. 225 18
      core/iwasm/interpreter/wasm_interp_fast.c
  33. 793 84
      core/iwasm/interpreter/wasm_loader.c
  34. 11 2
      core/iwasm/interpreter/wasm_opcode.h
  35. 590 166
      core/iwasm/interpreter/wasm_runtime.c
  36. 81 2
      core/iwasm/interpreter/wasm_runtime.h
  37. 1 1
      core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c
  38. 13 33
      core/shared/platform/common/posix/posix_memmap.c
  39. 1 2
      core/shared/platform/common/posix/posix_thread.c
  40. 25 10
      core/shared/platform/linux-sgx/sgx_platform.c
  41. 20 0
      core/shared/utils/bh_common.c
  42. 7 0
      core/shared/utils/bh_common.h
  43. 11 1
      core/shared/utils/bh_log.h
  44. 1 1
      core/shared/utils/runtime_timer.h
  45. 16 14
      doc/build_wamr.md
  46. 228 0
      doc/multi_module.md
  47. BIN
      doc/pics/multi_module_pic1.png
  48. 8 0
      product-mini/platforms/linux/CMakeLists.txt
  49. 102 32
      product-mini/platforms/linux/main.c
  50. 51 0
      samples/multi-module/CMakeLists.txt
  51. 144 0
      samples/multi-module/src/main.c
  52. 41 0
      samples/multi-module/wasm-apps/CMakeLists.txt
  53. 5 0
      samples/multi-module/wasm-apps/mA.c
  54. 16 0
      samples/multi-module/wasm-apps/mB.c
  55. 25 0
      samples/multi-module/wasm-apps/mC.c
  56. 1 0
      wamr-compiler/CMakeLists.txt
  57. 4 0
      wamr-compiler/main.c

+ 48 - 15
_clang-format → .clang-format

@@ -1,13 +1,47 @@
----
-BasedOnStyle: Mozilla
-IndentWidth: 4
+RawStringFormats:
+  - Language:        Cpp
+    Delimiters:
+      - c
+      - C
+      - cc
+      - CC
+      - cpp
+      - Cpp
+      - CPP
+      - 'c++'
+      - 'C++'
+      - h
+      - hpp
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+  - Language:        TextProto
+    Delimiters:
+      - pb
+      - PB
+      - proto
+      - PROTO
+    EnclosingFunctions:
+      - EqualsProto
+      - EquivToProto
+      - PARSE_PARTIAL_TEXT_PROTO
+      - PARSE_TEST_PROTO
+      - PARSE_TEXT_PROTO
+      - ParseTextOrDie
+      - ParseTextProtoOrDie
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
 
----
 Language: Cpp
+BasedOnStyle: Mozilla
+IndentWidth: 4
+AlignAfterOpenBracket: Align
+AllowAllArgumentsOnNextLine: false
 AlignConsecutiveMacros: true
 AllowShortBlocksOnASingleLine: true
+AlwaysBreakAfterReturnType: All
 BinPackArguments: true
-BinPackParameters: true
+BinPackParameters: false
+BreakBeforeBinaryOperators: NonAssignment
 BreakBeforeBraces: Custom
 BraceWrapping:
   AfterCaseLabel: false
@@ -17,16 +51,17 @@ BraceWrapping:
   AfterFunction: true
   AfterNamespace: false
   AfterObjCDeclaration: false
-  AfterStruct: true
+  AfterStruct: false
   AfterUnion: false
-  AfterExternBlock: true
+  AfterExternBlock: false
   BeforeCatch: false
-  BeforeElse: false
+  BeforeElse: true
   IndentBraces: false
   SplitEmptyFunction: true
   SplitEmptyRecord: false
   SplitEmptyNamespace: true
 ColumnLimit: 79
+ContinuationIndentWidth: 2
 DerivePointerAlignment: false
 IncludeBlocks: Regroup
 IncludeCategories:
@@ -36,20 +71,22 @@ IncludeCategories:
     Priority: 1
   - Regex: ".*"
     Priority: 3
+IndentPPDirectives: None
+KeepEmptyLinesAtTheStartOfBlocks: false
+NamespaceIndentation: None
 PointerAlignment: Right
 ReflowComments: false
-Standard: Cpp03
+Standard: Auto
 StatementMacros:
   - Q_UNUSED
   - QT_REQUIRE_VERSION
+
 # AccessModifierOffset: -2
-# AlignAfterOpenBracket: Align
 # AlignConsecutiveAssignments: false
 # AlignConsecutiveDeclarations: false
 # AlignEscapedNewlines: Right
 # AlignOperands:   true
 # AlignTrailingComments: true
-# AllowAllArgumentsOnNextLine: true
 # AllowAllConstructorInitializersOnNextLine: true
 # AllowAllParametersOfDeclarationOnNextLine: false
 # AllowShortCaseLabelsOnASingleLine: false
@@ -61,7 +98,6 @@ StatementMacros:
 # AlwaysBreakAfterReturnType: TopLevel
 # AlwaysBreakBeforeMultilineStrings: false
 # AlwaysBreakTemplateDeclarations: Yes
-# BreakBeforeBinaryOperators: None
 # BreakBeforeInheritanceComma: false
 # BreakInheritanceList: BeforeComma
 # BreakBeforeTernaryOperators: true
@@ -73,7 +109,6 @@ StatementMacros:
 # CompactNamespaces: false
 # ConstructorInitializerAllOnOneLineOrOnePerLine: false
 # ConstructorInitializerIndentWidth: 2
-# ContinuationIndentWidth: 2
 # Cpp11BracedListStyle: false
 # DisableFormat:   false
 # ExperimentalAutoDetectBinPacking: false
@@ -84,7 +119,6 @@ StatementMacros:
 #   - BOOST_FOREACH
 # IncludeIsMainRegex: '(Test)?$'
 # IndentCaseLabels: true
-# IndentPPDirectives: None
 # IndentWrappedFunctionNames: false
 # JavaScriptQuotes: Leave
 # JavaScriptWrapImports: true
@@ -92,7 +126,6 @@ StatementMacros:
 # MacroBlockBegin: ''
 # MacroBlockEnd:   ''
 # MaxEmptyLinesToKeep: 1
-# NamespaceIndentation: None
 # ObjCBinPackProtocolList: Auto
 # ObjCBlockIndentWidth: 2
 # ObjCSpaceAfterProperty: true

+ 3 - 0
.gitignore

@@ -3,8 +3,11 @@
 core/deps/lv_drivers
 core/deps/llvm
 core/deps/lvgl
+core/deps/tlsf
 core/shared/mem-alloc/tlsf
+core/app-framework/wgl
 
 wamr-sdk/out/
 wamr-sdk/runtime/build_runtime_sdk/
 test-tools/host-tool/bin/
+product-mini/app-samples/hello-world/test.wasm

+ 7 - 1
README.md

@@ -22,11 +22,17 @@ iwasm VM core
 
 - 100% compliant to the W3C WASM MVP
 - Small runtime binary size (85K for interpreter and 50K for AoT) and low memory usage
-- Near to native speed by AoT 
+- Near to native speed by AoT
 - Self-implemented module loader enables AoT working cross Linux, SGX and MCU systems
 - Choices of WASM application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc
 - [Embeddable with the supporting C API's](./doc/embed_wamr.md)
 - [The mechanism for exporting native API's to WASM applications](./doc/export_native_api.md)
+- [Multiple modules as dependencies](./doc/multi_module.md)
+
+### post-MVP features
+- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions)
+- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops)
+- [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations)
 
 ### Performance and memory usage
 The WAMR performance, footprint and memory usage data are available at the [performance](../../wiki/Performance) wiki page.

+ 1 - 0
assembly-script/.gitignore

@@ -0,0 +1 @@
+/node_modules

+ 13 - 1
build-scripts/config_common.cmake

@@ -123,8 +123,20 @@ else ()
   add_definitions (-DWASM_ENABLE_FAST_INTERP=0)
   message ("     Fast interpreter disabled")
 endif ()
-
+if (WAMR_BUILD_MULTI_MODULE EQUAL 1)
+  add_definitions (-DWASM_ENABLE_MULTI_MODULE=1)
+  message ("     Multiple modules enabled")
+else ()
+  add_definitions (-DWASM_ENABLE_MULTI_MODULE=0)
+  message ("     Multiple modules disabled")
+endif ()
 if (WAMR_BUILD_SPEC_TEST EQUAL 1)
   add_definitions (-DWASM_ENABLE_SPEC_TEST=1)
   message ("     spec test compatible mode is on")
 endif()
+if (WAMR_BUILD_BULK_MEMORY EQUAL 1)
+  add_definitions (-DWASM_ENABLE_BULK_MEMORY=1)
+  message ("     Bulk memory feature enabled")
+else ()
+  add_definitions (-DWASM_ENABLE_BULK_MEMORY=0)
+endif()

+ 1 - 1
core/app-framework/app-native-shared/bi-inc/attr_container.h

@@ -6,7 +6,7 @@
 #ifndef _ATTR_CONTAINER_H_
 #define _ATTR_CONTAINER_H_
 
-#include <inttypes.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

+ 0 - 5
core/app-framework/app-native-shared/restful_utils.c

@@ -71,23 +71,18 @@ void free_req_resp_packet(char * packet)
 request_t * unpack_request(char * packet, int size, request_t * request)
 {
     if (*packet != REQUES_PACKET_VER) {
-        printf("version fail\n");
         return NULL;
     }
     if (size < REQUEST_PACKET_FIX_PART_LEN) {
-        printf("size error: %d\n", size);
         return NULL;
     }
     uint16 url_len = ntohs(*((uint16*) (packet + 12)));
     uint32 payload_len = ntohl(*((uint32*) (packet + 14)));
 
     if (size != ( REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) {
-        printf("size error: %d, expect: %d\n", size,
-        REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len);
         return NULL;
     }
     if (*(packet + REQUEST_PACKET_FIX_PART_LEN + url_len - 1) != 0) {
-        printf("url not end with 0\n");
         return NULL;
     }
 

+ 3 - 3
core/app-mgr/app-manager/app_manager_host.c

@@ -246,7 +246,7 @@ bool app_manager_host_init(host_interface *interface)
     return true;
 }
 
-int app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size)
+int app_manager_host_send_msg(int msg_type, const char *buf, int size)
 {
     /* send an IMRT LINK message contains the buf as payload */
     if (host_commu.send != NULL) {
@@ -276,10 +276,10 @@ int app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size)
         n = host_commu.send(NULL, buf, size);
         os_mutex_unlock(&host_lock);
 
-        printf("sent %d bytes to host\n", n);
+        app_manager_printf("sent %d bytes to host\n", n);
         return n;
     } else {
-        printf("no send api provided\n");
+        app_manager_printf("no send api provided\n");
     }
     return 0;
 }

+ 6 - 6
core/app-mgr/app-manager/module_wasm_app.c

@@ -445,7 +445,7 @@ wasm_app_routine(void *arg)
                                         0, NULL)) {
                 const char *exception = wasm_runtime_get_exception(inst);
                 bh_assert(exception);
-                printf("Got exception running wasi start function: %s\n",
+                app_manager_printf("Got exception running wasi start function: %s\n",
                         exception);
                 wasm_runtime_clear_exception(inst);
                 goto fail1;
@@ -467,7 +467,7 @@ wasm_app_routine(void *arg)
                                 0, NULL)) {
         const char *exception = wasm_runtime_get_exception(inst);
         bh_assert(exception);
-        printf("Got exception running WASM code: %s\n",
+        app_manager_printf("Got exception running WASM code: %s\n",
                exception);
         wasm_runtime_clear_exception(inst);
         /* call on_destroy() in case some resources are opened in on_init()
@@ -644,7 +644,7 @@ wasm_app_module_install(request_t * msg)
             if (!module) {
                 SEND_ERR_RESPONSE(msg->mid,
                                   "Install WASM app failed: load WASM file failed.");
-                printf("error: %s\n", err);
+                app_manager_printf("error: %s\n", err);
                 destroy_all_aot_sections(aot_file->sections);
                 return false;
             }
@@ -674,7 +674,7 @@ wasm_app_module_install(request_t * msg)
             if (!inst) {
                 SEND_ERR_RESPONSE(msg->mid,
                                   "Install WASM app failed: instantiate wasm runtime failed.");
-                printf("error: %s\n", err);
+                app_manager_printf("error: %s\n", err);
                 wasm_runtime_unload(module);
                 destroy_all_aot_sections(aot_file->sections);
                 return false;
@@ -713,7 +713,7 @@ wasm_app_module_install(request_t * msg)
             if (!module) {
                 SEND_ERR_RESPONSE(msg->mid,
                                   "Install WASM app failed: load WASM file failed.");
-                printf("error: %s\n", err);
+                app_manager_printf("error: %s\n", err);
                 destroy_all_wasm_sections(bytecode_file->sections);
                 return false;
             }
@@ -744,7 +744,7 @@ wasm_app_module_install(request_t * msg)
             if (!inst) {
                 SEND_ERR_RESPONSE(msg->mid,
                                   "Install WASM app failed: instantiate wasm runtime failed.");
-                printf("error: %s\n", err);
+                app_manager_printf("error: %s\n", err);
                 wasm_runtime_unload(module);
                 destroy_all_wasm_sections(bytecode_file->sections);
                 return false;

+ 1 - 1
core/app-mgr/app-mgr-shared/app_manager_export.h

@@ -285,7 +285,7 @@ bool
 bh_applet_check_permission(const char *perm);
 
 int
-app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size);
+app_manager_host_send_msg(int msg_type, const char *buf, int size);
 
 #ifdef __cplusplus
 } /* end of extern "C" */

+ 10 - 0
core/config.h

@@ -94,6 +94,11 @@ enum {
 #define WASM_ENABLE_APP_FRAMEWORK 0
 #endif
 
+/* Bulk memory operation */
+#ifndef WASM_ENABLE_BULK_MEMORY
+#define WASM_ENABLE_BULK_MEMORY 0
+#endif
+
 /* WASM log system */
 #ifndef WASM_ENABLE_LOG
 #define WASM_ENABLE_LOG 1
@@ -120,6 +125,11 @@ enum {
 #define WASM_ENABLE_OPCODE_COUNTER 0
 #endif
 
+/* Support a module with dependency, other modules */
+#ifndef WASM_ENABLE_MULTI_MODULE
+#define WASM_ENABLE_MULTI_MODULE 0
+#endif
+
 /* Heap and stack profiling */
 #define BH_ENABLE_MEMORY_PROFILING 0
 

+ 12 - 4
core/iwasm/aot/aot_loader.c

@@ -363,6 +363,11 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
     for (i = 0; i < module->mem_init_data_count; i++) {
         uint32 init_expr_type, byte_count;
         uint64 init_expr_value;
+        uint32 is_passive;
+        uint32 memory_index;
+
+        read_uint32(buf, buf_end, is_passive);
+        read_uint32(buf, buf_end, memory_index);
         read_uint32(buf, buf_end, init_expr_type);
         read_uint64(buf, buf_end, init_expr_value);
         read_uint32(buf, buf_end, byte_count);
@@ -375,6 +380,11 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
             return false;
         }
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+        /* is_passive and memory_index is only used in bulk memory mode */
+        data_list[i]->is_passive = (bool)is_passive;
+        data_list[i]->memory_index = memory_index;
+#endif
         data_list[i]->offset.init_expr_type = (uint8)init_expr_type;
         data_list[i]->offset.u.i64 = (int64)init_expr_value;
         data_list[i]->byte_count = byte_count;
@@ -773,8 +783,7 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end,
         read_uint16(buf, buf_end, import_funcs[i].func_type_index);
         if (import_funcs[i].func_type_index >= module->func_type_count) {
             set_error_buf(error_buf, error_buf_size,
-                          "AOT module load failed: "
-                          "invalid function type index.");
+                          "AOT module load failed: unknown type.");
             return false;
         }
         import_funcs[i].func_type = module->func_types[import_funcs[i].func_type_index];
@@ -1067,8 +1076,7 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
         read_uint32(p, p_end, module->func_type_indexes[i]);
         if (module->func_type_indexes[i] >= module->func_type_count) {
             set_error_buf(error_buf, error_buf_size,
-                          "AOT module load failed: "
-                          "invalid function type index.");
+                          "AOT module load failed: unknown type.");
             return false;
         }
     }

+ 25 - 0
core/iwasm/aot/aot_reloc.h

@@ -12,6 +12,30 @@ typedef struct {
 
 #define REG_SYM(symbol) { #symbol, (void*)symbol }
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+#define REG_COMMON_SYMBOLS                \
+    REG_SYM(aot_set_exception_with_id),   \
+    REG_SYM(aot_invoke_native),           \
+    REG_SYM(aot_call_indirect),           \
+    REG_SYM(wasm_runtime_enlarge_memory), \
+    REG_SYM(wasm_runtime_set_exception),  \
+    REG_SYM(fmin),                        \
+    REG_SYM(fminf),                       \
+    REG_SYM(fmax),                        \
+    REG_SYM(fmaxf),                       \
+    REG_SYM(ceil),                        \
+    REG_SYM(ceilf),                       \
+    REG_SYM(floor),                       \
+    REG_SYM(floorf),                      \
+    REG_SYM(trunc),                       \
+    REG_SYM(truncf),                      \
+    REG_SYM(rint),                        \
+    REG_SYM(rintf),                       \
+    REG_SYM(memset),                      \
+    REG_SYM(memmove),                     \
+    REG_SYM(aot_memory_init),             \
+    REG_SYM(aot_data_drop)
+#else
 #define REG_COMMON_SYMBOLS                \
     REG_SYM(aot_set_exception_with_id),   \
     REG_SYM(aot_invoke_native),           \
@@ -30,6 +54,7 @@ typedef struct {
     REG_SYM(truncf),                      \
     REG_SYM(rint),                        \
     REG_SYM(rintf)
+#endif
 
 #define CHECK_RELOC_OFFSET(data_size) do {                                  \
     if (!check_reloc_offset(target_section_size, reloc_offset, data_size,   \

+ 168 - 76
core/iwasm/aot/aot_runtime.c

@@ -68,42 +68,62 @@ table_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
     uint32 i, global_index, global_data_offset, base_offset, length;
     AOTTableInitData *table_seg;
 
-    if (module->table_init_data_count > 0) {
-        for (i = 0; i < module->table_init_data_count; i++) {
-            table_seg = module->table_init_data_list[i];
-            bh_assert(table_seg->offset.init_expr_type ==
-                            INIT_EXPR_TYPE_I32_CONST
-                      || table_seg->offset.init_expr_type ==
-                            INIT_EXPR_TYPE_GET_GLOBAL);
-
-            /* Resolve table data base offset */
-            if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
-                global_index = table_seg->offset.u.global_index;
-                bh_assert(global_index <
-                          module->import_global_count + module->global_count);
-                /* TODO: && globals[table_seg->offset.u.global_index].type ==
-                           VALUE_TYPE_I32*/
-                if (global_index < module->import_global_count)
-                    global_data_offset =
-                        module->import_globals[global_index].data_offset;
-                else
-                    global_data_offset =
-                        module->globals[global_index - module->import_global_count]
-                                .data_offset;
-
-                base_offset = *(uint32*)
-                    ((uint8*)module_inst->global_data.ptr + global_data_offset);
-            }
+    for (i = 0; i < module->table_init_data_count; i++) {
+        table_seg = module->table_init_data_list[i];
+        bh_assert(table_seg->offset.init_expr_type ==
+                        INIT_EXPR_TYPE_I32_CONST
+                    || table_seg->offset.init_expr_type ==
+                        INIT_EXPR_TYPE_GET_GLOBAL);
+
+        /* Resolve table data base offset */
+        if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
+            global_index = table_seg->offset.u.global_index;
+            bh_assert(global_index <
+                        module->import_global_count + module->global_count);
+            /* TODO: && globals[table_seg->offset.u.global_index].type ==
+                        VALUE_TYPE_I32*/
+            if (global_index < module->import_global_count)
+                global_data_offset =
+                    module->import_globals[global_index].data_offset;
             else
-                base_offset = (uint32)table_seg->offset.u.i32;
-
-            /* Copy table data */
-            length = table_seg->func_index_count;
-            if (base_offset < module_inst->table_size) {
-                memcpy((uint32*)module_inst->table_data.ptr + base_offset,
-                       table_seg->func_indexes, length * sizeof(uint32));
-            }
+                global_data_offset =
+                    module->globals[global_index - module->import_global_count]
+                            .data_offset;
+
+            base_offset = *(uint32*)
+                ((uint8*)module_inst->global_data.ptr + global_data_offset);
+        }
+        else
+            base_offset = (uint32)table_seg->offset.u.i32;
+
+        /* Copy table data */
+        bh_assert(module_inst->table_data.ptr);
+        /* base_offset only since length might negative */
+        if (base_offset > module_inst->table_size) {
+            LOG_DEBUG("base_offset(%d) > table_size(%d)", base_offset,
+                      module_inst->table_size);
+            set_error_buf(error_buf, error_buf_size,
+                          "elements segment does not fit");
+            return false;
+        }
+
+        /* base_offset + length(could be zero) */
+        length = table_seg->func_index_count;
+        if (base_offset + length > module_inst->table_size) {
+            LOG_DEBUG("base_offset(%d) + length(%d) > table_size(%d)",
+                      base_offset, length, module_inst->table_size);
+            set_error_buf(error_buf, error_buf_size,
+                          "elements segment does not fit");
+            return false;
         }
+
+        /**
+         * Check function index in the current module inst for now.
+         * will check the linked table inst owner in future
+         */
+        memcpy((uint32 *)module_inst->table_data.ptr + base_offset,
+               table_seg->func_indexes,
+               length * sizeof(uint32));
     }
 
     return true;
@@ -169,50 +189,64 @@ memory_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
         module_inst->mem_bound_check_8bytes = module_inst->total_mem_size - 8;
     }
 
-    if (module->mem_init_page_count > 0) {
-        for (i = 0; i < module->mem_init_data_count; i++) {
-            data_seg = module->mem_init_data_list[i];
-            bh_assert(data_seg->offset.init_expr_type ==
-                            INIT_EXPR_TYPE_I32_CONST
-                      || data_seg->offset.init_expr_type ==
-                            INIT_EXPR_TYPE_GET_GLOBAL);
-
-            /* Resolve memory data base offset */
-            if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
-                global_index = data_seg->offset.u.global_index;
-                bh_assert(global_index <
-                          module->import_global_count + module->global_count);
-                /* TODO: && globals[data_seg->offset.u.global_index].type ==
-                           VALUE_TYPE_I32*/
-                if (global_index < module->import_global_count)
-                    global_data_offset =
-                        module->import_globals[global_index].data_offset;
-                else
-                    global_data_offset =
-                        module->globals[global_index - module->import_global_count]
-                                .data_offset;
-
-                base_offset = *(uint32*)
-                    ((uint8*)module_inst->global_data.ptr + global_data_offset);
-            }
+    for (i = 0; i < module->mem_init_data_count; i++) {
+        data_seg = module->mem_init_data_list[i];
+#if WASM_ENABLE_BULK_MEMORY != 0
+        if (data_seg->is_passive)
+            continue;
+#endif
+
+        bh_assert(data_seg->offset.init_expr_type ==
+                        INIT_EXPR_TYPE_I32_CONST
+                    || data_seg->offset.init_expr_type ==
+                        INIT_EXPR_TYPE_GET_GLOBAL);
+
+        /* Resolve memory data base offset */
+        if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
+            global_index = data_seg->offset.u.global_index;
+            bh_assert(global_index <
+                        module->import_global_count + module->global_count);
+            /* TODO: && globals[data_seg->offset.u.global_index].type ==
+                        VALUE_TYPE_I32*/
+            if (global_index < module->import_global_count)
+                global_data_offset =
+                    module->import_globals[global_index].data_offset;
             else
-                base_offset = (uint32)data_seg->offset.u.i32;
-
-            length = data_seg->byte_count;
-
-            /* Check memory data */
-            if (length > 0
-                && (base_offset >= module_inst->memory_data_size
-                    || base_offset + length > module_inst->memory_data_size)) {
-                set_error_buf(error_buf, error_buf_size,
-                             "AOT module instantiate failed: data segment out of range.");
-                goto fail2;
-            }
-
-            /* Copy memory data */
-            memcpy((uint8*)module_inst->memory_data.ptr + base_offset,
-                   data_seg->bytes, length);
+                global_data_offset =
+                    module->globals[global_index - module->import_global_count]
+                            .data_offset;
+
+            base_offset = *(uint32*)
+                ((uint8*)module_inst->global_data.ptr + global_data_offset);
+        } else {
+            base_offset = (uint32)data_seg->offset.u.i32;
+        }
+
+        /* Copy memory data */
+        bh_assert(module_inst->memory_data.ptr);
+
+        /* Check memory data */
+        /* check offset since length might negative */
+        if (base_offset > module_inst->memory_data_size) {
+            LOG_DEBUG("base_offset(%d) > memory_data_size(%d)", base_offset,
+                      module_inst->memory_data_size);
+            set_error_buf(error_buf, error_buf_size,
+                          "data segment does not fit");
+            goto fail2;
+        }
+
+        /* check offset + length(could be zero) */
+        length = data_seg->byte_count;
+        if (base_offset + length > module_inst->memory_data_size) {
+            LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%d)",
+                      base_offset, length, module_inst->memory_data_size);
+            set_error_buf(error_buf, error_buf_size,
+                          "data segment does not fit");
+            goto fail2;
         }
+
+        memcpy((uint8*)module_inst->memory_data.ptr + base_offset,
+                data_seg->bytes, length);
     }
 
     return true;
@@ -952,3 +986,61 @@ aot_call_indirect(WASMExecEnv *exec_env,
                                       func_type, signature, attachment,
                                       argv, argc, argv);
 }
+
+#if WASM_ENABLE_BULK_MEMORY != 0
+bool
+aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index,
+                uint32 offset, uint32 len, uint32 dst)
+{
+    AOTModule *aot_module;
+    uint8 *data = NULL;
+    uint8 *maddr;
+    uint64 seg_len = 0;
+
+    aot_module = (AOTModule *)module_inst->aot_module.ptr;
+    if (aot_module->is_jit_mode) {
+#if WASM_ENABLE_JIT != 0
+        seg_len = aot_module->wasm_module->data_segments[seg_index]->data_length;
+        data = aot_module->wasm_module->data_segments[seg_index]->data;
+#endif
+    }
+    else {
+        seg_len = aot_module->mem_init_data_list[seg_index]->byte_count;
+        data = aot_module->mem_init_data_list[seg_index]->bytes;
+    }
+
+    if (!aot_validate_app_addr(module_inst, dst, len))
+        return false;
+
+    if ((uint64)offset + (uint64)len > seg_len) {
+        aot_set_exception(module_inst, "out of bounds memory access");
+        return false;
+    }
+
+    maddr = aot_addr_app_to_native(module_inst, dst);
+
+    bh_memcpy_s(maddr, module_inst->memory_data_size - dst,
+                data + offset, len);
+    return true;
+}
+
+bool
+aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index)
+{
+    AOTModule *aot_module = (AOTModule *)(module_inst->aot_module.ptr);
+
+    if (aot_module->is_jit_mode) {
+#if WASM_ENABLE_JIT != 0
+        aot_module->wasm_module->data_segments[seg_index]->data_length = 0;
+        /* Currently we can't free the dropped data segment
+            as they are stored in wasm bytecode */
+#endif
+    }
+    else {
+        aot_module->mem_init_data_list[seg_index]->byte_count = 0;
+        /* Currently we can't free the dropped data segment
+            as the mem_init_data_count is a continuous array */
+    }
+    return true;
+}
+#endif /* WASM_ENABLE_BULK_MEMORY */

+ 9 - 0
core/iwasm/aot/aot_runtime.h

@@ -458,6 +458,15 @@ aot_call_indirect(WASMExecEnv *exec_env,
 uint32
 aot_get_plt_table_size();
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+bool
+aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index,
+                uint32 offset, uint32 len, uint32 dst);
+
+bool
+aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index);
+#endif
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 1 - 1
core/iwasm/common/wasm_native.c

@@ -243,6 +243,7 @@ wasm_native_init()
     if (!wasm_native_register_natives("env",
                                        native_symbols, n_native_symbols))
         return false;
+#endif /* WASM_ENABLE_LIBC_BUILTIN */
 
 #if WASM_ENABLE_SPEC_TEST
     n_native_symbols = get_spectest_export_apis(&native_symbols);
@@ -250,7 +251,6 @@ wasm_native_init()
                                        native_symbols, n_native_symbols))
         return false;
 #endif /* WASM_ENABLE_SPEC_TEST */
-#endif /* WASM_ENABLE_LIBC_BUILTIN */
 
 #if WASM_ENABLE_LIBC_WASI != 0
     n_native_symbols = get_libc_wasi_export_apis(&native_symbols);

+ 538 - 26
core/iwasm/common/wasm_runtime_common.c

@@ -16,6 +16,43 @@
 #include "../aot/aot_runtime.h"
 #endif
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+/*
+ * a safety insurance to prevent
+ * circular depencies leading a stack overflow
+ * try break early
+ */
+typedef struct LoadingModule {
+    bh_list_link l;
+    /* point to a string pool */
+    const char *module_name;
+} LoadingModule;
+
+static bh_list loading_module_list_head;
+static bh_list *const loading_module_list = &loading_module_list_head;
+static korp_mutex loading_module_list_lock;
+
+/*
+ * a list about all exported functions, globals, memories, tables of every
+ * fully loaded module
+ */
+static bh_list registered_module_list_head;
+static bh_list *const registered_module_list = &registered_module_list_head;
+static korp_mutex registered_module_list_lock;
+static void
+wasm_runtime_destroy_registered_module_list();
+#endif /* WASM_ENABLE_MULTI_MODULE */
+
+void
+set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format,
+                ...)
+{
+    va_list args;
+    va_start(args, format);
+    vsnprintf(error_buf, error_buf_size, format, args);
+    va_end(args);
+}
+
 static void
 set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
 {
@@ -34,11 +71,25 @@ wasm_runtime_env_init()
         return false;
     }
 
+#if WASM_ENABLE_MULTI_MODULE
+    if (BHT_OK != os_mutex_init(&registered_module_list_lock)) {
+        wasm_native_destroy();
+        bh_platform_destroy();
+        return false;
+    }
+
+    if (BHT_OK != os_mutex_init(&loading_module_list_lock)) {
+        os_mutex_destroy(&registered_module_list_lock);
+        wasm_native_destroy();
+        bh_platform_destroy();
+        return false;
+    }
+#endif
     return true;
 }
 
 static bool
-wasm_runtime_env_check(WASMExecEnv *exec_env)
+wasm_runtime_exec_env_check(WASMExecEnv *exec_env)
 {
     return !(!exec_env
         || !exec_env->module_inst
@@ -65,8 +116,17 @@ wasm_runtime_init()
 void
 wasm_runtime_destroy()
 {
+    /* runtime env destroy */
+#if WASM_ENABLE_MULTI_MODULE
+    wasm_runtime_destroy_loading_module_list();
+    os_mutex_destroy(&loading_module_list_lock);
+
+    wasm_runtime_destroy_registered_module_list();
+    os_mutex_destroy(&registered_module_list_lock);
+#endif
     wasm_native_destroy();
     bh_platform_destroy();
+
     wasm_runtime_memory_destroy();
 }
 
@@ -105,10 +165,342 @@ get_package_type(const uint8 *buf, uint32 size)
     return Package_Type_Unknown;
 }
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+static module_reader reader;
+static module_destroyer destroyer;
+
+void
+wasm_runtime_set_module_reader(const module_reader reader_cb,
+                               const module_destroyer destroyer_cb)
+{
+    reader = reader_cb;
+    destroyer = destroyer_cb;
+}
+
+module_reader
+wasm_runtime_get_module_reader()
+{
+    return reader;
+}
+
+module_destroyer
+wasm_runtime_get_module_destroyer()
+{
+    return destroyer;
+}
+
+static WASMRegisteredModule *
+wasm_runtime_find_module_registered_by_reference(WASMModuleCommon *module)
+{
+    WASMRegisteredModule *reg_module = NULL;
+
+    os_mutex_lock(&registered_module_list_lock);
+    reg_module = bh_list_first_elem(registered_module_list);
+    while (reg_module && module != reg_module->module) {
+        reg_module = bh_list_elem_next(reg_module);
+    }
+    os_mutex_unlock(&registered_module_list_lock);
+
+    return reg_module;
+}
+
+bool
+wasm_runtime_register_module_internal(const char *module_name,
+                                      WASMModuleCommon *module,
+                                      uint8 *orig_file_buf,
+                                      uint32 orig_file_buf_size,
+                                      char *error_buf,
+                                      uint32_t error_buf_size)
+{
+    WASMRegisteredModule *node = NULL;
+
+    node = wasm_runtime_find_module_registered_by_reference(module);
+    if (node) { /* module has been registered */
+        if (node->module_name) { /* module has name */
+           if (strcmp(node->module_name, module_name)) {
+               /* module has different name */
+               LOG_DEBUG("module(%p) has been registered with name %s",
+                         module, node->module_name);
+               set_error_buf_v(error_buf, error_buf_size,
+                               "can not rename the module");
+               return false;
+           }
+           else {
+               /* module has the same name */
+               LOG_DEBUG("module(%p) has been registered with the same name %s",
+                         module, node->module_name);
+               return true;
+           }
+        }
+        else {
+            /* module has empyt name, reset it */
+            node->module_name = module_name;
+            return true;
+        }
+    }
+
+    /* module hasn't been registered */
+    node = wasm_runtime_malloc(sizeof(WASMRegisteredModule));
+    if (!node) {
+        LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%d",
+                  sizeof(WASMRegisteredModule));
+        set_error_buf_v(error_buf, error_buf_size,
+                        "malloc WASMRegisteredModule failed. SZ=%d",
+                        sizeof(WASMRegisteredModule));
+        return false;
+    }
+
+    /* share the string and the module */
+    node->module_name = module_name;
+    node->module = module;
+    node->orig_file_buf = orig_file_buf;
+    node->orig_file_buf_size = orig_file_buf_size;
+
+    os_mutex_lock(&registered_module_list_lock);
+    bh_list_status ret = bh_list_insert(registered_module_list, node);
+    bh_assert(BH_LIST_SUCCESS == ret);
+    (void)ret;
+    os_mutex_unlock(&registered_module_list_lock);
+    return true;
+}
+
+bool
+wasm_runtime_register_module(const char *module_name, WASMModuleCommon *module,
+                             char *error_buf, uint32_t error_buf_size)
+{
+    if (!error_buf || !error_buf_size) {
+        LOG_ERROR("error buffer is required");
+        return false;
+    }
+
+    if (!module_name || !module) {
+        LOG_DEBUG("module_name and module are required");
+        set_error_buf_v(error_buf, error_buf_size,
+                        "module_name and module are required");
+        return false;
+    }
+
+    if (wasm_runtime_is_built_in_module(module_name)) {
+        LOG_DEBUG("%s is a built-in module name", module_name);
+        set_error_buf(error_buf, error_buf_size,
+                      "can not register as a built-in module");
+        return false;
+    }
+
+    return wasm_runtime_register_module_internal(
+                            module_name, module, NULL, 0,
+                            error_buf, error_buf_size);
+}
+
+void
+wasm_runtime_unregister_module(const WASMModuleCommon *module)
+{
+    WASMRegisteredModule *registered_module = NULL;
+
+    os_mutex_lock(&registered_module_list_lock);
+    registered_module = bh_list_first_elem(registered_module_list);
+    while (registered_module && module != registered_module->module) {
+        registered_module = bh_list_elem_next(registered_module);
+    }
+
+    /* it does not matter if it is not exist. after all, it is gone */
+    if (registered_module) {
+        bh_list_remove(registered_module_list, registered_module);
+        wasm_runtime_free(registered_module);
+    }
+    os_mutex_unlock(&registered_module_list_lock);
+}
+
+WASMModuleCommon *
+wasm_runtime_find_module_registered(const char *module_name)
+{
+    WASMRegisteredModule *module = NULL, *module_next;
+
+    os_mutex_lock(&registered_module_list_lock);
+    module = bh_list_first_elem(registered_module_list);
+    while (module) {
+        module_next = bh_list_elem_next(module);
+        if (module->module_name
+            && !strcmp(module_name, module->module_name)) {
+            break;
+        }
+        module = module_next;
+    }
+    os_mutex_unlock(&registered_module_list_lock);
+
+    return module ? module->module : NULL;
+}
+
+bool
+wasm_runtime_is_module_registered(const char *module_name)
+{
+    return NULL != wasm_runtime_find_module_registered(module_name);
+}
+
+/*
+ * simply destroy all
+ */
+static void
+wasm_runtime_destroy_registered_module_list()
+{
+    WASMRegisteredModule *reg_module = NULL;
+
+    os_mutex_lock(&registered_module_list_lock);
+    reg_module = bh_list_first_elem(registered_module_list);
+    while (reg_module) {
+        WASMRegisteredModule *next_reg_module = bh_list_elem_next(reg_module);
+
+        bh_list_remove(registered_module_list, reg_module);
+
+        /* now, it is time to release every module in the runtime */
+#if WASM_ENABLE_INTERP != 0
+        if (reg_module->module->module_type == Wasm_Module_Bytecode)
+            wasm_unload((WASMModule *)reg_module->module);
+#endif
+#if WASM_ENABLE_AOT != 0
+        if (reg_module->module->module_type == Wasm_Module_AoT)
+            aot_unload((AOTModule *)reg_module->module);
+#endif
+
+        /* destroy the file buffer */
+        if (destroyer && reg_module->orig_file_buf) {
+            destroyer(reg_module->orig_file_buf,
+                      reg_module->orig_file_buf_size);
+            reg_module->orig_file_buf = NULL;
+            reg_module->orig_file_buf_size = 0;
+        }
+
+        wasm_runtime_free(reg_module);
+        reg_module = next_reg_module;
+    }
+    os_mutex_unlock(&registered_module_list_lock);
+}
+
+bool
+wasm_runtime_add_loading_module(const char *module_name, char *error_buf,
+                                uint32 error_buf_size)
+{
+    LOG_DEBUG("add %s into a loading list", module_name);
+    LoadingModule *loadingModule = wasm_runtime_malloc(sizeof(LoadingModule));
+
+    if (!loadingModule) {
+        set_error_buf_v(error_buf, error_buf_size,
+                        "malloc LoadingModule failed. SZ=%d",
+                        sizeof(LoadingModule));
+        return false;
+    }
+
+    /* share the incoming string */
+    loadingModule->module_name = module_name;
+
+    os_mutex_lock(&loading_module_list_lock);
+    bh_list_status ret = bh_list_insert(loading_module_list, loadingModule);
+    bh_assert(BH_LIST_SUCCESS == ret);
+    (void)ret;
+    os_mutex_unlock(&loading_module_list_lock);
+    return true;
+}
+
+void
+wasm_runtime_delete_loading_module(const char *module_name)
+{
+    LOG_DEBUG("delete %s from a loading list", module_name);
+
+    LoadingModule *module = NULL;
+
+    os_mutex_lock(&loading_module_list_lock);
+    module = bh_list_first_elem(loading_module_list);
+    while (module && strcmp(module->module_name, module_name)) {
+        module = bh_list_elem_next(module);
+    }
+
+    /* it does not matter if it is not exist. after all, it is gone */
+    if (module) {
+        bh_list_remove(loading_module_list, module);
+        wasm_runtime_free(module);
+    }
+    os_mutex_unlock(&loading_module_list_lock);
+}
+
+bool
+wasm_runtime_is_loading_module(const char *module_name)
+{
+    LOG_DEBUG("find %s in a loading list", module_name);
+
+    LoadingModule *module = NULL;
+
+    os_mutex_lock(&loading_module_list_lock);
+    module = bh_list_first_elem(loading_module_list);
+    while (module && strcmp(module_name, module->module_name)) {
+        module = bh_list_elem_next(module);
+    }
+    os_mutex_unlock(&loading_module_list_lock);
+
+    return module != NULL;
+}
+
+void
+wasm_runtime_destroy_loading_module_list()
+{
+    LoadingModule *module = NULL;
+
+    os_mutex_lock(&loading_module_list_lock);
+    module = bh_list_first_elem(loading_module_list);
+    while (module) {
+        LoadingModule *next_module = bh_list_elem_next(module);
+
+        bh_list_remove(loading_module_list, module);
+        /*
+         * will not free the module_name since it is
+         * shared one of the const string pool
+         */
+        wasm_runtime_free(module);
+
+        module = next_module;
+    }
+
+    os_mutex_unlock(&loading_module_list_lock);
+}
+#endif /* WASM_ENABLE_MULTI_MODULE */
+
+bool
+wasm_runtime_is_built_in_module(const char *module_name)
+{
+    return (!strcmp("env", module_name)
+            || !strcmp("wasi_unstable", module_name)
+            || !strcmp("wasi_snapshot_preview1", module_name)
+            || !strcmp("spectest", module_name)
+            );
+}
+
+static WASMModuleCommon *
+register_module_with_null_name(WASMModuleCommon *module_common,
+                               char *error_buf, uint32 error_buf_size)
+{
+#if WASM_ENABLE_MULTI_MODULE != 0
+    if (module_common) {
+        if (!wasm_runtime_register_module_internal(NULL, module_common,
+                                                   NULL, 0,
+                                                   error_buf,
+                                                   error_buf_size)) {
+            wasm_runtime_unload(module_common);
+            return NULL;
+        }
+        return module_common;
+    }
+    else
+        return NULL;
+#else
+    return module_common;
+#endif
+}
+
 WASMModuleCommon *
 wasm_runtime_load(const uint8 *buf, uint32 size,
                   char *error_buf, uint32 error_buf_size)
 {
+    WASMModuleCommon *module_common = NULL;
+
     if (get_package_type(buf, size) == Wasm_Module_Bytecode) {
 #if WASM_ENABLE_AOT != 0 && WASM_ENABLE_JIT != 0
         AOTModule *aot_module;
@@ -121,18 +513,24 @@ wasm_runtime_load(const uint8 *buf, uint32 size,
             wasm_unload(module);
             return NULL;
         }
-        return (WASMModuleCommon*)aot_module;
+
+        module_common = (WASMModuleCommon*)aot_module;
+        return register_module_with_null_name(module_common,
+                                              error_buf, error_buf_size);
 #elif WASM_ENABLE_INTERP != 0
-        return (WASMModuleCommon*)
+        module_common = (WASMModuleCommon*)
                wasm_load(buf, size, error_buf, error_buf_size);
+        return register_module_with_null_name(module_common,
+                                              error_buf, error_buf_size);
 #endif
     }
     else if (get_package_type(buf, size) == Wasm_Module_AoT) {
 #if WASM_ENABLE_AOT != 0
-        return (WASMModuleCommon*)
+        module_common = (WASMModuleCommon*)
                aot_load_from_aot_file(buf, size, error_buf, error_buf_size);
-
-#endif /* end of WASM_ENABLE_AOT */
+        return register_module_with_null_name(module_common,
+                                              error_buf, error_buf_size);
+#endif
     }
 
     if (size < 4)
@@ -148,17 +546,25 @@ WASMModuleCommon *
 wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot,
                                 char *error_buf, uint32_t error_buf_size)
 {
+    WASMModuleCommon *module_common;
+
 #if WASM_ENABLE_INTERP != 0
-    if (!is_aot)
-        return (WASMModuleCommon*)
+    if (!is_aot) {
+        module_common = (WASMModuleCommon*)
                wasm_load_from_sections(section_list,
                                        error_buf, error_buf_size);
+        return register_module_with_null_name(module_common,
+                                              error_buf, error_buf_size);
+    }
 #endif
 #if WASM_ENABLE_AOT != 0
-    if (is_aot)
-        return (WASMModuleCommon*)
+    if (is_aot) {
+        module_common = (WASMModuleCommon*)
                aot_load_from_sections(section_list,
                                       error_buf, error_buf_size);
+        return register_module_with_null_name(module_common,
+                                              error_buf, error_buf_size);
+    }
 #endif
 
     set_error_buf(error_buf, error_buf_size,
@@ -169,12 +575,21 @@ wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot,
 void
 wasm_runtime_unload(WASMModuleCommon *module)
 {
+#if WASM_ENABLE_MULTI_MODULE != 0
+    /**
+     * since we will unload and free all module when runtime_destroy()
+     * we don't want users to unwillingly disrupt it
+     */
+    return;
+#endif
+
 #if WASM_ENABLE_INTERP != 0
     if (module->module_type == Wasm_Module_Bytecode) {
         wasm_unload((WASMModule*)module);
         return;
     }
 #endif
+
 #if WASM_ENABLE_AOT != 0
     if (module->module_type == Wasm_Module_AoT) {
         aot_unload((AOTModule*)module);
@@ -285,9 +700,9 @@ wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst,
 bool
 wasm_runtime_call_wasm(WASMExecEnv *exec_env,
                        WASMFunctionInstanceCommon *function,
-                       unsigned argc, uint32 argv[])
+                       uint32 argc, uint32 argv[])
 {
-    if (!wasm_runtime_env_check(exec_env)) {
+    if (!wasm_runtime_exec_env_check(exec_env)) {
         LOG_ERROR("Invalid exec env stack info.");
         return false;
     }
@@ -313,7 +728,7 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env,
 bool
 wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst,
                                            WASMFunctionInstanceCommon *function,
-                                           unsigned argc, uint32 argv[])
+                                           uint32 argc, uint32 argv[])
 {
 #if WASM_ENABLE_INTERP != 0
     if (module_inst->module_type == Wasm_Module_Bytecode)
@@ -1077,7 +1492,7 @@ check_main_func_type(const WASMType *type)
 
 bool
 wasm_application_execute_main(WASMModuleInstanceCommon *module_inst,
-                              int argc, char *argv[])
+                              int32 argc, char *argv[])
 {
     WASMFunctionInstanceCommon *func;
     WASMType *func_type = NULL;
@@ -1165,6 +1580,54 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst,
                                                       argc1, argv1);
 }
 
+
+
+#if WASM_ENABLE_MULTI_MODULE != 0
+static WASMModuleInstance *
+get_sub_module_inst(const WASMModuleInstance *parent_module_inst,
+                    const char *sub_module_name)
+{
+    WASMSubModInstNode *node =
+      bh_list_first_elem(parent_module_inst->sub_module_inst_list);
+
+    while (node && strcmp(node->module_name, sub_module_name)) {
+        node = bh_list_elem_next(node);
+    }
+    return node ? node->module_inst : NULL;
+}
+
+static bool
+parse_function_name(char *orig_function_name, char **p_module_name,
+                    char **p_function_name)
+{
+    if (orig_function_name[0] != '$') {
+        *p_module_name = NULL;
+        *p_function_name = orig_function_name;
+        return true;
+    }
+
+    /**
+     * $module_name$function_name\0
+     *  ===>
+     * module_name\0function_name\0
+     *  ===>
+     * module_name
+     * function_name
+     */
+    char *p1 = orig_function_name;
+    char *p2 = strchr(p1 + 1, '$');
+    if (!p2) {
+        LOG_DEBUG("can not parse the incoming function name");
+        return false;
+    }
+
+    *p_module_name = p1 + 1;
+    *p2 = '\0';
+    *p_function_name = p2 + 1;
+    return strlen(*p_module_name) && strlen(*p_function_name);
+}
+#endif
+
 /**
  * Implementation of wasm_application_execute_func()
  */
@@ -1173,32 +1636,76 @@ static WASMFunctionInstanceCommon*
 resolve_function(const WASMModuleInstanceCommon *module_inst,
                  const char *name)
 {
-    uint32 i;
+    uint32 i = 0;
+    WASMFunctionInstanceCommon *ret = NULL;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMModuleInstance *sub_module_inst = NULL;
+    char *orig_name = NULL;
+    char *sub_module_name = NULL;
+    char *function_name = NULL;
+    uint32 length = strlen(name) + 1;
+
+    orig_name = wasm_runtime_malloc(sizeof(char) * length);
+    if (!orig_name) {
+        return NULL;
+    }
+
+    memset(orig_name, 0, sizeof(char) * length);
+    strncpy(orig_name, name, length);
+
+    if (!parse_function_name(orig_name, &sub_module_name, &function_name)) {
+        goto LEAVE;
+    }
+
+    LOG_DEBUG("%s -> %s and %s", name, sub_module_name, function_name);
+
+    if (sub_module_name) {
+        sub_module_inst = get_sub_module_inst(
+          (WASMModuleInstance *)module_inst, sub_module_name);
+        if (!sub_module_inst) {
+            LOG_DEBUG("can not find a sub module named %s", sub_module_name);
+            goto LEAVE;
+        }
+    }
+#else
+    const char *function_name = name;
+#endif
 
 #if WASM_ENABLE_INTERP != 0
     if (module_inst->module_type == Wasm_Module_Bytecode) {
         WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst;
+
+#if WASM_ENABLE_MULTI_MODULE != 0
+        wasm_inst = sub_module_inst ? sub_module_inst : wasm_inst;
+#endif /* WASM_ENABLE_MULTI_MODULE */
+
         for (i = 0; i < wasm_inst->export_func_count; i++) {
-           if (!strcmp(wasm_inst->export_functions[i].name, name))
-                return wasm_inst->export_functions[i].function;
+           if (!strcmp(wasm_inst->export_functions[i].name, function_name)) {
+                ret = wasm_inst->export_functions[i].function;
+                break;
+           }
         }
-        return NULL;
     }
-#endif
+#endif /* WASM_ENABLE_INTERP */
 
 #if WASM_ENABLE_AOT != 0
     if (module_inst->module_type == Wasm_Module_AoT) {
         AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst;
         AOTModule *module = (AOTModule*)aot_inst->aot_module.ptr;
         for (i = 0; i < module->export_func_count; i++) {
-            if (!strcmp(module->export_funcs[i].func_name, name))
-                return (WASMFunctionInstance*)&module->export_funcs[i];
+            if (!strcmp(module->export_funcs[i].func_name, function_name)) {
+                ret = (WASMFunctionInstance*)&module->export_funcs[i];
+                break;
+            }
         }
-        return NULL;
     }
 #endif
 
-    return NULL;
+#if WASM_ENABLE_MULTI_MODULE != 0
+LEAVE:
+    wasm_runtime_free(orig_name);
+#endif
+    return ret;
 }
 
 union ieee754_float {
@@ -1251,7 +1758,7 @@ static union {
 
 bool
 wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
-                              const char *name, int argc, char *argv[])
+                              const char *name, int32 argc, char *argv[])
 {
     WASMFunctionInstanceCommon *func;
     WASMType *type = NULL;
@@ -1262,6 +1769,7 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
     char buf[128];
 
     bh_assert(argc >= 0);
+    LOG_DEBUG("call a function \"%s\" with %d arguments", name, argc);
     func = resolve_function(module_inst, name);
 
     if (!func) {
@@ -1273,7 +1781,11 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
 #if WASM_ENABLE_INTERP != 0
     if (module_inst->module_type == Wasm_Module_Bytecode) {
         WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)func;
-        if (wasm_func->is_import_func) {
+        if (wasm_func->is_import_func
+#if WASM_ENABLE_MULTI_MODULE != 0
+            && !wasm_func->import_func_inst
+#endif
+        ) {
             snprintf(buf, sizeof(buf), "lookup function %s failed.", name);
             wasm_runtime_set_exception(module_inst, buf);
             goto fail;
@@ -2146,7 +2658,7 @@ wasm_runtime_call_indirect(WASMExecEnv *exec_env,
                            uint32_t element_indices,
                            uint32_t argc, uint32_t argv[])
 {
-    if (!wasm_runtime_env_check(exec_env)) {
+    if (!wasm_runtime_exec_env_check(exec_env)) {
         LOG_ERROR("Invalid exec env stack info.");
         return false;
     }

+ 69 - 11
core/iwasm/common/wasm_runtime_common.h

@@ -21,7 +21,6 @@
 extern "C" {
 #endif
 
-
 typedef struct WASMModuleCommon {
     /* Module type, for module loaded from WASM bytecode binary,
        this field is Wasm_Module_Bytecode, and this structure should
@@ -56,9 +55,25 @@ typedef struct WASIContext {
 } WASIContext;
 #endif
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+typedef struct WASMRegisteredModule {
+    bh_list_link l;
+    /* point to a string pool */
+    const char *module_name;
+    WASMModuleCommon *module;
+    /* to store the original module file buffer address */
+    uint8 *orig_file_buf;
+    uint32 orig_file_buf_size;
+} WASMRegisteredModule;
+#endif
+
 typedef package_type_t PackageType;
 typedef wasm_section_t WASMSection, AOTSection;
 
+void
+set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format,
+                ...);
+
 /* See wasm_export.h for description */
 bool
 wasm_runtime_init();
@@ -75,6 +90,7 @@ wasm_runtime_destroy();
 PackageType
 get_package_type(const uint8 *buf, uint32 size);
 
+
 /* See wasm_export.h for description */
 WASMModuleCommon *
 wasm_runtime_load(const uint8 *buf, uint32 size,
@@ -83,7 +99,7 @@ wasm_runtime_load(const uint8 *buf, uint32 size,
 /* See wasm_export.h for description */
 WASMModuleCommon *
 wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot,
-                                char *error_buf, uint32_t error_buf_size);
+                                char *error_buf, uint32 error_buf_size);
 
 /* See wasm_export.h for description */
 void
@@ -119,21 +135,21 @@ wasm_runtime_get_module_inst(WASMExecEnv *exec_env);
 
 /* See wasm_export.h for description */
 void *
-wasm_runtime_get_function_attachment(wasm_exec_env_t exec_env);
+wasm_runtime_get_function_attachment(WASMExecEnv *exec_env);
 
 /* See wasm_export.h for description */
 void
-wasm_runtime_set_user_data(wasm_exec_env_t exec_env, void *user_data);
+wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data);
 
 /* See wasm_export.h for description */
 void *
-wasm_runtime_get_user_data(wasm_exec_env_t exec_env);
+wasm_runtime_get_user_data(WASMExecEnv *exec_env);
 
 /* See wasm_export.h for description */
 bool
 wasm_runtime_call_wasm(WASMExecEnv *exec_env,
                        WASMFunctionInstanceCommon *function,
-                       unsigned argc, uint32 argv[]);
+                       uint32 argc, uint32 argv[]);
 
 /**
  * Call a function reference of a given WASM runtime instance with
@@ -154,23 +170,23 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env,
  */
 bool
 wasm_runtime_call_indirect(WASMExecEnv *exec_env,
-                           uint32_t element_indices,
-                           uint32_t argc, uint32_t argv[]);
+                           uint32 element_indices,
+                           uint32 argc, uint32 argv[]);
 
 bool
 wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst,
                                            WASMFunctionInstanceCommon *function,
-                                           unsigned argc, uint32 argv[]);
+                                           uint32 argc, uint32 argv[]);
 
 /* See wasm_export.h for description */
 bool
 wasm_application_execute_main(WASMModuleInstanceCommon *module_inst,
-                              int argc, char *argv[]);
+                              int32 argc, char *argv[]);
 
 /* See wasm_export.h for description */
 bool
 wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
-                              const char *name, int argc, char *argv[]);
+                              const char *name, int32 argc, char *argv[]);
 
 /* See wasm_export.h for description */
 void
@@ -261,6 +277,48 @@ void
 wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst,
                             uint32 llvm_stack);
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+void
+wasm_runtime_set_module_reader(const module_reader reader,
+                               const module_destroyer destroyer);
+
+module_reader
+wasm_runtime_get_module_reader();
+
+module_destroyer
+wasm_runtime_get_module_destroyer();
+
+bool
+wasm_runtime_register_module_internal(const char *module_name,
+                                      WASMModuleCommon *module,
+                                      uint8 *orig_file_buf,
+                                      uint32 orig_file_buf_size,
+                                      char *error_buf,
+                                      uint32 error_buf_size);
+
+void
+wasm_runtime_unregister_module(const WASMModuleCommon *module);
+
+bool
+wasm_runtime_is_module_registered(const char *module_name);
+
+bool
+wasm_runtime_add_loading_module(const char *module_name,
+                                char *error_buf, uint32 error_buf_size);
+
+void
+wasm_runtime_delete_loading_module(const char *module_name);
+
+bool
+wasm_runtime_is_loading_module(const char *module_name);
+
+void
+wasm_runtime_destroy_loading_module_list();
+#endif /* WASM_ENALBE_MULTI_MODULE */
+
+bool
+wasm_runtime_is_built_in_module(const char *module_name);
+
 #if WASM_ENABLE_LIBC_WASI != 0
 /* See wasm_export.h for description */
 void

+ 4 - 0
core/iwasm/compilation/aot.c

@@ -60,6 +60,10 @@ aot_create_mem_init_data_list(const WASMModule *module)
       goto fail;
     }
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+    data_list[i]->is_passive = module->data_segments[i]->is_passive;
+    data_list[i]->memory_index = module->data_segments[i]->memory_index;
+#endif
     data_list[i]->offset = module->data_segments[i]->base_offset;
     data_list[i]->byte_count = module->data_segments[i]->data_length;
     memcpy(data_list[i]->bytes, module->data_segments[i]->data,

+ 6 - 0
core/iwasm/compilation/aot.h

@@ -24,6 +24,12 @@ typedef WASMType AOTFuncType;
  * A segment of memory init data
  */
 typedef struct AOTMemInitData {
+#if WASM_ENABLE_BULK_MEMORY != 0
+  /* Passive flag */
+  bool is_passive;
+  /* memory index */
+  uint32 memory_index;
+#endif
   /* Start address of init data */
   AOTInitExpr offset;
   /* Byte count */

+ 33 - 0
core/iwasm/compilation/aot_compiler.c

@@ -741,6 +741,39 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
             if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, true))
               return false;
             break;
+#if WASM_ENABLE_BULK_MEMORY != 0
+          case WASM_OP_MEMORY_INIT:
+          {
+            uint32 seg_index;
+            read_leb_uint32(frame_ip, frame_ip_end, seg_index);
+            frame_ip ++;
+            if (!aot_compile_op_memory_init(comp_ctx, func_ctx, seg_index))
+              return false;
+            break;
+          }
+          case WASM_OP_DATA_DROP:
+          {
+            uint32 seg_index;
+            read_leb_uint32(frame_ip, frame_ip_end, seg_index);
+            if (!aot_compile_op_data_drop(comp_ctx, func_ctx, seg_index))
+              return false;
+            break;
+          }
+          case WASM_OP_MEMORY_COPY:
+          {
+            frame_ip += 2;
+            if (!aot_compile_op_memory_copy(comp_ctx, func_ctx))
+              return false;
+            break;
+          }
+          case WASM_OP_MEMORY_FILL:
+          {
+            frame_ip ++;
+            if (!aot_compile_op_memory_fill(comp_ctx, func_ctx))
+              return false;
+            break;
+          }
+#endif /* WASM_ENABLE_BULK_MEMORY */
           default:
             break;
         }

+ 50 - 10
core/iwasm/compilation/aot_emit_aot_file.c

@@ -124,8 +124,18 @@ get_mem_init_data_size(AOTMemInitData *mem_init_data)
 {
     /* init expr type (4 bytes) + init expr value (8 bytes)
        + byte count (4 bytes) + bytes */
-    return (uint32)(sizeof(uint32) + sizeof(uint64)
-                    + sizeof(uint32) + mem_init_data->byte_count);
+    uint32 total_size =
+        (uint32)(sizeof(uint32) + sizeof(uint64)
+                 + sizeof(uint32) + mem_init_data->byte_count);
+
+    /* bulk_memory enabled:
+        is_passive (4 bytes) + memory_index (4 bytes)
+       bulk memory disabled:
+        placeholder (4 bytes) + placeholder (4 bytes)
+    */
+    total_size += (sizeof(uint32) + sizeof(uint32));
+
+    return total_size;
 }
 
 static uint32
@@ -682,7 +692,8 @@ get_relocation_section_size(AOTObjectData *obj_data)
 }
 
 static uint32
-get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data)
+get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data,
+                  AOTObjectData *obj_data)
 {
     uint32 size = 0;
 
@@ -868,7 +879,8 @@ aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
 
 static bool
 aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
-                  AOTCompData *comp_data, AOTObjectData *obj_data)
+                  AOTCompContext *comp_ctx, AOTCompData *comp_data,
+                  AOTObjectData *obj_data)
 {
     uint32 offset = *p_offset, i;
     AOTMemInitData **init_datas = comp_data->mem_init_data_list;
@@ -882,6 +894,18 @@ aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
 
     for (i = 0; i < comp_data->mem_init_data_count; i++) {
         offset = align_uint(offset, 4);
+#if WASM_ENABLE_BULK_MEMORY != 0
+        if (comp_ctx->enable_bulk_memory) {
+            EMIT_U32(init_datas[i]->is_passive);
+            EMIT_U32(init_datas[i]->memory_index);
+        }
+        else
+#endif
+        {
+            /* emit two placeholder to keep the same size */
+            EMIT_U32(0);
+            EMIT_U32(0);
+        }
         EMIT_U32(init_datas[i]->offset.init_expr_type);
         EMIT_U64(init_datas[i]->offset.u.i64);
         EMIT_U32(init_datas[i]->byte_count);
@@ -1077,7 +1101,8 @@ aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
 
 static bool
 aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
-                           AOTCompData *comp_data, AOTObjectData *obj_data)
+                           AOTCompContext *comp_ctx, AOTCompData *comp_data,
+                           AOTObjectData *obj_data)
 {
     uint32 section_size = get_init_data_section_size(comp_data, obj_data);
     uint32 offset = *p_offset;
@@ -1087,7 +1112,7 @@ aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
     EMIT_U32(AOT_SECTION_TYPE_INIT_DATA);
     EMIT_U32(section_size);
 
-    if (!aot_emit_mem_info(buf, buf_end, &offset, comp_data, obj_data)
+    if (!aot_emit_mem_info(buf, buf_end, &offset, comp_ctx, comp_data, obj_data)
         || !aot_emit_table_info(buf, buf_end, &offset, comp_data, obj_data)
         || !aot_emit_func_type_info(buf, buf_end, &offset, comp_data, obj_data)
         || !aot_emit_import_global_info(buf, buf_end, &offset, comp_data, obj_data)
@@ -1405,7 +1430,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
 
     obj_data->target_info.bin_type = bin_type - LLVMBinaryTypeELF32L;
 
-    if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF32B) {
+    if (bin_type == LLVMBinaryTypeELF32L
+        || bin_type == LLVMBinaryTypeELF32B) {
         struct elf32_ehdr *elf_header;
         bool is_little_bin = bin_type == LLVMBinaryTypeELF32L;
 
@@ -1420,7 +1446,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
         SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin);
         SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin);
     }
-    else {
+    else if (bin_type == LLVMBinaryTypeELF64L
+             || bin_type == LLVMBinaryTypeELF64B) {
         struct elf64_ehdr *elf_header;
         bool is_little_bin = bin_type == LLVMBinaryTypeELF64L;
 
@@ -1435,6 +1462,19 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
         SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin);
         SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin);
     }
+    else if (bin_type == LLVMBinaryTypeMachO32L
+             || bin_type == LLVMBinaryTypeMachO32B) {
+        /* TODO: parse file type of Mach-O 32 */
+        aot_set_last_error("invaid llvm binary bin_type.");
+        return false;
+    }
+    else if (bin_type == LLVMBinaryTypeMachO64L
+             || bin_type == LLVMBinaryTypeMachO64B) {
+        /* TODO: parse file type of Mach-O 64 */
+        aot_set_last_error("invaid llvm binary bin_type.");
+        return false;
+    }
+
 
     strncpy(obj_data->target_info.arch, comp_ctx->target_arch,
             sizeof(obj_data->target_info.arch));
@@ -1941,7 +1981,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data,
 
     bh_print_time("Begin to emit AOT file");
 
-    aot_file_size = get_aot_file_size(comp_data, obj_data);
+    aot_file_size = get_aot_file_size(comp_ctx, comp_data, obj_data);
 
     if (!(buf = aot_file_buf = wasm_runtime_malloc(aot_file_size))) {
         aot_set_last_error("allocate memory failed.");
@@ -1953,7 +1993,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data,
 
     if (!aot_emit_file_header(buf, buf_end, &offset, comp_data, obj_data)
         || !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, obj_data)
-        || !aot_emit_init_data_section(buf, buf_end, &offset, comp_data, obj_data)
+        || !aot_emit_init_data_section(buf, buf_end, &offset, comp_ctx, comp_data, obj_data)
         || !aot_emit_text_section(buf, buf_end, &offset, comp_data, obj_data)
         || !aot_emit_func_section(buf, buf_end, &offset, comp_data, obj_data)
         || !aot_emit_export_section(buf, buf_end, &offset, comp_data, obj_data)

+ 287 - 0
core/iwasm/compilation/aot_emit_memory.c

@@ -626,3 +626,290 @@ fail:
     return false;
 }
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+
+static LLVMValueRef
+check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                           LLVMValueRef offset, LLVMValueRef bytes)
+{
+    LLVMValueRef maddr, max_addr, cmp;
+    LLVMValueRef mem_base_addr;
+    LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
+    LLVMBasicBlockRef check_succ;
+    uint32 off = offsetof(AOTModuleInstance, memory_data_size);
+    LLVMValueRef mem_size_offset, mem_size_ptr, mem_size;
+
+    /* Get memory base address and memory data size */
+    if (func_ctx->mem_space_unchanged) {
+        mem_base_addr = func_ctx->mem_base_addr;
+    }
+    else {
+        if (!(mem_base_addr = LLVMBuildLoad(comp_ctx->builder,
+                                            func_ctx->mem_base_addr,
+                                            "mem_base"))) {
+            aot_set_last_error("llvm build load failed.");
+            goto fail;
+        }
+    }
+
+    /* return addres directly if constant offset and inside memory space */
+    if (LLVMIsConstant(offset) && LLVMIsConstant(bytes)) {
+        uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(offset);
+        uint64 mem_len = (uint64)LLVMConstIntGetZExtValue(bytes);
+        uint32 num_bytes_per_page = comp_ctx->comp_data->num_bytes_per_page;
+        uint32 init_page_count = comp_ctx->comp_data->mem_init_page_count;
+        uint32 mem_data_size = num_bytes_per_page * init_page_count;
+        if (mem_data_size > 0
+            && mem_offset + mem_len <= mem_data_size) {
+            /* inside memory space */
+            /* maddr = mem_base_addr + moffset */
+            if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder,
+                                               mem_base_addr,
+                                               &offset, 1, "maddr"))) {
+                aot_set_last_error("llvm build add failed.");
+                goto fail;
+            }
+            return maddr;
+        }
+    }
+
+    /* mem_size_offset = aot_inst + off */
+    mem_size_offset = I32_CONST(off);
+    if (!(mem_size_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder,
+                                              func_ctx->aot_inst,
+                                              &mem_size_offset, 1,
+                                              "mem_size_ptr_tmp"))) {
+        aot_set_last_error("llvm build inbounds gep failed.");
+        return NULL;
+    }
+
+    /* cast to int32* */
+    if (!(mem_size_ptr = LLVMBuildBitCast(comp_ctx->builder, mem_size_ptr,
+                                          INT32_PTR_TYPE, "mem_size_ptr"))) {
+        aot_set_last_error("llvm build bitcast failed.");
+        return NULL;
+    }
+
+    /* load memory size */
+    if (!(mem_size = LLVMBuildLoad(comp_ctx->builder,
+                                   mem_size_ptr, "mem_size"))) {
+        aot_set_last_error("llvm build load failed.");
+        return NULL;
+    }
+
+    ADD_BASIC_BLOCK(check_succ, "check_succ");
+    LLVMMoveBasicBlockAfter(check_succ, block_curr);
+
+    offset = LLVMBuildZExt(comp_ctx->builder, offset, I64_TYPE, "extend_offset");
+    bytes = LLVMBuildZExt(comp_ctx->builder, bytes, I64_TYPE, "extend_len");
+    mem_size = LLVMBuildZExt(comp_ctx->builder, mem_size, I64_TYPE, "extend_size");
+
+    BUILD_OP(Add, offset, bytes, max_addr, "max_addr");
+    BUILD_ICMP(LLVMIntUGT, max_addr, mem_size, cmp,
+               "cmp_max_mem_addr");
+    if (!aot_emit_exception(comp_ctx, func_ctx,
+                            EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
+                            true, cmp, check_succ)) {
+        goto fail;
+    }
+
+    /* maddr = mem_base_addr + offset */
+    if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr,
+                                       &offset, 1, "maddr"))) {
+        aot_set_last_error("llvm build add failed.");
+        goto fail;
+    }
+    return maddr;
+fail:
+    return NULL;
+}
+
+#define GET_AOT_FUNCTION(name, argc) do {                               \
+    if (!(func_type = LLVMFunctionType(ret_type, param_types,           \
+                                       argc, false))) {                 \
+        aot_set_last_error("llvm add function type failed.");           \
+        return false;                                                   \
+    }                                                                   \
+    if (comp_ctx->is_jit_mode) {                                        \
+        /* JIT mode, call the function directly */                      \
+        if (!(func_ptr_type = LLVMPointerType(func_type, 0))) {         \
+            aot_set_last_error("llvm add pointer type failed.");        \
+            return false;                                               \
+        }                                                               \
+        if (!(value = I64_CONST((uint64)(uintptr_t)name))               \
+            || !(func = LLVMConstIntToPtr(value, func_ptr_type))) {     \
+            aot_set_last_error("create LLVM value failed.");            \
+            return false;                                               \
+        }                                                               \
+    }                                                                   \
+    else {                                                              \
+        char *func_name = #name;                                        \
+        /* AOT mode, delcare the function */                            \
+        if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \
+            && !(func = LLVMAddFunction(comp_ctx->module,               \
+                                        func_name, func_type))) {       \
+            aot_set_last_error("llvm add function failed.");            \
+            return false;                                               \
+        }                                                               \
+    }                                                                   \
+  } while (0)
+
+bool
+aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                           uint32 seg_index)
+{
+    LLVMValueRef seg, offset, dst, len, param_values[5], ret_value, func, value;
+    LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type;
+    AOTFuncType *aot_func_type = func_ctx->aot_func->func_type;
+    LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
+    LLVMBasicBlockRef mem_init_fail, init_success;
+
+    seg = I32_CONST(seg_index);
+
+    POP_I32(len);
+    POP_I32(offset);
+    POP_I32(dst);
+
+    param_types[0] = INT8_PTR_TYPE;
+    param_types[1] = I32_TYPE;
+    param_types[2] = I32_TYPE;
+    param_types[3] = I32_TYPE;
+    param_types[4] = I32_TYPE;
+    ret_type = INT8_TYPE;
+
+    GET_AOT_FUNCTION(aot_memory_init, 5);
+
+    /* Call function aot_memory_init() */
+    param_values[0] = func_ctx->aot_inst;
+    param_values[1] = seg;
+    param_values[2] = offset;
+    param_values[3] = len;
+    param_values[4] = dst;
+    if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func,
+                                    param_values, 5, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        return false;
+    }
+
+    BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_init_ret");
+
+    ADD_BASIC_BLOCK(mem_init_fail, "mem_init_fail");
+    ADD_BASIC_BLOCK(init_success, "init_success");
+
+    LLVMMoveBasicBlockAfter(mem_init_fail, block_curr);
+    LLVMMoveBasicBlockAfter(init_success, block_curr);
+
+    if (!LLVMBuildCondBr(comp_ctx->builder, ret_value,
+                         init_success, mem_init_fail)) {
+        aot_set_last_error("llvm build cond br failed.");
+        goto fail;
+    }
+
+    /* If memory.init failed, return this function
+        so the runtime can catch the exception */
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail);
+    if (aot_func_type->result_count) {
+        switch (aot_func_type->types[aot_func_type->param_count]) {
+            case VALUE_TYPE_I32:
+                LLVMBuildRet(comp_ctx->builder, I32_ZERO);
+                break;
+            case VALUE_TYPE_I64:
+                LLVMBuildRet(comp_ctx->builder, I64_ZERO);
+                break;
+            case VALUE_TYPE_F32:
+                LLVMBuildRet(comp_ctx->builder, F32_ZERO);
+                break;
+            case VALUE_TYPE_F64:
+                LLVMBuildRet(comp_ctx->builder, F64_ZERO);
+                break;
+        }
+    }
+    else {
+        LLVMBuildRetVoid(comp_ctx->builder);
+    }
+
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, init_success);
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                         uint32 seg_index)
+{
+    LLVMValueRef seg, param_values[2], ret_value, func, value;
+    LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type;
+
+    seg = I32_CONST(seg_index);
+
+    param_types[0] = INT8_PTR_TYPE;
+    param_types[1] = I32_TYPE;
+    ret_type = INT8_TYPE;
+
+    GET_AOT_FUNCTION(aot_data_drop, 2);
+
+    /* Call function aot_data_drop() */
+    param_values[0] = func_ctx->aot_inst;
+    param_values[1] = seg;
+    if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func,
+                                    param_values, 2, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        return false;
+    }
+    return true;
+}
+
+bool
+aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMValueRef src, dst, src_addr, dst_addr, len, res;
+
+    POP_I32(len);
+    POP_I32(src);
+    POP_I32(dst);
+
+    if (!(src_addr =
+            check_bulk_memory_overflow(comp_ctx, func_ctx, src, len)))
+        return false;
+
+    if (!(dst_addr =
+            check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len)))
+        return false;
+
+    if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1,
+                                 src_addr, 1, len))) {
+        aot_set_last_error("llvm build memmove failed.");
+        return false;
+    }
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMValueRef val, dst, dst_addr, len, res;
+
+    POP_I32(len);
+    POP_I32(val);
+    POP_I32(dst);
+
+    if (!(dst_addr =
+            check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len)))
+        return false;
+
+    val = LLVMBuildIntCast2(comp_ctx->builder, val, INT8_TYPE, true, "mem_set_value");
+
+    if (!(res = LLVMBuildMemSet(comp_ctx->builder, dst_addr,
+                                val, len, 1))) {
+        aot_set_last_error("llvm build memset failed.");
+        return false;
+    }
+    return true;
+fail:
+    return false;
+}
+#endif /* WASM_ENABLE_BULK_MEMORY */

+ 16 - 0
core/iwasm/compilation/aot_emit_memory.h

@@ -50,6 +50,22 @@ aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
 bool
 aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
 
+#if WASM_ENABLE_BULK_MEMORY != 0
+bool
+aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                           uint32 seg_index);
+
+bool
+aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                         uint32 seg_index);
+
+bool
+aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
+
+bool
+aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
+#endif
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 3 - 0
core/iwasm/compilation/aot_llvm.c

@@ -914,6 +914,9 @@ aot_create_comp_context(AOTCompData *comp_data,
         goto fail;
     }
 
+    if (option->enable_bulk_memory)
+        comp_ctx->enable_bulk_memory = true;
+
     if (option->is_jit_mode) {
         /* Create LLVM execution engine */
         LLVMInitializeMCJITCompilerOptions(&jit_options, sizeof(jit_options));

+ 4 - 0
core/iwasm/compilation/aot_llvm.h

@@ -185,6 +185,9 @@ typedef struct AOTCompContext {
   LLVMExecutionEngineRef exec_engine;
   bool is_jit_mode;
 
+  /* Bulk memory feature */
+  bool enable_bulk_memory;
+
   /* Whether optimize the JITed code */
   bool optimize;
 
@@ -223,6 +226,7 @@ typedef struct AOTCompOption{
     char *target_abi;
     char *target_cpu;
     char *cpu_features;
+    bool enable_bulk_memory;
     uint32 opt_level;
     uint32 size_level;
     uint32 output_format;

+ 1 - 0
core/iwasm/include/aot_export.h

@@ -39,6 +39,7 @@ typedef struct AOTCompOption{
     char *target_abi;
     char *target_cpu;
     char *cpu_features;
+    bool enable_bulk_memory;
     uint32_t opt_level;
     uint32_t size_level;
     uint32_t output_format;

+ 1 - 1
core/iwasm/include/lib_export.h

@@ -6,7 +6,7 @@
 #ifndef _LIB_EXPORT_H_
 #define _LIB_EXPORT_H_
 
-#include <inttypes.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {

+ 54 - 2
core/iwasm/include/wasm_export.h

@@ -6,7 +6,7 @@
 #ifndef _WASM_EXPORT_H
 #define _WASM_EXPORT_H
 
-#include <inttypes.h>
+#include <stdint.h>
 #include <stdbool.h>
 #include "lib_export.h"
 
@@ -178,6 +178,56 @@ void wasm_runtime_free(void *ptr);
 package_type_t
 get_package_type(const uint8_t *buf, uint32_t size);
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+/**
+ * It is a callback for WAMR providing by embedding to load a module file
+ * into a buffer
+ */
+typedef bool (*module_reader)(const char *module_name,
+                              uint8_t **p_buffer, uint32_t *p_size);
+
+/**
+ * It is a callback for WAMR providing by embedding to release the buffer which
+ * is used by loading a module file
+ */
+typedef void (*module_destroyer)(uint8_t *buffer, uint32_t size);
+
+/**
+ * To setup callbacks for reading and releasing a buffer about a module file
+ *
+ * @param reader a callback to read a module file into a buffer
+ * @param destroyer a callback to release above buffer
+ */
+void
+wasm_runtime_set_module_reader(const module_reader reader,
+                               const module_destroyer destroyer);
+/**
+ * Give the "module" a name "module_name".
+ * can not assign a new name to a module if it already has a name
+ *
+ * @param module_name indicate a name
+ * @param module the target module
+ * @param error_buf output of the exception info
+ * @param error_buf_size the size of the exception string
+ *
+ * @return true means success, false means failed
+ */
+bool
+wasm_runtime_register_module(const char *module_name, wasm_module_t module,
+                             char *error_buf, uint32_t error_buf_size);
+
+/**
+ * To check if there is already a loaded module named module_name in the
+ * runtime. you will not want to load repeately
+ *
+ * @param module_name indicate a name
+ *
+ * @return return WASM module loaded, NULL if failed
+ */
+wasm_module_t
+wasm_runtime_find_module_registered(const char *module_name);
+#endif /* WASM_ENABLE_MULTI_MODULE */
+
 /**
  * Load a WASM module from a specified byte buffer. The byte buffer can be
  * WASM binary data when interpreter or JIT is enabled, or AOT binary data
@@ -348,7 +398,9 @@ wasm_application_execute_main(wasm_module_inst_t module_inst,
  * and execute that function.
  *
  * @param module_inst the WASM module instance
- * @param name the name of the function to execute
+ * @param name the name of the function to execute.
+ *  to indicate the module name via: $module_name$function_name
+ *  or just a function name: function_name
  * @param argc the number of arguments
  * @param argv the arguments array
  *

+ 43 - 3
core/iwasm/interpreter/wasm.h

@@ -22,6 +22,8 @@ extern "C" {
 #define VALUE_TYPE_VOID 0x40
 /* Used by AOT */
 #define VALUE_TYPE_I1  0x41
+/*  Used by loader to represent any type of i32/i64/f32/f64 */
+#define VALUE_TYPE_ANY 0x42
 
 /* Table Element Type */
 #define TABLE_ELEM_TYPE_ANY_FUNC 0x70
@@ -50,6 +52,9 @@ extern "C" {
 #define SECTION_TYPE_ELEM 9
 #define SECTION_TYPE_CODE 10
 #define SECTION_TYPE_DATA 11
+#if WASM_ENABLE_BULK_MEMORY != 0
+#define SECTION_TYPE_DATACOUNT 12
+#endif
 
 #define IMPORT_KIND_FUNC 0
 #define IMPORT_KIND_TABLE 1
@@ -66,6 +71,10 @@ extern "C" {
 #define BLOCK_TYPE_IF 2
 #define BLOCK_TYPE_FUNCTION 3
 
+typedef struct WASMModule WASMModule;
+typedef struct WASMFunction WASMFunction;
+typedef struct WASMGlobal WASMGlobal;
+
 typedef union WASMValue {
     int32 i32;
     uint32 u32;
@@ -119,6 +128,10 @@ typedef struct WASMTableImport {
     uint32 init_size;
     /* specified if (flags & 1), else it is 0x10000 */
     uint32 max_size;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMModule *import_module;
+    WASMTable *import_table_linked;
+#endif
 } WASMTableImport;
 
 typedef struct WASMMemoryImport {
@@ -128,6 +141,10 @@ typedef struct WASMMemoryImport {
     uint32 num_bytes_per_page;
     uint32 init_page_count;
     uint32 max_page_count;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMModule *import_module;
+    WASMMemory *import_memory_linked;
+#endif
 } WASMMemoryImport;
 
 typedef struct WASMFunctionImport {
@@ -135,13 +152,17 @@ typedef struct WASMFunctionImport {
     char *field_name;
     /* function type */
     WASMType *func_type;
-    /* function pointer after linked */
+    /* native function pointer after linked */
     void *func_ptr_linked;
     /* signature from registered native symbols */
     const char *signature;
     /* attachment */
     void *attachment;
     bool call_conv_raw;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMModule *import_module;
+    WASMFunction *import_func_linked;
+#endif
 } WASMFunctionImport;
 
 typedef struct WASMGlobalImport {
@@ -151,6 +172,12 @@ typedef struct WASMGlobalImport {
     bool is_mutable;
     /* global data after linked */
     WASMValue global_data_linked;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    /* imported function pointer after linked */
+    // TODO: remove if not necessary
+    WASMModule *import_module;
+    WASMGlobal *import_global_linked;
+#endif
 } WASMGlobalImport;
 
 typedef struct WASMImport {
@@ -223,6 +250,9 @@ typedef struct WASMDataSeg {
     uint32 memory_index;
     InitializerExpression base_offset;
     uint32 data_length;
+#if WASM_ENABLE_BULK_MEMORY != 0
+    bool is_passive;
+#endif
     uint8 *data;
 } WASMDataSeg;
 
@@ -266,7 +296,12 @@ typedef struct WASMModule {
     uint32 global_count;
     uint32 export_count;
     uint32 table_seg_count;
+    /* data seg count read from data segment section */
     uint32 data_seg_count;
+#if WASM_ENABLE_BULK_MEMORY != 0
+    /* data count read from datacount section */
+    uint32 data_seg_count1;
+#endif
 
     uint32 import_function_count;
     uint32 import_table_count;
@@ -308,6 +343,12 @@ typedef struct WASMModule {
     WASIArguments wasi_args;
     bool is_wasi_module;
 #endif
+
+#if WASM_ENABLE_MULTI_MODULE != 0
+    // TODO: mutex ? mutli-threads ?
+    bh_list import_module_list_head;
+    bh_list *import_module_list;
+#endif
 } WASMModule;
 
 typedef struct WASMBranchBlock {
@@ -430,5 +471,4 @@ wasm_type_equal(const WASMType *type1, const WASMType *type2)
 } /* end of extern "C" */
 #endif
 
-#endif /* end of _WASM_H_ */
-
+#endif /* end of _WASM_H_ */

+ 248 - 19
core/iwasm/interpreter/wasm_interp_classic.c

@@ -236,6 +236,15 @@ LOAD_I16(void *addr)
       goto out_of_bounds;                                                   \
   } while (0)
 
+#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do {                \
+    uint64 offset1 = (int32)(start);                                        \
+    if (offset1 + bytes <= linear_mem_size)                                 \
+      /* App heap space is not valid space for bulk memory operation */     \
+      maddr = memory->memory_data + offset1;                                \
+    else                                                                    \
+      goto out_of_bounds;                                                   \
+  } while (0)
+
 static inline uint32
 rotl32(uint32 n, uint32 c)
 {
@@ -877,6 +886,59 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst,
     wasm_exec_env_set_cur_frame(exec_env, prev_frame);
 }
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+static void
+wasm_interp_call_func_bytecode(WASMModuleInstance *module,
+                               WASMExecEnv *exec_env,
+                               WASMFunctionInstance *cur_func,
+                               WASMInterpFrame *prev_frame);
+
+static void
+wasm_interp_call_func_import(WASMModuleInstance *module_inst,
+                             WASMExecEnv *exec_env,
+                             WASMFunctionInstance *cur_func,
+                             WASMInterpFrame *prev_frame)
+{
+    WASMModuleInstance *sub_module_inst = cur_func->import_module_inst;
+    WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst;
+    WASMFunctionImport *func_import = cur_func->u.func_import;
+    uint8 *ip = prev_frame->ip;
+    char buf[128];
+
+    if (!sub_func_inst) {
+        snprintf(buf, sizeof(buf),
+                 "fail to call unlinked import function (%s, %s)",
+                 func_import->module_name, func_import->field_name);
+        wasm_set_exception(module_inst, buf);
+        return;
+    }
+
+    /* set ip NULL to make call_func_bytecode return after executing
+       this function */
+    prev_frame->ip = NULL;
+
+    /* replace exec_env's module_inst with sub_module_inst so we can
+       call it */
+    exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst;
+
+    /* call function of sub-module*/
+    wasm_interp_call_func_bytecode(sub_module_inst, exec_env,
+                                   sub_func_inst, prev_frame);
+
+    /* restore ip and module_inst */
+    prev_frame->ip = ip;
+    exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst;
+
+    /* transfer exception if it is thrown */
+    if (wasm_get_exception(sub_module_inst)) {
+        bh_memcpy_s(module_inst->cur_exception,
+                    sizeof(module_inst->cur_exception),
+                    sub_module_inst->cur_exception,
+                    sizeof(sub_module_inst->cur_exception));
+    }
+}
+#endif
+
 #if WASM_ENABLE_LABELS_AS_VALUES != 0
 
 #define HANDLE_OP(opcode) HANDLE_##opcode
@@ -902,6 +964,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
   uint32 total_mem_size = memory ? num_bytes_per_page * memory->cur_page_count
                                    - heap_base_offset : 0;
   uint8 *global_data = module->global_data;
+#if WASM_ENABLE_BULK_MEMORY != 0
+  uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0;
+#endif
   WASMTableInstance *table = module->default_table;
   WASMGlobalInstance *globals = module->globals;
   uint8 opcode_IMPDEP = WASM_OP_IMPDEP;
@@ -1086,14 +1151,27 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
       HANDLE_OP (WASM_OP_CALL):
         read_leb_uint32(frame_ip, frame_ip_end, fidx);
-        bh_assert(fidx < module->function_count);
+#if WASM_ENABLE_MULTI_MODULE != 0
+        if (fidx >= module->function_count) {
+          wasm_set_exception(module, "unknown function");
+          goto got_exception;
+        }
+#endif
+
         cur_func = module->functions + fidx;
         goto call_func_from_interp;
 
       HANDLE_OP (WASM_OP_CALL_INDIRECT):
         {
           WASMType *cur_type, *cur_func_type;
-
+          WASMTableInstance *cur_table_inst;
+
+          /**
+           * type check. compiler will make sure all like
+           * (call_indirect (type $x) (i32.const 1))
+           * the function type has to be defined in the module also
+           * no matter it is used or not
+           */
           read_leb_uint32(frame_ip, frame_ip_end, tidx);
           if (tidx >= module->module->type_count) {
             wasm_set_exception(module, "type index is overflow");
@@ -1105,27 +1183,48 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           frame_ip++;
           val = POP_I32();
 
-          if (val < 0 || val >= (int32)table->cur_size) {
+          /* careful, it might be a table in another module */
+          cur_table_inst = table;
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (table->table_inst_linked) {
+              cur_table_inst = table->table_inst_linked;
+          }
+#endif
+
+          if (val < 0 || val >= (int32)cur_table_inst->cur_size) {
             wasm_set_exception(module, "undefined element");
             goto got_exception;
           }
 
-          fidx = ((uint32*)table->base_addr)[val];
+          fidx = ((uint32*)cur_table_inst->base_addr)[val];
           if (fidx == (uint32)-1) {
             wasm_set_exception(module, "uninitialized element");
             goto got_exception;
           }
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (fidx >=  module->function_count) {
+            wasm_set_exception(module, "unknown function");
+            goto got_exception;
+          }
+#endif
+
+          /* always call module own functions */
           cur_func = module->functions + fidx;
 
-          if (cur_func->is_import_func)
-            cur_func_type = cur_func->u.func_import->func_type;
+          if (cur_func->is_import_func
+#if WASM_ENABLE_MULTI_MODULE != 0
+              && !cur_func->import_func_inst
+#endif
+          )
+              cur_func_type = cur_func->u.func_import->func_type;
           else
             cur_func_type = cur_func->u.func->func_type;
           if (!wasm_type_equal(cur_type, cur_func_type)) {
             wasm_set_exception(module, "indirect call type mismatch");
             goto got_exception;
           }
+
           goto call_func_from_interp;
         }
 
@@ -1264,7 +1363,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
           bh_assert(global_idx < module->global_count);
           global = globals + global_idx;
-          global_addr = global_data + global->data_offset;
+          global_addr =
+#if WASM_ENABLE_MULTI_MODULE != 0
+            global->import_global_inst
+              ? global->import_module_inst->global_data
+                + global->import_global_inst->data_offset
+              :
+#endif
+              global_data + global->data_offset;
 
           switch (global->type) {
             case VALUE_TYPE_I32:
@@ -1289,7 +1395,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
           bh_assert(global_idx < module->global_count);
           global = globals + global_idx;
-          global_addr = global_data + global->data_offset;
+          global_addr =
+#if WASM_ENABLE_MULTI_MODULE != 0
+            global->import_global_inst
+              ? global->import_module_inst->global_data
+                + global->import_global_inst->data_offset
+              :
+#endif
+              global_data + global->data_offset;
 
           switch (global->type) {
             case VALUE_TYPE_I32:
@@ -1521,6 +1634,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           memory = module->default_memory;
           total_mem_size = num_bytes_per_page * memory->cur_page_count
                            - heap_base_offset;
+#if WASM_ENABLE_BULK_MEMORY != 0
+          linear_mem_size = num_bytes_per_page * memory->cur_page_count;
+#endif
         }
 
         (void)reserved;
@@ -2357,6 +2473,78 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           DEF_OP_TRUNC_SAT_F64(-1.0f, 18446744073709551616.0,
                                false, false);
           break;
+#if WASM_ENABLE_BULK_MEMORY != 0
+        case WASM_OP_MEMORY_INIT:
+        {
+          uint32 addr, segment;
+          uint64 bytes, offset, seg_len;
+          uint8* data;
+
+          read_leb_uint32(frame_ip, frame_ip_end, segment);
+          /* skip memory index */
+          frame_ip++;
+
+          bytes = (uint64)(uint32)POP_I32();
+          offset = (uint64)(uint32)POP_I32();
+          addr = (uint32)POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr);
+
+          seg_len = (uint64)module->module->data_segments[segment]->data_length;
+          data = module->module->data_segments[segment]->data;
+          if (offset + bytes > seg_len)
+            goto out_of_bounds;
+
+          bh_memcpy_s(maddr, linear_mem_size - addr,
+                      data + offset, bytes);
+          break;
+        }
+        case WASM_OP_DATA_DROP:
+        {
+          uint32 segment;
+
+          read_leb_uint32(frame_ip, frame_ip_end, segment);
+          module->module->data_segments[segment]->data_length = 0;
+
+          break;
+        }
+        case WASM_OP_MEMORY_COPY:
+        {
+          uint32 dst, src, len;
+          uint8 *mdst, *msrc;
+
+          frame_ip += 2;
+
+          len = POP_I32();
+          src = POP_I32();
+          dst = POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc);
+          CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst);
+
+          /* allowing the destination and source to overlap */
+          bh_memmove_s(mdst, linear_mem_size - dst,
+                       msrc, len);
+
+          break;
+        }
+        case WASM_OP_MEMORY_FILL:
+        {
+          uint32 dst, len;
+          uint8 val, *mdst;
+          frame_ip++;
+
+          len = POP_I32();
+          val = POP_I32();
+          dst = POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst);
+
+          memset(mdst, val, len);
+
+          break;
+        }
+#endif /* WASM_ENABLE_BULK_MEMORY */
         default:
           wasm_set_exception(module, "WASM interp failed: unsupported opcode.");
             goto got_exception;
@@ -2430,14 +2618,25 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
   call_func_from_entry:
     {
       if (cur_func->is_import_func) {
-        wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame);
-        prev_frame = frame->prev_frame;
-        cur_func = frame->function;
-        UPDATE_ALL_FROM_FRAME();
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (cur_func->import_func_inst) {
+              wasm_interp_call_func_import(module, exec_env, cur_func,
+                                           prev_frame);
+          }
+          else
+#endif
+          {
+              wasm_interp_call_func_native(module, exec_env, cur_func,
+                                           prev_frame);
+          }
 
-        memory = module->default_memory;
-        if (wasm_get_exception(module))
-          goto got_exception;
+          prev_frame = frame->prev_frame;
+          cur_func = frame->function;
+          UPDATE_ALL_FROM_FRAME();
+
+          memory = module->default_memory;
+          if (wasm_get_exception(module))
+              goto got_exception;
       }
       else {
         WASMFunction *cur_wasm_func = cur_func->u.func;
@@ -2521,6 +2720,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst,
                       WASMFunctionInstance *function,
                       uint32 argc, uint32 argv[])
 {
+    // TODO: since module_inst = exec_env->module_inst, shall we remove the 1st arg?
     WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env);
     WASMInterpFrame *frame, *outs_area;
 
@@ -2559,15 +2759,44 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst,
 
     wasm_exec_env_set_cur_frame(exec_env, frame);
 
-    if (function->is_import_func)
-        wasm_interp_call_func_native(module_inst, exec_env, function, frame);
-    else
+    if (function->is_import_func) {
+#if WASM_ENABLE_MULTI_MODULE != 0
+        if (function->import_module_inst) {
+            LOG_DEBUG("it is a function of a sub module");
+            wasm_interp_call_func_import(module_inst,
+                                         exec_env,
+                                         function,
+                                         frame);
+        }
+        else
+#endif
+        {
+            LOG_DEBUG("it is an native function");
+            /* it is a native function */
+            wasm_interp_call_func_native(module_inst,
+                                         exec_env,
+                                         function,
+                                         frame);
+        }
+    }
+    else {
+        LOG_DEBUG("it is a function of the module itself");
         wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame);
+    }
 
     /* Output the return value to the caller */
     if (!wasm_get_exception(module_inst)) {
-        for (i = 0; i < function->ret_cell_num; i++)
+        for (i = 0; i < function->ret_cell_num; i++) {
             argv[i] = *(frame->sp + i - function->ret_cell_num);
+        }
+
+        if (function->ret_cell_num) {
+            LOG_DEBUG("first return value argv[0]=%d", argv[0]);
+        } else {
+            LOG_DEBUG("no return value");
+        }
+    } else {
+        LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst));
     }
 
     wasm_exec_env_set_cur_frame(exec_env, prev_frame);

+ 225 - 18
core/iwasm/interpreter/wasm_interp_fast.c

@@ -238,6 +238,15 @@ LOAD_I16(void *addr)
       goto out_of_bounds;                                                \
   } while (0)
 
+#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do {                \
+    uint64 offset1 = (int32)(start);                                        \
+    if (offset1 + bytes <= linear_mem_size)                                 \
+      /* App heap space is not valid space for bulk memory operation */     \
+      maddr = memory->memory_data + offset1;                                \
+    else                                                                    \
+      goto out_of_bounds;                                                   \
+  } while (0)
+
 static inline uint32
 rotl32(uint32 n, uint32 c)
 {
@@ -815,6 +824,59 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst,
     wasm_exec_env_set_cur_frame(exec_env, prev_frame);
 }
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+static void
+wasm_interp_call_func_bytecode(WASMModuleInstance *module,
+                               WASMExecEnv *exec_env,
+                               WASMFunctionInstance *cur_func,
+                               WASMInterpFrame *prev_frame);
+
+static void
+wasm_interp_call_func_import(WASMModuleInstance *module_inst,
+                             WASMExecEnv *exec_env,
+                             WASMFunctionInstance *cur_func,
+                             WASMInterpFrame *prev_frame)
+{
+   WASMModuleInstance *sub_module_inst = cur_func->import_module_inst;
+    WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst;
+    WASMFunctionImport *func_import = cur_func->u.func_import;
+    uint8 *ip = prev_frame->ip;
+    char buf[128];
+
+    if (!sub_func_inst) {
+        snprintf(buf, sizeof(buf),
+                 "fail to call unlinked import function (%s, %s)",
+                 func_import->module_name, func_import->field_name);
+        wasm_set_exception(module_inst, buf);
+        return;
+    }
+
+    /* set ip NULL to make call_func_bytecode return after executing
+       this function */
+    prev_frame->ip = NULL;
+
+    /* replace exec_env's module_inst with sub_module_inst so we can
+       call it */
+    exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst;
+
+    /* call function of sub-module*/
+    wasm_interp_call_func_bytecode(sub_module_inst, exec_env,
+                                   sub_func_inst, prev_frame);
+
+    /* restore ip and module_inst */
+    prev_frame->ip = ip;
+    exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst;
+
+    /* transfer exception if it is thrown */
+    if (wasm_get_exception(sub_module_inst)) {
+        bh_memcpy_s(module_inst->cur_exception,
+                    sizeof(module_inst->cur_exception),
+                    sub_module_inst->cur_exception,
+                    sizeof(sub_module_inst->cur_exception));
+    }
+}
+#endif
+
 #if WASM_ENABLE_OPCODE_COUNTER != 0
 typedef struct OpcodeInfo {
     char *name;
@@ -894,6 +956,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
   uint32 total_mem_size = memory ? num_bytes_per_page * memory->cur_page_count
                                    - heap_base_offset : 0;
   uint8 *global_data = module->global_data;
+#if WASM_ENABLE_BULK_MEMORY != 0
+  uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0;
+#endif
   WASMTableInstance *table = module->default_table;
   WASMGlobalInstance *globals = module->globals;
   uint8 opcode_IMPDEP = WASM_OP_IMPDEP;
@@ -997,6 +1062,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
       HANDLE_OP (WASM_OP_CALL_INDIRECT):
         {
           WASMType *cur_type, *cur_func_type;
+          WASMTableInstance *cur_table_inst;
 
           tidx = GET_OPERAND(int32, 0);
           val = GET_OPERAND(int32, 2);
@@ -1008,21 +1074,41 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           }
           cur_type = module->module->types[tidx];
 
-          if (val < 0 || val >= (int32)table->cur_size) {
+          /* careful, it might be a table in another module */
+          cur_table_inst = table;
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (table->table_inst_linked) {
+              cur_table_inst = table->table_inst_linked;
+          }
+#endif
+
+          if (val < 0 || val >= (int32)cur_table_inst->cur_size) {
             wasm_set_exception(module, "undefined element");
             goto got_exception;
           }
 
-          fidx = ((uint32*)table->base_addr)[val];
+          fidx = ((uint32*)cur_table_inst->base_addr)[val];
           if (fidx == (uint32)-1) {
             wasm_set_exception(module, "uninitialized element");
             goto got_exception;
           }
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (fidx >= module->function_count) {
+            wasm_set_exception(module, "unknown function");
+            goto got_exception;
+          }
+#endif
+
+          /* always call module own functions */
           cur_func = module->functions + fidx;
 
-          if (cur_func->is_import_func)
-            cur_func_type = cur_func->u.func_import->func_type;
+          if (cur_func->is_import_func
+#if WASM_ENABLE_MULTI_MODULE != 0
+              && !cur_func->import_func_inst
+#endif
+          )
+              cur_func_type = cur_func->u.func_import->func_type;
           else
             cur_func_type = cur_func->u.func->func_type;
           if (!wasm_type_equal(cur_type, cur_func_type)) {
@@ -1115,7 +1201,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
           bh_assert(global_idx < module->global_count);
           global = globals + global_idx;
-          global_addr = global_data + global->data_offset;
+          global_addr =
+#if WASM_ENABLE_MULTI_MODULE != 0
+            global->import_global_inst
+              ? global->import_module_inst->global_data
+                + global->import_global_inst->data_offset
+              :
+#endif
+          global_data + global->data_offset;
 
           switch (global->type) {
             case VALUE_TYPE_I32:
@@ -1141,7 +1234,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
           bh_assert(global_idx < module->global_count);
           global = globals + global_idx;
-          global_addr = global_data + global->data_offset;
+          global_addr =
+#if WASM_ENABLE_MULTI_MODULE != 0
+            global->import_global_inst
+              ? global->import_module_inst->global_data
+                + global->import_global_inst->data_offset
+              :
+#endif
+              global_data + global->data_offset;
 
           switch (global->type) {
             case VALUE_TYPE_I32:
@@ -1429,6 +1529,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           memory = module->default_memory;
           total_mem_size = num_bytes_per_page * memory->cur_page_count
                            - heap_base_offset;
+#if WASM_ENABLE_BULK_MEMORY != 0
+          linear_mem_size = num_bytes_per_page * memory->cur_page_count;
+#endif
         }
 
         (void)reserved;
@@ -2295,6 +2398,76 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
           DEF_OP_TRUNC_SAT_F64(-1.0, 18446744073709551616.0,
                                false, false);
           break;
+#if WASM_ENABLE_BULK_MEMORY != 0
+        case WASM_OP_MEMORY_INIT:
+        {
+          uint32 addr, segment;
+          uint64 bytes, offset, seg_len;
+          uint8* data;
+
+          segment = GET_OPERAND(uint32, 0);
+          frame_ip += 2;
+
+          bytes = (uint64)POP_I32();
+          offset = (uint64)POP_I32();
+          addr = POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr);
+
+          seg_len = (uint64)module->module->data_segments[segment]->data_length;
+          data = module->module->data_segments[segment]->data;
+          if (offset + bytes > seg_len)
+            goto out_of_bounds;
+
+          bh_memcpy_s(maddr, linear_mem_size - addr,
+                      data + offset, bytes);
+          break;
+        }
+        case WASM_OP_DATA_DROP:
+        {
+          uint32 segment;
+
+          segment = GET_OPERAND(uint32, 0);
+          frame_ip += 2;
+
+          module->module->data_segments[segment]->data_length = 0;
+
+          break;
+        }
+        case WASM_OP_MEMORY_COPY:
+        {
+          uint32 dst, src, len;
+          uint8 *mdst, *msrc;
+
+          len = POP_I32();
+          src = POP_I32();
+          dst = POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc);
+          CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst);
+
+          /* allowing the destination and source to overlap */
+          bh_memmove_s(mdst, linear_mem_size - dst,
+                       msrc, len);
+
+          break;
+        }
+        case WASM_OP_MEMORY_FILL:
+        {
+          uint32 dst, len;
+          uint8 val, *mdst;
+
+          len = POP_I32();
+          val = POP_I32();
+          dst = POP_I32();
+
+          CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst);
+
+          memset(mdst, val, len);
+
+          break;
+        }
+#endif /* WASM_ENABLE_BULK_MEMORY */
         default:
           wasm_set_exception(module, "WASM interp failed: unsupported opcode.");
             goto got_exception;
@@ -2310,7 +2483,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
       HANDLE_OP (WASM_OP_CALL):
         fidx = frame_lp[GET_OFFSET()];
-        bh_assert(fidx < module->function_count);
+#if WASM_ENABLE_MULTI_MODULE != 0
+        if (fidx >= module->function_count) {
+          wasm_set_exception(module, "unknown function");
+          goto got_exception;
+        }
+#endif
         cur_func = module->functions + fidx;
         goto call_func_from_interp;
 
@@ -2383,7 +2561,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
               GET_OPERAND(int64, (2 * (cur_func->param_count - i - 1)));
             outs_area->lp += 2;
         } else {
-          *(outs_area->lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1)));;
+          *(outs_area->lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1)));
           outs_area->lp ++;
         }
       }
@@ -2397,14 +2575,25 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
   call_func_from_entry:
     {
       if (cur_func->is_import_func) {
-        wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame);
-        prev_frame = frame->prev_frame;
-        cur_func = frame->function;
-        UPDATE_ALL_FROM_FRAME();
+#if WASM_ENABLE_MULTI_MODULE != 0
+          if (cur_func->import_func_inst) {
+              wasm_interp_call_func_import(module, exec_env, cur_func,
+                                           prev_frame);
+          }
+          else
+#endif
+          {
+              wasm_interp_call_func_native(module, exec_env, cur_func,
+                                           prev_frame);
+          }
 
-        memory = module->default_memory;
-        if (wasm_get_exception(module))
-          goto got_exception;
+          prev_frame = frame->prev_frame;
+          cur_func = frame->function;
+          UPDATE_ALL_FROM_FRAME();
+
+          memory = module->default_memory;
+          if (wasm_get_exception(module))
+              goto got_exception;
       }
       else {
         WASMFunction *cur_wasm_func = cur_func->u.func;
@@ -2528,10 +2717,28 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst,
 
     wasm_exec_env_set_cur_frame(exec_env, frame);
 
-    if (function->is_import_func)
-        wasm_interp_call_func_native(module_inst, exec_env, function, frame);
-    else
+    if (function->is_import_func) {
+#if WASM_ENABLE_MULTI_MODULE != 0
+        if (function->import_module_inst) {
+            LOG_DEBUG("it is a function of a sub module");
+            wasm_interp_call_func_import(module_inst,
+                                         exec_env,
+                                         function,
+                                         frame);
+        }
+        else
+#endif
+        {
+            LOG_DEBUG("it is an native function");
+            wasm_interp_call_func_native(module_inst,
+                                         exec_env,
+                                         function,
+                                         frame);
+        }
+    }
+    else {
         wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame);
+    }
 
     /* Output the return value to the caller */
     if (!wasm_get_exception(module_inst)) {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 793 - 84
core/iwasm/interpreter/wasm_loader.c


+ 11 - 2
core/iwasm/interpreter/wasm_opcode.h

@@ -262,7 +262,7 @@ typedef enum WASMOpcode {
     WASM_OP_MISC_PREFIX           = 0xfc,
 } WASMOpcode;
 
-typedef enum WASMEXTOpcode {
+typedef enum WASMMiscEXTOpcode {
     WASM_OP_I32_TRUNC_SAT_S_F32   = 0x00,
     WASM_OP_I32_TRUNC_SAT_U_F32   = 0x01,
     WASM_OP_I32_TRUNC_SAT_S_F64   = 0x02,
@@ -271,7 +271,16 @@ typedef enum WASMEXTOpcode {
     WASM_OP_I64_TRUNC_SAT_U_F32   = 0x05,
     WASM_OP_I64_TRUNC_SAT_S_F64   = 0x06,
     WASM_OP_I64_TRUNC_SAT_U_F64   = 0x07,
-} WASMEXTOpcode;
+#if WASM_ENABLE_BULK_MEMORY != 0
+    WASM_OP_MEMORY_INIT           = 0x08,
+    WASM_OP_DATA_DROP             = 0x09,
+    WASM_OP_MEMORY_COPY           = 0x0a,
+    WASM_OP_MEMORY_FILL           = 0x0b,
+    WASM_OP_TABLE_INIT            = 0x0c,
+    WASM_OP_ELEM_DROP             = 0x0d,
+    WASM_OP_TABLE_COPY            = 0x0e
+#endif
+} WASMMiscEXTOpcode;
 
 #ifdef __cplusplus
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 590 - 166
core/iwasm/interpreter/wasm_runtime.c


+ 81 - 2
core/iwasm/interpreter/wasm_runtime.h

@@ -15,6 +15,12 @@
 extern "C" {
 #endif
 
+typedef struct WASMModuleInstance WASMModuleInstance;
+typedef struct WASMFunctionInstance WASMFunctionInstance;
+typedef struct WASMMemoryInstance WASMMemoryInstance;
+typedef struct WASMTableInstance WASMTableInstance;
+typedef struct WASMGlobalInstance WASMGlobalInstance;
+
 typedef struct WASMMemoryInstance {
     /* Number bytes per page */
     uint32 num_bytes_per_page;
@@ -36,6 +42,10 @@ typedef struct WASMMemoryInstance {
     /* End address of memory */
     uint8 *end_addr;
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+    /* to indicate which module instance create it */
+    WASMModuleInstance *owner;
+#endif
     /* Base address, the layout is:
        heap_data + memory data
        memory data init size is: num_bytes_per_page * cur_page_count
@@ -52,6 +62,10 @@ typedef struct WASMTableInstance {
     uint32 cur_size;
     /* Maximum size */
     uint32 max_size;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    /* just for import, keep the reference here */
+    WASMTableInstance *table_inst_linked;
+#endif
     /* Base address */
     uint8 base_addr[1];
 } WASMTableInstance;
@@ -65,6 +79,11 @@ typedef struct WASMGlobalInstance {
     uint32 data_offset;
     /* initial value */
     WASMValue initial_value;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    /* just for import, keep the reference here */
+    WASMModuleInstance *import_module_inst;
+    WASMGlobalInstance *import_global_inst;
+#endif
 } WASMGlobalInstance;
 
 typedef struct WASMFunctionInstance {
@@ -93,6 +112,10 @@ typedef struct WASMFunctionInstance {
         WASMFunctionImport *func_import;
         WASMFunction *func;
     } u;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMModuleInstance *import_module_inst;
+    WASMFunctionInstance *import_func_inst;
+#endif
 } WASMFunctionInstance;
 
 typedef struct WASMExportFuncInstance {
@@ -100,6 +123,23 @@ typedef struct WASMExportFuncInstance {
     WASMFunctionInstance *function;
 } WASMExportFuncInstance;
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+typedef struct WASMExportGlobInstance {
+    char *name;
+    WASMGlobalInstance *global;
+} WASMExportGlobInstance;
+
+typedef struct WASMExportTabInstance {
+    char *name;
+    WASMTableInstance *table;
+} WASMExportTabInstance;
+
+typedef struct WASMExportMemInstance {
+    char *name;
+    WASMMemoryInstance *memory;
+} WASMExportMemInstance;
+#endif
+
 typedef struct WASMModuleInstance {
     /* Module instance type, for module instance loaded from
        WASM bytecode binary, this field is Wasm_Module_Bytecode;
@@ -112,13 +152,25 @@ typedef struct WASMModuleInstance {
     uint32 table_count;
     uint32 global_count;
     uint32 function_count;
+
     uint32 export_func_count;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    uint32 export_glob_count;
+    uint32 export_mem_count;
+    uint32 export_tab_count;
+#endif
 
     WASMMemoryInstance **memories;
     WASMTableInstance **tables;
     WASMGlobalInstance *globals;
     WASMFunctionInstance *functions;
+
     WASMExportFuncInstance *export_functions;
+#if WASM_ENABLE_MULTI_MODULE != 0
+    WASMExportGlobInstance *export_globals;
+    WASMExportMemInstance *export_memories;
+    WASMExportTabInstance *export_tables;
+#endif
 
     WASMMemoryInstance *default_memory;
     WASMTableInstance *default_table;
@@ -148,11 +200,26 @@ typedef struct WASMModuleInstance {
 
     /* Main exec env */
     WASMExecEnv *main_exec_env;
+
+#if WASM_ENABLE_MULTI_MODULE != 0
+    // TODO: mutex ? mutli-threads ?
+    bh_list sub_module_inst_list_head;
+    bh_list *sub_module_inst_list;
+#endif
 } WASMModuleInstance;
 
 struct WASMInterpFrame;
 typedef struct WASMInterpFrame WASMRuntimeFrame;
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+typedef struct WASMSubModInstNode {
+    bh_list_link l;
+    /* point to a string pool */
+    const char *module_name;
+    WASMModuleInstance *module_inst;
+} WASMSubModInstNode;
+#endif
+
 /**
  * Return the code block of a function.
  *
@@ -182,10 +249,11 @@ wasm_get_func_code_end(WASMFunctionInstance *func)
 {
 #if WASM_ENABLE_FAST_INTERP == 0
     return func->is_import_func
-           ? NULL : func->u.func->code + func->u.func->code_size;
+             ? NULL : func->u.func->code + func->u.func->code_size;
 #else
     return func->is_import_func
-           ? NULL : func->u.func->code_compiled + func->u.func->code_compiled_size;
+             ? NULL
+             : func->u.func->code_compiled + func->u.func->code_compiled_size;
 #endif
 }
 
@@ -212,6 +280,17 @@ WASMFunctionInstance *
 wasm_lookup_function(const WASMModuleInstance *module_inst,
                              const char *name, const char *signature);
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+WASMGlobalInstance *
+wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name);
+
+WASMMemoryInstance *
+wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name);
+
+WASMTableInstance *
+wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name);
+#endif
+
 bool
 wasm_call_function(WASMExecEnv *exec_env,
                    WASMFunctionInstance *function,

+ 1 - 1
core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c

@@ -1098,7 +1098,7 @@ static NativeSymbol native_symbols_spectest[] = {
     REG_NATIVE_FUNC(print, "()"),
     REG_NATIVE_FUNC(print_i32, "(i)"),
     REG_NATIVE_FUNC(print_i32_f32, "(if)"),
-    REG_NATIVE_FUNC(print_f64_f64, "(fF)"),
+    REG_NATIVE_FUNC(print_f64_f64, "(FF)"),
     REG_NATIVE_FUNC(print_f32, "(f)"),
     REG_NATIVE_FUNC(print_f64, "(F)")
 };

+ 13 - 33
core/shared/platform/common/posix/posix_memmap.c

@@ -11,13 +11,11 @@ os_mmap(void *hint, uint32 size, int prot, int flags)
     int map_prot = PROT_NONE;
     int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
     uint64 request_size, page_size;
-    uint8 *addr, *addr_aligned;
+    uint8 *addr;
     uint32 i;
 
-    /* align to 2M if no less than 2M, else align to 4K */
-    page_size = size < 2 * 1024 * 1024 ? 4096 : 2 * 1024 * 1024;
+    page_size = getpagesize();
     request_size = (size + page_size - 1) & ~(page_size - 1);
-    request_size += page_size;
 
     if (request_size >= UINT32_MAX)
         return NULL;
@@ -47,43 +45,25 @@ os_mmap(void *hint, uint32 size, int prot, int flags)
         if (addr != MAP_FAILED)
             break;
     }
+
     if (addr == MAP_FAILED)
         return NULL;
 
-    addr_aligned = (uint8*)(uintptr_t)
-        (((uint64)(uintptr_t)addr + page_size - 1) & ~(page_size - 1));
-
-    /* Unmap memory allocated before the aligned base address */
-    if (addr != addr_aligned) {
-        uint32 prefix_size = (uint32)(addr_aligned - addr);
-        munmap(addr, prefix_size);
-        request_size -= prefix_size;
-    }
-
-    /* Unmap memory allocated after the potentially unaligned end */
-    if (size != request_size) {
-        uint32 suffix_size = (uint32)(request_size - size);
-        munmap(addr_aligned + size, suffix_size);
-        request_size -= size;
-    }
-
-#ifndef __APPLE__
-    if (size >= 2 * 1024 * 1024) {
-        /* Try to use huge page to improve performance */
-        if (!madvise(addr, size, MADV_HUGEPAGE))
-            /* make huge page become effective */
-            memset(addr, 0, size);
-    }
-#endif
-
-    return addr_aligned;
+    return addr;
 }
 
 void
 os_munmap(void *addr, uint32 size)
 {
-    if (addr)
-        munmap(addr, size);
+    uint64 page_size = getpagesize();
+    uint64 request_size = (size + page_size - 1) & ~(page_size - 1);
+
+    if (addr) {
+        if (munmap(addr, request_size)) {
+            os_printf("os_munmap error addr:%p, size:0x%lx, errno:%d\n",
+                      addr, request_size, errno);
+        }
+    }
 }
 
 int

+ 1 - 2
core/shared/platform/common/posix/posix_thread.c

@@ -249,5 +249,4 @@ uint8 *os_thread_get_stack_boundary()
 #endif
 
     return addr;
-}
-
+}

+ 25 - 10
core/shared/platform/linux-sgx/sgx_platform.c

@@ -82,29 +82,39 @@ int os_vprintf(const char * format, va_list arg)
     return 0;
 }
 
-void* os_mmap(void *hint, unsigned int size, int prot, int flags)
+void* os_mmap(void *hint, uint32 size, int prot, int flags)
 {
 #if WASM_ENABLE_AOT != 0
     int mprot = 0;
-    unsigned alignedSize = (size+4095) & (unsigned)~4095; //Page aligned
+    uint64 aligned_size, page_size;
     void* ret = NULL;
     sgx_status_t st = 0;
 
-    ret = sgx_alloc_rsrv_mem(alignedSize);
+    page_size = getpagesize();
+    aligned_size = (size + page_size - 1) & ~(page_size - 1);
+
+    if (aligned_size >= UINT32_MAX)
+        return NULL;
+
+    ret = sgx_alloc_rsrv_mem(aligned_size);
     if (ret == NULL) {
-        os_printf("os_mmap(size=%d, alignedSize=%d, prot=0x%x) failed.",size, alignedSize, prot);
+        os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.",
+                  size, aligned_size, prot);
         return NULL;
     }
+
     if (prot & MMAP_PROT_READ)
         mprot |= SGX_PROT_READ;
     if (prot & MMAP_PROT_WRITE)
         mprot |= SGX_PROT_WRITE;
     if (prot & MMAP_PROT_EXEC)
         mprot |= SGX_PROT_EXEC;
-    st = sgx_tprotect_rsrv_mem(ret, alignedSize, mprot);
-    if (st != SGX_SUCCESS){
-        os_printf("os_mmap(size=%d,prot=0x%x) failed to set protect.",size, prot);
-        sgx_free_rsrv_mem(ret, alignedSize);
+
+    st = sgx_tprotect_rsrv_mem(ret, aligned_size, mprot);
+    if (st != SGX_SUCCESS) {
+        os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.",
+                  size, prot);
+        sgx_free_rsrv_mem(ret, aligned_size);
         return NULL;
     }
 
@@ -117,7 +127,11 @@ void* os_mmap(void *hint, unsigned int size, int prot, int flags)
 void os_munmap(void *addr, uint32 size)
 {
 #if WASM_ENABLE_AOT != 0
-    sgx_free_rsrv_mem(addr, size);
+    uint64 aligned_size, page_size;
+
+    page_size = getpagesize();
+    aligned_size = (size + page_size - 1) & ~(page_size - 1);
+    sgx_free_rsrv_mem(addr, aligned_size);
 #endif
 }
 
@@ -135,7 +149,8 @@ int os_mprotect(void *addr, uint32 size, int prot)
         mprot |= SGX_PROT_EXEC;
     st = sgx_tprotect_rsrv_mem(addr, size, mprot);
     if (st != SGX_SUCCESS)
-        os_printf("os_mprotect(addr=0x%lx,size=%d,prot=0x%x) failed.", addr, size, prot);
+        os_printf("os_mprotect(addr=0x%lx, size=%u, prot=0x%x) failed.",
+                  addr, size, prot);
 
     return (st == SGX_SUCCESS? 0:-1);
 #else

+ 20 - 0
core/shared/utils/bh_common.c

@@ -32,6 +32,26 @@ b_memcpy_s(void * s1, unsigned int s1max,
   return 0;
 }
 
+int b_memmove_s(void * s1, unsigned int s1max,
+                const void * s2, unsigned int n)
+{
+  char *dest = (char*)s1;
+  char *src = (char*)s2;
+  if (n == 0) {
+    return 0;
+  }
+
+  if (s1 == NULL || s1max > RSIZE_MAX) {
+    return -1;
+  }
+  if (s2 == NULL || n > s1max) {
+    memset(dest, 0, s1max);
+    return -1;
+  }
+  memmove(dest, src, n);
+  return 0;
+}
+
 int
 b_strcat_s(char * s1, unsigned int s1max, const char * s2)
 {

+ 7 - 0
core/shared/utils/bh_common.h

@@ -18,6 +18,12 @@ extern "C" {
     bh_assert (_ret == 0);                                              \
   } while (0)
 
+#define bh_memmove_s(dest, dlen, src, slen) do {                        \
+    int _ret = slen == 0 ? 0 : b_memmove_s (dest, dlen, src, slen);     \
+    (void)_ret;                                                         \
+    bh_assert (_ret == 0);                                              \
+  } while (0)
+
 #define bh_strcat_s(dest, dlen, src) do {                               \
     int _ret = b_strcat_s (dest, dlen, src);                            \
     (void)_ret;                                                         \
@@ -31,6 +37,7 @@ extern "C" {
   } while (0)
 
 int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n);
+int b_memmove_s(void * s1, unsigned int s1max, const void * s2, unsigned int n);
 int b_strcat_s(char * s1, unsigned int s1max, const char * s2);
 int b_strcpy_s(char * s1, unsigned int s1max, const char * s2);
 

+ 11 - 1
core/shared/utils/bh_log.h

@@ -41,12 +41,22 @@ bh_log_set_verbose_level(uint32 level);
 void
 bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...);
 
+#if BH_DEBUG == 1
 #define LOG_FATAL(...)   bh_log(BH_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__)
+#else
+#define LOG_FATAL(...)   bh_log(BH_LOG_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__)
+#endif
+
 #define LOG_ERROR(...)   bh_log(BH_LOG_LEVEL_ERROR, NULL, 0, __VA_ARGS__)
-#define LOG_DEBUG(...)   bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, 0, __VA_ARGS__)
 #define LOG_WARNING(...) bh_log(BH_LOG_LEVEL_WARNING, NULL, 0, __VA_ARGS__)
 #define LOG_VERBOSE(...) bh_log(BH_LOG_LEVEL_VERBOSE, NULL, 0, __VA_ARGS__)
 
+#if BH_DEBUG == 1
+#define LOG_DEBUG(...)   bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
+#else
+#define LOG_DEBUG(...)   /* do nothing */
+#endif
+
 void
 bh_print_time(const char *prompt);
 

+ 1 - 1
core/shared/utils/runtime_timer.h

@@ -17,7 +17,7 @@ uint32 bh_get_elpased_ms(uint32 *last_system_clock);
 
 struct _timer_ctx;
 typedef struct _timer_ctx * timer_ctx_t;
-typedef void (*timer_callback_f)(uint32 id, unsigned int owner);
+typedef void (*timer_callback_f)(unsigned int id, unsigned int owner);
 typedef void (*check_timer_expiry_f)(timer_ctx_t ctx);
 
 timer_ctx_t create_timer_ctx(timer_callback_f timer_handler,

+ 16 - 14
doc/build_wamr.md

@@ -7,7 +7,7 @@ It is recommended to use the [WAMR SDK](../wamr-sdk) tools to build a project th
 
 ## iwasm VM core CMake building configurations
 
-By including the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in CMakeList.txt, it is easy to build minimal product with CMake. 
+By including the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in CMakeList.txt, it is easy to build minimal product with CMake.
 
 ```cmake
 # add this in your CMakeList.text
@@ -21,19 +21,19 @@ The script `runtime_lib.cmake` defined a number of variables for configuring the
 
 #### **Configure platform and architecture**
 
-- **WAMR_BUILD_PLATFORM**:  set the target platform. It can be set to any platform name (folder name) under folder [core/shared/platform](../core/shared/platform). 
+- **WAMR_BUILD_PLATFORM**:  set the target platform. It can be set to any platform name (folder name) under folder [core/shared/platform](../core/shared/platform).
 
 - **WAMR_BUILD_TARGET**: set the target CPU architecture. Current supported targets:  X86_64, X86_32, AArch64, ARM, THUMB, XTENSA and MIPS. For AArch64, ARM and THUMB, the format is <arch>[<sub-arch>][_VFP] where <sub-arch> is the ARM sub-architecture and the "_VFP" suffix means VFP coprocessor registers s0-s15 (d0-d7) are used for passing arguments or returning results in standard procedure-call. Both <sub-arch> and "_VFP" are optional. e.g. AARCH64, AARCH64V8, AARCHV8.1, ARMV7, ARMV7_VFP, THUMBV7, THUMBV7_VFP and so on.
 
   ```bash
-  cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM  
+  cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM
   ```
 
-#### **Configure interpreter** 
+#### **Configure interpreter**
 
 - **WAMR_BUILD_INTERP**=1/0:  enable or disable WASM interpreter
 
-- **WAMR_BUILD_FAST_INTERP**=1/0:build fast (default) or classic WASM interpreter. 
+- **WAMR_BUILD_FAST_INTERP**=1/0:build fast (default) or classic WASM interpreter.
 
   NOTE: the fast interpreter will run ~2X faster than classic interpreter, but it consumes about 2X memory to hold the WASM bytecode code.
 
@@ -48,14 +48,16 @@ The script `runtime_lib.cmake` defined a number of variables for configuring the
 
 - **WAMR_BUILD_LIBC_WASI**=1/0, default to disable if no set
 
-  
+#### **Enable Multi-Module feature**
+
+- **WAMR_BUILD_MULTI_MODULE**=1/0, default to disable if not set
 
 **Combination of configurations:**
 
 We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command:
 
 ``` Bash
-cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_PLATFORM=linux 
+cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_PLATFORM=linux
 ```
 
 Or if we want to enable interpreter, disable AOT and WASI, and build as X86_32, we can run command:
@@ -73,7 +75,7 @@ If you are building for ARM architecture on a X86 development machine, you can u
 ```
 cmake .. -DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE  \
          -DWAMR_BUILD_PLATFORM=linux    \
-         -DWAMR_BUILD_TARGET=ARM  
+         -DWAMR_BUILD_TARGET=ARM
 ```
 
 Refer to toochain sample file [`samples/simple/profiles/arm-interp/toolchain.cmake`](../samples/simple/profiles/arm-interp/toolchain.cmake) for how to build mini product for ARM target architecture.
@@ -260,7 +262,7 @@ AliOS-Things
    ```
 7. build source code and run
    For linux host:
-   
+
    ``` Bash
    aos make helloworld@linuxhost -c config
    aos make
@@ -269,7 +271,7 @@ AliOS-Things
 
    For developerkit:
    Modify file middleware/iwasm/aos.mk, patch as:
-   
+
    ``` C
    WAMR_BUILD_TARGET := THUMBV7M
    ```
@@ -285,11 +287,11 @@ Android
 able to generate a shared library support Android platform.
 - need an [android SDK](https://developer.android.com/studio). Go and get the "Command line tools only"
 - look for a command named *sdkmanager* and download below components. version numbers might need to check and pick others
-   - "build-tools;29.0.3" 
-   - "cmake;3.10.2.4988404" 
-   - "ndk;21.0.6113669" 
+   - "build-tools;29.0.3"
+   - "cmake;3.10.2.4988404"
+   - "ndk;21.0.6113669"
    - "patcher;v4"
-   - "platform-tools" 
+   - "platform-tools"
    - "platforms;android-29"
 - add bin/ of the downloaded cmake to $PATH
 - export ANDROID_SDK_HOME=/the/path/of/downloaded/sdk/

+ 228 - 0
doc/multi_module.md

@@ -0,0 +1,228 @@
+Multiple Modules as Dependencies
+=========================
+
+It is allowed that one WASM module can *import* *functions*, *globals*, *memories* and *tables* from other modules as its dependencies, and also one module can *export* those entities for other modules to *access* and may *write*.
+
+WAMR loads all dependencies recursively according to the *import section* of a module.
+
+> Currently WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details.
+
+## Multi-Module Related APIs
+
+### Register a module
+
+``` c
+bool
+wasm_runtime_register_module(const char *module_name,
+                             wasm_module_t module,
+                             char *error_buf,
+                             uint32_t error_buf_size);
+```
+
+It is used to register a *module* with a *module_name* to WASM runtime, especially for the root module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its *module name*.
+
+Fot all the sub modules, WAMR will get their names and load the .wasm files from the filesystem or stream, so no need to register the sub modules again.
+
+### Find a registered module
+
+``` c
+wasm_module_t
+wasm_runtime_find_module_registered(
+    const char *module_name);
+```
+
+It is used to check if a module with a given *module_name* has been registered, if yes return the module.
+
+### Module reader and destroyer
+
+``` c
+typedef bool (*module_reader)(const char *module_name,
+                              uint8_t **p_buffer,
+                              uint32_t *p_size);
+
+typedef void (*module_destroyer)(uint8_t *buffer,
+                                 uint32_t size);
+
+void
+wasm_runtime_set_module_reader(const module_reader reader,
+                               const module_destroyer destroyer);
+```
+
+WAMR hopes that the native host or embedding environment loads/unloads the module WASM files by themselves and only passes runtime the binary content without worrying filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading the sub modules. Developers must implement the two callbacks by themselves.
+
+### Call function of sub module
+
+```c
+wasm_function_inst_t
+wasm_runtime_lookup_function(wasm_module_inst_t const module_inst,
+                             const char *name,
+                             const char *signature);
+```
+
+Multi-module allows to lookup the function of sub module and call it. There are two ways to indicate the function *name*:
+
+- parent function name only by default, used to lookup the function of parent module
+- sub module name, function name of sub module and two $ symbols, e.g. `$sub_module_name$function_name`, used to lookup function of sub module
+
+## Example
+
+### WASM modules
+Suppose we have three C files, *mA.c*, *mB.c* and *mC.c*. Each of them has some exported functions and import some from others except mA.
+
+Undefined symbols can be marked in the source code with the *import_name* clang attribute which means that they are expected to be undefined at static link time. Without the *import_module* clang attribute, undefined symbols will be marked from the *env* module.
+
+``` C
+// mA.c
+int A() { return 10; }
+```
+
+``` C
+// mB.c
+__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A();
+int B() { return 11; }
+int call_A() { return A(); }
+```
+
+``` C
+// mC.c
+__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A();
+__attribute__((import_module("mB"))) __attribute__((import_name("B"))) extern int B();
+int C() { return 12; }
+int call_A() { return A(); }
+int call_B() { return B(); }
+```
+
+By default no undefined symbols are allowed in the final binary. The flag *--allow-undefined* results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols.
+
+When building an executable, only the entry point (_start) and symbols with the *export_name* attribute  exported by default. in addition, symbols can be exported via the linker command line using *--export*.
+
+In the example, another linked command option *--export-all* is used.
+
+> with more detail, please refer to [WebAssembly lld port][https://lld.llvm.org/WebAssembly.html]
+
+Here is an example how to compile a *.c* to a *.wasm* with clang. Since there is no *start* function, we use *--no-entry* option.
+
+``` shell
+$ clang --target=wasm32 -nostdlib \
+    -Wl,--no-entry,--allow-undefined,--export-all \
+    -o mA.wasm mA.c
+$ clang --target=wasm32 -nostdlib \
+    -Wl,--no-entry,--allow-undefined,--export-all \
+    -o mB.wasm mB.c
+$ clang --target=wasm32 -nostdlib \
+    -Wl,--no-entry,--allow-undefined,--export-all \
+    -o mC.wasm mC.c
+```
+
+put *mA.wasm*, *mB.wasm* and *mC.wasm* in the directory *wasm-apps*
+
+``` shell
+$ # copy mA.wasm, mB.wasm and mC.wasm into wasm-apps
+$ tree wasm-apps/
+wasm-apps/
+├── mA.wasm
+├── mB.wasm
+└── mC.wasm
+```
+
+eventually, their *import relationships* will be like:
+
+![import relationships](./pics/multi_module_pic1.png)
+
+### libvmlib
+
+We need to enable *WAMR_BUILD_MULTI_MODULE* option when building WAMR vmlib. Please ref to [Build WAMR core](./build_wamr.md) for a thoughtful guide.
+
+### code
+
+After all above preparation, we can call some functions from native code with APIs
+
+first, create two callbacks to load WASM module files into memory and unload them later
+
+``` c
+static bool
+module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size)
+{
+  // ...
+  *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size);
+  // ...
+}
+
+static void
+module_destroyer_cb(uint8 *buffer, uint32 size)
+{
+  BH_FREE(buffer);
+}
+```
+
+second, create a large buffer and tell WAMR malloc any resource only from this buffer later
+
+``` c
+static char sandbox_memory_space[10 * 1024 * 1024] = { 0 };
+```
+
+third, put all together
+
+``` c
+int main()
+{
+  /* all malloc() only from the given buffer */
+  init_args.mem_alloc_type = Alloc_With_Pool;
+  init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space;
+  init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space);
+
+  /* initialize runtime environment */
+  wasm_runtime_full_init(&init_args);
+
+  /* set module reader and destroyer */
+  wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb);
+
+  /* load WASM byte buffer from WASM bin file */
+  module_reader_cb("mC", &file_buf, &file_buf_size));
+
+  /* load mC and let WAMR load mA and mB */
+  module = wasm_runtime_load(file_buf, file_buf_size,
+                             error_buf, sizeof(error_buf));
+
+  /* instantiate the module */
+  module_inst =
+          wasm_runtime_instantiate(module, stack_size,
+          heap_size, error_buf, sizeof(error_buf)));
+
+
+  printf("call \"C\", it will return 0xc:i32, ===> ");
+  wasm_application_execute_func(module_inst, "C", 0, &args[0]);
+  printf("call \"call_B\", it will return 0xb:i32, ===> ");
+  wasm_application_execute_func(module_inst, "call_B", 0, &args[0]);
+  printf("call \"call_A\", it will return 0xa:i32, ===>");
+  wasm_application_execute_func(module_inst, "call_A", 0, &args[0]);
+
+  /* call some functions of mB */
+  printf("call \"mB.B\", it will return 0xb:i32, ===>");
+  wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]);
+  printf("call \"mB.call_A\", it will return 0xa:i32, ===>");
+  wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]);
+
+  /* call some functions of mA */
+  printf("call \"mA.A\", it will return 0xa:i32, ===>");
+  wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]);
+
+  // ...
+}
+```
+
+> please refer to [main.c](../samples/multi_modules/src/main.c)
+
+The output of the main.c will like:
+
+``` shell
+$ ./a.out
+
+call "C", it will return 0xc:i32, ===> 0xc:i32
+call "call_B", it will return 0xb:i32, ===> 0xb:i32
+call "call_A", it will return 0xa:i32, ===>0xa:i32
+call "mB.B", it will return 0xb:i32, ===>0xb:i32
+call "mB.call_A", it will return 0xa:i32, ===>0xa:i32
+call "mA.A", it will return 0xa:i32, ===>0xa:i32
+
+```

BIN
doc/pics/multi_module_pic1.png


+ 8 - 0
product-mini/platforms/linux/CMakeLists.txt

@@ -4,6 +4,7 @@
 cmake_minimum_required (VERSION 2.8)
 
 project (iwasm)
+# set (CMAKE_VERBOSE_MAKEFILE 1)
 
 set (WAMR_BUILD_PLATFORM "linux")
 
@@ -11,6 +12,8 @@ set (WAMR_BUILD_PLATFORM "linux")
 set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
 
+set (CMAKE_C_STANDARD 99)
+
 # Set WAMR_BUILD_TARGET, currently values supported:
 # "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA"
 if (NOT DEFINED WAMR_BUILD_TARGET)
@@ -57,6 +60,11 @@ if (NOT DEFINED WAMR_BUILD_FAST_INTERP)
   set (WAMR_BUILD_FAST_INTERP 1)
 endif ()
 
+if (NOT DEFINED WAMR_BUILD_MULTI_MODULE)
+  # Enable multiple modules
+  set (WAMR_BUILD_MULTI_MODULE 0)
+endif ()
+
 set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
 
 include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)

+ 102 - 32
product-mini/platforms/linux/main.c

@@ -8,24 +8,26 @@
 #endif
 #include <stdlib.h>
 #include <string.h>
+
 #include "bh_platform.h"
-#include "bh_assert.h"
-#include "bh_log.h"
 #include "bh_read_file.h"
 #include "wasm_export.h"
 
 static int app_argc;
 static char **app_argv;
 
-static int print_help()
+#define MODULE_PATH ("--module-path=")
+
+static int
+print_help()
 {
     printf("Usage: iwasm [-options] wasm_file [args...]\n");
     printf("options:\n");
-    printf("  -f|--function name     Specify function name to run in module\n"
-           "                         rather than main\n");
+    printf("  -f|--function name     Specify a function name of the module to run rather\n"
+           "                         than main\n");
 #if WASM_ENABLE_LOG != 0
-    printf("  -v=n                   Set log verbose level (0 to 5, default is 2),\n"
-           "                         larger level with more log\n");
+    printf("  -v=n                   Set log verbose level (0 to 5, default is 2) larger\n"
+           "                         level with more log\n");
 #endif
     printf("  --stack-size=n         Set maximum stack size in bytes, default is 16 KB\n");
     printf("  --heap-size=n          Set maximum heap size in bytes, default is 16 KB\n");
@@ -39,11 +41,14 @@ static int print_help()
     printf("                         to the program, for example:\n");
     printf("                           --dir=<dir1> --dir=<dir2>\n");
 #endif
-
+#if WASM_ENABLE_MULTI_MODULE != 0
+    printf("  --module-path=         Indicate a module search path. default is current\n"
+           "                         directory('./')\n");
+#endif
     return 1;
 }
 
-static void*
+static void *
 app_instance_main(wasm_module_inst_t module_inst)
 {
     const char *exception;
@@ -54,7 +59,7 @@ app_instance_main(wasm_module_inst_t module_inst)
     return NULL;
 }
 
-static void*
+static void *
 app_instance_func(wasm_module_inst_t module_inst, const char *func_name)
 {
     wasm_application_execute_func(module_inst, func_name, app_argc - 1,
@@ -81,20 +86,31 @@ split_string(char *str, int *count)
     do {
         p = strtok(str, " ");
         str = NULL;
-        res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1));
+        res = (char **)realloc(res, sizeof(char *) * (uint32)(idx + 1));
         if (res == NULL) {
             return NULL;
         }
         res[idx++] = p;
     } while (p);
 
+    /**
+     * since the function name,
+     * res[0] might be contains a '\' to indicate a space
+     * func\name -> func name
+     */
+    p = strchr(res[0], '\\');
+    while (p) {
+        *p = ' ';
+        p = strchr(p, '\\');
+    }
+
     if (count) {
         *count = idx - 1;
     }
     return res;
 }
 
-static void*
+static void *
 app_instance_repl(wasm_module_inst_t module_inst)
 {
     char *cmd = NULL;
@@ -149,7 +165,49 @@ validate_env_str(char *env)
 static char global_heap_buf[10 * 1024 * 1024] = { 0 };
 #endif
 
-int main(int argc, char *argv[])
+#if WASM_ENABLE_MULTI_MODULE != 0
+static char *
+handle_module_path(const char *module_path)
+{
+    // next character after =
+    return (strchr(module_path, '=')) + 1;
+}
+
+static char *module_search_path = ".";
+static bool
+module_reader_callback(const char *module_name, uint8 **p_buffer,
+                       uint32 *p_size)
+{
+    const char *format = "%s/%s.wasm";
+    int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) +
+             strlen(".wasm") + 1;
+    char *wasm_file_name = BH_MALLOC(sz);
+    if (!wasm_file_name) {
+        return false;
+    }
+
+    snprintf(wasm_file_name, sz, format, module_search_path, module_name);
+
+    *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size);
+
+    wasm_runtime_free(wasm_file_name);
+    return *p_buffer != NULL;
+}
+
+static void
+moudle_destroyer(uint8 *buffer, uint32 size)
+{
+    if (!buffer) {
+        return;
+    }
+
+    wasm_runtime_free(buffer);
+    buffer = NULL;
+}
+#endif /* WASM_ENABLE_MULTI_MODULE */
+
+int
+main(int argc, char *argv[])
 {
     char *wasm_file = NULL;
     const char *func_name = NULL;
@@ -172,6 +230,8 @@ int main(int argc, char *argv[])
 #endif
 
     /* Process options.  */
+    // TODO: use a option name and option handler pair table to
+    //       optimize
     for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
         if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) {
             argc--, argv++;
@@ -188,8 +248,9 @@ int main(int argc, char *argv[])
                 return print_help();
         }
 #endif
-        else if (!strcmp(argv[0], "--repl"))
+        else if (!strcmp(argv[0], "--repl")) {
             is_repl_mode = true;
+        }
         else if (!strncmp(argv[0], "--stack-size=", 13)) {
             if (argv[0][13] == '\0')
                 return print_help();
@@ -204,9 +265,9 @@ int main(int argc, char *argv[])
         else if (!strncmp(argv[0], "--dir=", 6)) {
             if (argv[0][6] == '\0')
                 return print_help();
-            if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) {
+            if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) {
                 printf("Only allow max dir number %d\n",
-                       (int)(sizeof(dir_list) / sizeof(char*)));
+                       (int)(sizeof(dir_list) / sizeof(char *)));
                 return -1;
             }
             dir_list[dir_list_size++] = argv[0] + 6;
@@ -216,20 +277,29 @@ int main(int argc, char *argv[])
 
             if (argv[0][6] == '\0')
                 return print_help();
-            if (env_list_size >= sizeof(env_list) / sizeof(char*)) {
+            if (env_list_size >= sizeof(env_list) / sizeof(char *)) {
                 printf("Only allow max env number %d\n",
-                       (int)(sizeof(env_list) / sizeof(char*)));
+                       (int)(sizeof(env_list) / sizeof(char *)));
                 return -1;
             }
             tmp_env = argv[0] + 6;
             if (validate_env_str(tmp_env))
                 env_list[env_list_size++] = tmp_env;
             else {
-                printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n",
+                printf("Wasm parse env string failed: expect \"key=value\", "
+                       "got \"%s\"\n",
                        tmp_env);
                 return print_help();
             }
         }
+#endif /* WASM_ENABLE_LIBC_WASI */
+#if WASM_ENABLE_MULTI_MODULE != 0
+        else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) {
+            module_search_path = handle_module_path(argv[0]);
+            if (!strlen(module_search_path)) {
+                return print_help();
+            }
+        }
 #endif
         else
             return print_help();
@@ -261,13 +331,19 @@ int main(int argc, char *argv[])
         return -1;
     }
 
+#if WASM_ENABLE_LOG != 0
     bh_log_set_verbose_level(log_verbose_level);
+#endif
 
     /* load WASM byte buffer from WASM bin file */
-    if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file,
-                                                          &wasm_file_size)))
+    if (!(wasm_file_buf =
+            (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size)))
         goto fail1;
 
+#if WASM_ENABLE_MULTI_MODULE != 0
+    wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer);
+#endif
+
     /* load WASM module */
     if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
                                           error_buf, sizeof(error_buf)))) {
@@ -276,19 +352,14 @@ int main(int argc, char *argv[])
     }
 
 #if WASM_ENABLE_LIBC_WASI != 0
-    wasm_runtime_set_wasi_args(wasm_module,
-                               dir_list, dir_list_size,
-                               NULL, 0,
-                               env_list, env_list_size,
-                               argv, argc);
+    wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0,
+                               env_list, env_list_size, argv, argc);
 #endif
 
     /* instantiate the module */
-    if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module,
-                                                      stack_size,
-                                                      heap_size,
-                                                      error_buf,
-                                                      sizeof(error_buf)))) {
+    if (!(wasm_module_inst =
+            wasm_runtime_instantiate(wasm_module, stack_size, heap_size,
+                                     error_buf, sizeof(error_buf)))) {
         printf("%s\n", error_buf);
         goto fail3;
     }
@@ -316,4 +387,3 @@ fail1:
     wasm_runtime_destroy();
     return 0;
 }
-

+ 51 - 0
samples/multi-module/CMakeLists.txt

@@ -0,0 +1,51 @@
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required(VERSION 2.8)
+project(multi_module)
+set(CMAKE_VERBOSE_MAKEFILE on)
+
+################  runtime settings  ################
+set(WAMR_BUILD_PLATFORM "linux")
+
+# Resetdefault linker flags
+set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
+
+# WAMR features switch
+set(WAMR_BUILD_TARGET "X86_64")
+set(WAMR_BUILD_INTERP 1)
+set(WAMR_BUILD_AOT 0)
+set(WAMR_BUILD_JIT 0)
+set(WAMR_BUILD_LIBC_BUILTIN 1)
+set(WAMR_BUILD_LIBC_WASI 1)
+set(WAMR_BUILD_FAST_INTERP 0)
+set(WAMR_BUILD_MULTI_MODULE 1)
+
+# compiling and linking flags
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security -mindirect-branch-register")
+
+# build out vmlib
+set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+
+add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE})
+################################################
+
+################  application related  ################
+
+################ WASM MODULES
+# .c -> .wasm
+add_subdirectory(wasm-apps)
+
+################ NATIVE
+include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
+include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
+
+add_executable(multi_module src/main.c ${UNCOMMON_SHARED_SOURCE})
+
+add_dependencies(multi_module vmlib wasm-modules)
+
+# libraries
+target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm)

+ 144 - 0
samples/multi-module/src/main.c

@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bh_read_file.h"
+#include "platform_common.h"
+#include "wasm_export.h"
+
+static char *
+build_module_path(const char *module_name)
+{
+    const char *module_search_path = "./wasm-apps";
+    const char *format = "%s/%s.wasm";
+    int sz = strlen(module_search_path) + strlen("/") + strlen(module_name)
+             + strlen(".wasm") + 1;
+    char *wasm_file_name = BH_MALLOC(sz);
+    if (!wasm_file_name) {
+        return NULL;
+    }
+
+    snprintf(wasm_file_name, sz, format, module_search_path, module_name);
+    return wasm_file_name;
+}
+
+static bool
+module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size)
+{
+    char *wasm_file_path = build_module_path(module_name);
+    if (!wasm_file_path) {
+        return false;
+    }
+
+    printf("- bh_read_file_to_buffer %s\n", wasm_file_path);
+    *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size);
+    BH_FREE(wasm_file_path);
+    return *p_buffer != NULL;
+}
+
+static void
+module_destroyer_cb(uint8 *buffer, uint32 size)
+{
+    printf("- release the read file buffer\n");
+    if (!buffer) {
+        return;
+    }
+
+    BH_FREE(buffer);
+    buffer = NULL;
+}
+
+/* 10M */
+static char sandbox_memory_space[10 * 1024 * 1024] = { 0 };
+int
+main()
+{
+    bool ret = false;
+    /* 16K */
+    const uint32 stack_size = 16 * 1024;
+    const uint32 heap_size = 16 * 1024;
+
+    RuntimeInitArgs init_args = { 0 };
+    char error_buf[128] = { 0 };
+    /* parameters and return values */
+    char* args[1] = { 0 };
+
+    uint8 *file_buf = NULL;
+    uint32 file_buf_size = 0;
+    wasm_module_t module = NULL;
+    wasm_module_inst_t module_inst = NULL;
+
+    /* all malloc() only from the given buffer */
+    init_args.mem_alloc_type = Alloc_With_Pool;
+    init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space;
+    init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space);
+
+    printf("- wasm_runtime_full_init\n");
+    /* initialize runtime environment */
+    if (!wasm_runtime_full_init(&init_args)) {
+        printf("Init runtime environment failed.\n");
+        goto EXIT;
+    }
+
+#if WASM_ENABLE_MULTI_MODULE != 0
+    printf("- wasm_runtime_set_module_reader\n");
+    /* set module reader and destroyer */
+    wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb);
+#endif
+
+    /* load WASM byte buffer from WASM bin file */
+    if (!module_reader_cb("mC", &file_buf, &file_buf_size)) {
+        goto RELEASE_RUNTIME;
+    }
+
+    /* load mC and let WAMR load mA and mB */
+    printf("- wasm_runtime_load\n");
+    if (!(module = wasm_runtime_load(file_buf, file_buf_size,
+                                          error_buf, sizeof(error_buf)))) {
+        printf("%s\n", error_buf);
+        goto RELEASE_BINARY;
+    }
+
+    /* instantiate the module */
+    printf("- wasm_runtime_instantiate\n");
+    if (!(module_inst =
+            wasm_runtime_instantiate(module, stack_size, heap_size,
+                                     error_buf, sizeof(error_buf)))) {
+        printf("%s\n", error_buf);
+        goto UNLOAD_MODULE;
+    }
+
+    /* call some functions of mC */
+    printf("\n----------------------------------------\n");
+    printf("call \"C\", it will return 0xc:i32, ===> ");
+    wasm_application_execute_func(module_inst, "C", 0, &args[0]);
+    printf("call \"call_B\", it will return 0xb:i32, ===> ");
+    wasm_application_execute_func(module_inst, "call_B", 0, &args[0]);
+    printf("call \"call_A\", it will return 0xa:i32, ===>");
+    wasm_application_execute_func(module_inst, "call_A", 0, &args[0]);
+
+    /* call some functions of mB */
+    printf("call \"mB.B\", it will return 0xb:i32, ===>");
+    wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]);
+    printf("call \"mB.call_A\", it will return 0xa:i32, ===>");
+    wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]);
+
+    /* call some functions of mA */
+    printf("call \"mA.A\", it will return 0xa:i32, ===>");
+    wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]);
+    printf("----------------------------------------\n\n");
+    ret = true;
+
+    printf("- wasm_runtime_deinstantiate\n");
+    wasm_runtime_deinstantiate(module_inst);
+UNLOAD_MODULE:
+    printf("- wasm_runtime_unload\n");
+    wasm_runtime_unload(module);
+RELEASE_BINARY:
+    module_destroyer_cb(file_buf, file_buf_size);
+RELEASE_RUNTIME:
+    printf("- wasm_runtime_destroy\n");
+    wasm_runtime_destroy();
+EXIT:
+    return ret ? 0 : 1;
+}

+ 41 - 0
samples/multi-module/wasm-apps/CMakeLists.txt

@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 2.8)
+project(wasm-apps)
+
+set(CMAKE_VERBOSE_MAKEFILE on)
+
+set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
+set(CLANG_COMMAND "/opt/wasi-sdk/bin/clang")
+
+set(CLANG_FLAGS --target=wasm32 -nostdlib)
+set(CLANG_FLAGS ${CLANG_FLAGS} -Wl,--no-entry,--allow-undefined,--export-all)
+
+set(SOURCE_A ${CMAKE_CURRENT_SOURCE_DIR}/mA.c)
+add_custom_command(
+  OUTPUT mA.wasm
+  COMMENT "Transform mA.C to mA.WASM"
+  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mA.wasm ${SOURCE_A}
+  DEPENDS ${SOURCE_A}
+  VERBATIM
+)
+
+set(SOURCE_B ${CMAKE_CURRENT_SOURCE_DIR}/mB.c)
+add_custom_command(
+  OUTPUT mB.wasm
+  COMMENT "Transform mB.C to mB.WASM"
+  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mB.wasm ${SOURCE_B}
+  DEPENDS ${SOURCE_B}
+  VERBATIM
+)
+
+set(SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/mC.c)
+add_custom_command(
+  OUTPUT mC.wasm
+  COMMENT "Transform mC.C to mC.WASM"
+  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mC.wasm ${SOURCE_C}
+  DEPENDS ${SOURCE_C}
+  VERBATIM
+)
+
+add_custom_target(wasm-modules ALL
+  DEPENDS mA.wasm mB.wasm mC.wasm
+)

+ 5 - 0
samples/multi-module/wasm-apps/mA.c

@@ -0,0 +1,5 @@
+int
+A()
+{
+    return 10;
+}

+ 16 - 0
samples/multi-module/wasm-apps/mB.c

@@ -0,0 +1,16 @@
+__attribute__((import_module("mA")))
+__attribute__((import_name("A"))) extern int
+A();
+
+int
+B()
+{
+    return 11;
+}
+
+int
+call_A()
+{
+    return A();
+}
+

+ 25 - 0
samples/multi-module/wasm-apps/mC.c

@@ -0,0 +1,25 @@
+__attribute__((import_module("mA")))
+__attribute__((import_name("A"))) extern int
+A();
+
+__attribute__((import_module("mB")))
+__attribute__((import_name("B"))) extern int
+B();
+
+int
+C()
+{
+    return 12;
+}
+
+int
+call_A()
+{
+    return A();
+}
+
+int
+call_B()
+{
+    return B();
+}

+ 1 - 0
wamr-compiler/CMakeLists.txt

@@ -14,6 +14,7 @@ set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
 
 add_definitions(-DWASM_ENABLE_INTERP=1)
 add_definitions(-DWASM_ENABLE_WAMR_COMPILER=1)
+add_definitions(-DWASM_ENABLE_BULK_MEMORY=1)
 
 # Set WAMR_BUILD_TARGET, currently values supported:
 # "X86_64", "AMD_64", "X86_32", "ARM_32", "MIPS_32", "XTENSA_32"

+ 4 - 0
wamr-compiler/main.c

@@ -35,6 +35,7 @@ print_help()
   printf("                              object         Native object file\n");
   printf("                              llvmir-unopt   Unoptimized LLVM IR\n");
   printf("                              llvmir-opt     Optimized LLVM IR\n");
+  printf("  --enable-bulk-memory      Enable the post-MVP bulk memory feature\n");
   printf("  -v=n                      Set log verbose level (0 to 5, default is 2), larger with more log\n");
   printf("Examples: wamrc -o test.aot test.wasm\n");
   printf("          wamrc --target=i386 -o test.aot test.wasm\n");
@@ -127,6 +128,9 @@ main(int argc, char *argv[])
         if (log_verbose_level < 0 || log_verbose_level > 5)
             return print_help();
     }
+    else if (!strcmp(argv[0], "--enable-bulk-memory")) {
+        option.enable_bulk_memory = true;
+    }
     else
       return print_help();
   }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно