/*
 * Copyright (C) 2019 Intel Corporation. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 */

#include <stdlib.h>
#include "bh_platform.h"
#include "bh_read_file.h"
#include "wasm_export.h"
#include "aot_export.h"

#if BH_HAS_DLFCN
#include <dlfcn.h>

typedef uint32 (*get_native_lib_func)(char **p_module_name,
                                      NativeSymbol **p_native_symbols);

static uint32
load_and_register_native_libs(const char **native_lib_list,
                              uint32 native_lib_count,
                              void **native_handle_list)
{
    uint32 i, native_handle_count = 0, n_native_symbols;
    NativeSymbol *native_symbols;
    char *module_name;
    void *handle;

    for (i = 0; i < native_lib_count; i++) {
        /* open the native library */
        if (!(handle = dlopen(native_lib_list[i], RTLD_NOW | RTLD_GLOBAL))
            && !(handle = dlopen(native_lib_list[i], RTLD_LAZY))) {
            LOG_WARNING("warning: failed to load native library %s",
                        native_lib_list[i]);
            continue;
        }

        /* lookup get_native_lib func */
        get_native_lib_func get_native_lib = dlsym(handle, "get_native_lib");
        if (!get_native_lib) {
            LOG_WARNING("warning: failed to lookup `get_native_lib` function "
                        "from native lib %s",
                        native_lib_list[i]);
            dlclose(handle);
            continue;
        }

        n_native_symbols = get_native_lib(&module_name, &native_symbols);

        /* register native symbols */
        if (!(n_native_symbols > 0 && module_name && native_symbols
              && wasm_runtime_register_natives(module_name, native_symbols,
                                               n_native_symbols))) {
            LOG_WARNING("warning: failed to register native lib %s",
                        native_lib_list[i]);
            dlclose(handle);
            continue;
        }

        native_handle_list[native_handle_count++] = handle;
    }

    return native_handle_count;
}

static void
unregister_and_unload_native_libs(uint32 native_lib_count,
                                  void **native_handle_list)
{
    uint32 i, n_native_symbols;
    NativeSymbol *native_symbols;
    char *module_name;
    void *handle;

    for (i = 0; i < native_lib_count; i++) {
        handle = native_handle_list[i];

        /* lookup get_native_lib func */
        get_native_lib_func get_native_lib = dlsym(handle, "get_native_lib");
        if (!get_native_lib) {
            LOG_WARNING("warning: failed to lookup `get_native_lib` function "
                        "from native lib %p",
                        handle);
            continue;
        }

        n_native_symbols = get_native_lib(&module_name, &native_symbols);
        if (n_native_symbols == 0 || module_name == NULL
            || native_symbols == NULL) {
            LOG_WARNING("warning: get_native_lib returned different values for "
                        "native lib %p",
                        handle);
            continue;
        }

        /* unregister native symbols */
        if (!wasm_runtime_unregister_natives(module_name, native_symbols)) {
            LOG_WARNING("warning: failed to unregister native lib %p", handle);
            continue;
        }

        dlclose(handle);
    }
}
#endif

/* clang-format off */
static void
print_help()
{
    printf("Usage: wamrc [options] -o output_file wasm_file\n");
    printf("  --target=<arch-name>      Set the target arch, which has the general format: <arch><sub>\n");
    printf("                            <arch> = x86_64, i386, aarch64, arm, thumb, xtensa, mips,\n");
    printf("                                     riscv64, riscv32.\n");
    printf("                              Default is host arch, e.g. x86_64\n");
    printf("                            <sub> = for ex. on arm or thumb: v5, v6m, v7a, v7m, etc.\n");
    printf("                            Use --target=help to list supported targets\n");
    printf("  --target-abi=<abi>        Set the target ABI, e.g. gnu, eabi, gnueabihf, msvc, etc.\n");
    printf("                              Default is gnu if target isn't riscv64 or riscv32\n");
    printf("                              For target riscv64 and riscv32, default is lp64d and ilp32d\n");
    printf("                            Use --target-abi=help to list all the ABI supported\n");
    printf("  --cpu=<cpu>               Set the target CPU (default: host CPU, e.g. skylake)\n");
    printf("                            Use --cpu=help to list all the CPU supported\n");
    printf("  --cpu-features=<features> Enable or disable the CPU features\n");
    printf("                            Use +feature to enable a feature, or -feature to disable it\n");
    printf("                            For example, --cpu-features=+feature1,-feature2\n");
    printf("                            Use --cpu-features=+help to list all the features supported\n");
    printf("  --opt-level=n             Set the optimization level (0 to 3, default is 3)\n");
    printf("  --size-level=n            Set the code size level (0 to 3, default is 3)\n");
    printf("  -sgx                      Generate code for SGX platform (Intel Software Guard Extensions)\n");
    printf("  --bounds-checks=1/0       Enable or disable the bounds checks for memory access:\n");
    printf("                              by default it is disabled in all 64-bit platforms except SGX and\n");
    printf("                              in these platforms runtime does bounds checks with hardware trap,\n");
    printf("                              and by default it is enabled in all 32-bit platforms\n");
    printf("                            CAVEAT: --bounds-checks=0 enables some optimizations\n");
    printf("                              which make the compiled AOT module incompatible\n");
    printf("                              with a runtime without the hardware bounds checks.\n");
    printf("  --stack-bounds-checks=1/0 Enable or disable the bounds checks for native stack:\n");
    printf("                              if the option isn't set, the status is same as `--bounds-check`,\n");
    printf("                              if the option is set:\n");
    printf("                                (1) it is always enabled when `--bounds-checks` is enabled,\n");
    printf("                                (2) else it is enabled/disabled according to the option value\n");
    printf("  --stack-usage=<file>      Generate a stack-usage file.\n");
    printf("                              Similarly to `clang -fstack-usage`.\n");
    printf("  --format=<format>         Specifies the format of the output file\n");
    printf("                            The format supported:\n");
    printf("                              aot (default)  AoT file\n");
    printf("                              object         Native object file\n");
    printf("                              llvmir-unopt   Unoptimized LLVM IR\n");
    printf("                              llvmir-opt     Optimized LLVM IR\n");
    printf("  --disable-bulk-memory     Disable the MVP bulk memory feature\n");
    printf("  --enable-multi-thread     Enable multi-thread feature, the dependent features bulk-memory and\n");
    printf("                            thread-mgr will be enabled automatically\n");
    printf("  --enable-tail-call        Enable the post-MVP tail call feature\n");
    printf("  --disable-simd            Disable the post-MVP 128-bit SIMD feature:\n");
    printf("                              currently 128-bit SIMD is supported for x86-64 and aarch64 targets,\n");
    printf("                              and by default it is enabled in them and disabled in other targets\n");
    printf("  --disable-ref-types       Disable the MVP reference types feature\n");
    printf("  --disable-aux-stack-check Disable auxiliary stack overflow/underflow check\n");
    printf("  --enable-dump-call-stack  Enable stack trace feature\n");
    printf("  --enable-perf-profiling   Enable function performance profiling\n");
    printf("  --enable-memory-profiling Enable memory usage profiling\n");
    printf("  --xip                     A shorthand of --enable-indirect-mode --disable-llvm-intrinsics\n");
    printf("  --enable-indirect-mode    Enable call function through symbol table but not direct call\n");
    printf("  --disable-llvm-intrinsics Disable the LLVM built-in intrinsics\n");
    printf("  --enable-builtin-intrinsics=<flags>\n");
    printf("                            Enable the specified built-in intrinsics, it will override the default\n");
    printf("                              settings. It only takes effect when --disable-llvm-intrinsics is set.\n");
    printf("                            Available flags: all, i32.common, i64.common, f32.common, f64.common,\n");
    printf("                              i32.clz, i32.ctz, etc, refer to doc/xip.md for full list\n");
    printf("                            Use comma to separate, please refer to doc/xip.md for full list.\n");
    printf("  --disable-llvm-lto        Disable the LLVM link time optimization\n");
    printf("  --enable-llvm-pgo         Enable LLVM PGO (Profile-Guided Optimization)\n");
    printf("  --enable-llvm-passes=<passes>\n");
    printf("                            Enable the specified LLVM passes, using comma to separate\n");
    printf("  --use-prof-file=<file>    Use profile file collected by LLVM PGO (Profile-Guided Optimization)\n");
    printf("  --enable-segue[=<flags>]  Enable using segment register GS as the base address of linear memory,\n");
    printf("                            only available on linux/linux-sgx x86-64, which may improve performance,\n");
    printf("                            flags can be: i32.load, i64.load, f32.load, f64.load, v128.load,\n");
    printf("                                          i32.store, i64.store, f32.store, f64.store, v128.store\n");
    printf("                            Use comma to separate, e.g. --enable-segue=i32.load,i64.store\n");
    printf("                            and --enable-segue means all flags are added.\n");
    printf("  --emit-custom-sections=<section names>\n");
    printf("                            Emit the specified custom sections to AoT file, using comma to separate\n");
    printf("                            multiple names, e.g.\n");
    printf("                                --emit-custom-sections=section1,section2,sectionN\n");
#if BH_HAS_DLFCN
    printf("  --native-lib=<lib>        Register native libraries to the WASM module, which\n");
    printf("                            are shared object (.so) files, for example:\n");
    printf("                              --native-lib=test1.so --native-lib=test2.so\n");
#endif
#if WASM_ENABLE_LINUX_PERF != 0
    printf("  --enable-linux-perf       Enable linux perf support\n");
#endif
    printf("  -v=n                      Set log verbose level (0 to 5, default is 2), larger with more log\n");
    printf("  --version                 Show version information\n");
    printf("Examples: wamrc -o test.aot test.wasm\n");
    printf("          wamrc --target=i386 -o test.aot test.wasm\n");
    printf("          wamrc --target=i386 --format=object -o test.o test.wasm\n");
    printf("          wamrc --target-abi=help\n");
    printf("          wamrc --target=x86_64 --cpu=help\n");
}
/* clang-format on */

#define PRINT_HELP_AND_EXIT() \
    do {                      \
        print_help();         \
        goto fail0;           \
    } while (0)

/**
 * Split a string into an array of strings
 * Returns NULL on failure
 * Memory must be freed by caller
 * Based on: http://stackoverflow.com/a/11198630/471795
 */
static char **
split_string(char *str, int *count, const char *delimer)
{
    char **res = NULL, **res1;
    char *p;
    int idx = 0;

    /* split string and append tokens to 'res' */
    do {
        p = strtok(str, delimer);
        str = NULL;
        res1 = res;
        res = (char **)realloc(res1, sizeof(char *) * (uint32)(idx + 1));
        if (res == NULL) {
            free(res1);
            return NULL;
        }
        res[idx++] = p;
    } while (p);

    /**
     * Due to the section name,
     * res[0] might contain 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 uint32
resolve_segue_flags(char *str_flags)
{
    uint32 segue_flags = 0;
    int32 flag_count, i;
    char **flag_list;

    flag_list = split_string(str_flags, &flag_count, ",");
    if (flag_list) {
        for (i = 0; i < flag_count; i++) {
            if (!strcmp(flag_list[i], "i32.load")) {
                segue_flags |= 1 << 0;
            }
            else if (!strcmp(flag_list[i], "i64.load")) {
                segue_flags |= 1 << 1;
            }
            else if (!strcmp(flag_list[i], "f32.load")) {
                segue_flags |= 1 << 2;
            }
            else if (!strcmp(flag_list[i], "f64.load")) {
                segue_flags |= 1 << 3;
            }
            else if (!strcmp(flag_list[i], "v128.load")) {
                segue_flags |= 1 << 4;
            }
            else if (!strcmp(flag_list[i], "i32.store")) {
                segue_flags |= 1 << 8;
            }
            else if (!strcmp(flag_list[i], "i64.store")) {
                segue_flags |= 1 << 9;
            }
            else if (!strcmp(flag_list[i], "f32.store")) {
                segue_flags |= 1 << 10;
            }
            else if (!strcmp(flag_list[i], "f64.store")) {
                segue_flags |= 1 << 11;
            }
            else if (!strcmp(flag_list[i], "v128.store")) {
                segue_flags |= 1 << 12;
            }
            else {
                /* invalid flag */
                segue_flags = (uint32)-1;
                break;
            }
        }
        free(flag_list);
    }
    return segue_flags;
}

/* When print help info for target/cpu/target-abi/cpu-features, load this dummy
 * wasm file content rather than from an input file, the dummy wasm file content
 * is: magic header + version number */
static unsigned char dummy_wasm_file[8] = { 0x00, 0x61, 0x73, 0x6D,
                                            0x01, 0x00, 0x00, 0x00 };

int
main(int argc, char *argv[])
{
    char *wasm_file_name = NULL, *out_file_name = NULL;
    uint8 *wasm_file = NULL;
    uint32 wasm_file_size;
    wasm_module_t wasm_module = NULL;
    aot_comp_data_t comp_data = NULL;
    aot_comp_context_t comp_ctx = NULL;
    RuntimeInitArgs init_args;
    AOTCompOption option = { 0 };
    char error_buf[128];
    int log_verbose_level = 2;
    bool sgx_mode = false, size_level_set = false, use_dummy_wasm = false;
    int exit_status = EXIT_FAILURE;
#if BH_HAS_DLFCN
    const char *native_lib_list[8] = { NULL };
    uint32 native_lib_count = 0;
    void *native_handle_list[8] = { NULL };
    uint32 native_handle_count = 0;
#endif
#if WASM_ENABLE_LINUX_PERF != 0
    bool enable_linux_perf = false;
#endif

    option.opt_level = 3;
    option.size_level = 3;
    option.output_format = AOT_FORMAT_FILE;
    /* default value, enable or disable depends on the platform */
    option.bounds_checks = 2;
    /* default value, enable or disable depends on the platform */
    option.stack_bounds_checks = 2;
    option.enable_simd = true;
    option.enable_aux_stack_check = true;
    option.enable_bulk_memory = true;
    option.enable_ref_types = true;

    /* Process options */
    for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
        if (!strcmp(argv[0], "-o")) {
            argc--, argv++;
            if (argc < 2)
                PRINT_HELP_AND_EXIT();
            out_file_name = argv[0];
        }
        else if (!strncmp(argv[0], "--target=", 9)) {
            if (argv[0][9] == '\0')
                PRINT_HELP_AND_EXIT();
            option.target_arch = argv[0] + 9;
            if (!strcmp(option.target_arch, "help")) {
                use_dummy_wasm = true;
            }
        }
        else if (!strncmp(argv[0], "--target-abi=", 13)) {
            if (argv[0][13] == '\0')
                PRINT_HELP_AND_EXIT();
            option.target_abi = argv[0] + 13;
            if (!strcmp(option.target_abi, "help")) {
                use_dummy_wasm = true;
            }
        }
        else if (!strncmp(argv[0], "--cpu=", 6)) {
            if (argv[0][6] == '\0')
                PRINT_HELP_AND_EXIT();
            option.target_cpu = argv[0] + 6;
            if (!strcmp(option.target_cpu, "help")) {
                use_dummy_wasm = true;
            }
        }
        else if (!strncmp(argv[0], "--cpu-features=", 15)) {
            if (argv[0][15] == '\0')
                PRINT_HELP_AND_EXIT();
            option.cpu_features = argv[0] + 15;
            if (!strcmp(option.cpu_features, "+help")) {
                use_dummy_wasm = true;
            }
        }
        else if (!strncmp(argv[0], "--opt-level=", 12)) {
            if (argv[0][12] == '\0')
                PRINT_HELP_AND_EXIT();
            option.opt_level = (uint32)atoi(argv[0] + 12);
            if (option.opt_level > 3)
                option.opt_level = 3;
        }
        else if (!strncmp(argv[0], "--size-level=", 13)) {
            if (argv[0][13] == '\0')
                PRINT_HELP_AND_EXIT();
            option.size_level = (uint32)atoi(argv[0] + 13);
            if (option.size_level > 3)
                option.size_level = 3;
            size_level_set = true;
        }
        else if (!strcmp(argv[0], "-sgx")) {
            sgx_mode = true;
        }
        else if (!strncmp(argv[0], "--bounds-checks=", 16)) {
            option.bounds_checks = (atoi(argv[0] + 16) == 1) ? 1 : 0;
        }
        else if (!strncmp(argv[0], "--stack-bounds-checks=", 22)) {
            option.stack_bounds_checks = (atoi(argv[0] + 22) == 1) ? 1 : 0;
        }
        else if (!strncmp(argv[0], "--stack-usage=", 14)) {
            option.stack_usage_file = argv[0] + 14;
        }
        else if (!strncmp(argv[0], "--format=", 9)) {
            if (argv[0][9] == '\0')
                PRINT_HELP_AND_EXIT();
            if (!strcmp(argv[0] + 9, "aot"))
                option.output_format = AOT_FORMAT_FILE;
            else if (!strcmp(argv[0] + 9, "object"))
                option.output_format = AOT_OBJECT_FILE;
            else if (!strcmp(argv[0] + 9, "llvmir-unopt"))
                option.output_format = AOT_LLVMIR_UNOPT_FILE;
            else if (!strcmp(argv[0] + 9, "llvmir-opt"))
                option.output_format = AOT_LLVMIR_OPT_FILE;
            else {
                printf("Invalid format %s.\n", argv[0] + 9);
                PRINT_HELP_AND_EXIT();
            }
        }
        else if (!strncmp(argv[0], "-v=", 3)) {
            log_verbose_level = atoi(argv[0] + 3);
            if (log_verbose_level < 0 || log_verbose_level > 5)
                PRINT_HELP_AND_EXIT();
        }
        else if (!strcmp(argv[0], "--disable-bulk-memory")) {
            option.enable_bulk_memory = false;
        }
        else if (!strcmp(argv[0], "--enable-multi-thread")) {
            option.enable_bulk_memory = true;
            option.enable_thread_mgr = true;
            option.enable_ref_types = false;
        }
        else if (!strcmp(argv[0], "--enable-tail-call")) {
            option.enable_tail_call = true;
        }
        else if (!strcmp(argv[0], "--enable-simd")) {
            /* obsolete option, kept for compatibility */
            option.enable_simd = true;
        }
        else if (!strcmp(argv[0], "--disable-simd")) {
            option.enable_simd = false;
        }
        else if (!strcmp(argv[0], "--disable-ref-types")) {
            option.enable_ref_types = false;
        }
        else if (!strcmp(argv[0], "--disable-aux-stack-check")) {
            option.enable_aux_stack_check = false;
        }
        else if (!strcmp(argv[0], "--enable-dump-call-stack")) {
            option.enable_aux_stack_frame = true;
        }
        else if (!strcmp(argv[0], "--enable-perf-profiling")) {
            option.enable_aux_stack_frame = true;
        }
        else if (!strcmp(argv[0], "--enable-memory-profiling")) {
            option.enable_stack_estimation = true;
        }
        else if (!strcmp(argv[0], "--xip")) {
            option.is_indirect_mode = true;
            option.disable_llvm_intrinsics = true;
        }
        else if (!strcmp(argv[0], "--enable-indirect-mode")) {
            option.is_indirect_mode = true;
        }
        else if (!strcmp(argv[0], "--disable-llvm-intrinsics")) {
            option.disable_llvm_intrinsics = true;
        }
        else if (!strncmp(argv[0], "--enable-builtin-intrinsics=", 28)) {
            if (argv[0][28] == '\0')
                PRINT_HELP_AND_EXIT();
            option.builtin_intrinsics = argv[0] + 28;
        }
        else if (!strcmp(argv[0], "--disable-llvm-lto")) {
            option.disable_llvm_lto = true;
        }
        else if (!strcmp(argv[0], "--enable-llvm-pgo")) {
            option.enable_llvm_pgo = true;
        }
        else if (!strncmp(argv[0], "--enable-llvm-passes=", 21)) {
            if (argv[0][21] == '\0')
                PRINT_HELP_AND_EXIT();
            option.llvm_passes = argv[0] + 21;
        }
        else if (!strncmp(argv[0], "--use-prof-file=", 16)) {
            if (argv[0][16] == '\0')
                PRINT_HELP_AND_EXIT();
            option.use_prof_file = argv[0] + 16;
        }
        else if (!strcmp(argv[0], "--enable-segue")) {
            /* all flags are enabled */
            option.segue_flags = 0x1F1F;
        }
        else if (!strncmp(argv[0], "--enable-segue=", 15)) {
            option.segue_flags = resolve_segue_flags(argv[0] + 15);
            if (option.segue_flags == (uint32)-1)
                PRINT_HELP_AND_EXIT();
        }
        else if (!strncmp(argv[0], "--emit-custom-sections=", 23)) {
            int len = 0;
            if (option.custom_sections) {
                free(option.custom_sections);
            }

            option.custom_sections = split_string(argv[0] + 23, &len, ",");
            if (!option.custom_sections) {
                printf("Failed to process emit-custom-sections: alloc "
                       "memory failed\n");
                PRINT_HELP_AND_EXIT();
            }

            option.custom_sections_count = len;
        }
#if BH_HAS_DLFCN
        else if (!strncmp(argv[0], "--native-lib=", 13)) {
            if (argv[0][13] == '\0')
                PRINT_HELP_AND_EXIT();
            if (native_lib_count >= sizeof(native_lib_list) / sizeof(char *)) {
                printf("Only allow max native lib number %d\n",
                       (int)(sizeof(native_lib_list) / sizeof(char *)));
                goto fail0;
            }
            native_lib_list[native_lib_count++] = argv[0] + 13;
        }
#endif
#if WASM_ENABLE_LINUX_PERF != 0
        else if (!strncmp(argv[0], "--enable-linux-perf", 19)) {
            enable_linux_perf = true;
        }
#endif
        else if (!strncmp(argv[0], "--version", 9)) {
            uint32 major, minor, patch;
            wasm_runtime_get_version(&major, &minor, &patch);
            printf("wamrc %u.%u.%u\n", major, minor, patch);
            return 0;
        }
        else
            PRINT_HELP_AND_EXIT();
    }

    if (!use_dummy_wasm && (argc == 0 || !out_file_name))
        PRINT_HELP_AND_EXIT();

    if (!size_level_set) {
        /**
         * Set opt level to 1 by default for Windows and MacOS as
         * they can not memory map out 0-2GB memory and might not
         * be able to meet the requirements of some AOT relocation
         * operations.
         */
        if (option.target_abi && !strcmp(option.target_abi, "msvc")) {
            LOG_VERBOSE("Set size level to 1 for Windows AOT file");
            option.size_level = 1;
        }
#if defined(_WIN32) || defined(_WIN32_) || defined(__APPLE__) \
    || defined(__MACH__)
        if (!option.target_abi) {
            LOG_VERBOSE("Set size level to 1 for Windows or MacOS AOT file");
            option.size_level = 1;
        }
#endif
    }

    if (sgx_mode) {
        option.size_level = 1;
        option.is_sgx_platform = true;
    }

    if (!use_dummy_wasm) {
        wasm_file_name = argv[0];

        if (!strcmp(wasm_file_name, out_file_name)) {
            printf("Error: input file and output file are the same");
            return -1;
        }
    }

    memset(&init_args, 0, sizeof(RuntimeInitArgs));

    init_args.mem_alloc_type = Alloc_With_Allocator;
    init_args.mem_alloc_option.allocator.malloc_func = malloc;
    init_args.mem_alloc_option.allocator.realloc_func = realloc;
    init_args.mem_alloc_option.allocator.free_func = free;
#if WASM_ENABLE_LINUX_PERF != 0
    init_args.enable_linux_perf = enable_linux_perf;
#endif

    /* initialize runtime environment */
    if (!wasm_runtime_full_init(&init_args)) {
        printf("Init runtime environment failed.\n");
        return -1;
    }

    bh_log_set_verbose_level(log_verbose_level);

#if BH_HAS_DLFCN
    bh_print_time("Begin to load native libs");
    native_handle_count = load_and_register_native_libs(
        native_lib_list, native_lib_count, native_handle_list);
#endif

    bh_print_time("Begin to load wasm file");

    if (use_dummy_wasm) {
        /* load WASM byte buffer from dummy buffer */
        wasm_file_size = sizeof(dummy_wasm_file);
        wasm_file = dummy_wasm_file;
    }
    else {
        /* load WASM byte buffer from WASM bin file */
        if (!(wasm_file = (uint8 *)bh_read_file_to_buffer(wasm_file_name,
                                                          &wasm_file_size)))
            goto fail1;
    }

    if (get_package_type(wasm_file, wasm_file_size) != Wasm_Module_Bytecode) {
        printf("Invalid file type: expected wasm file but got other\n");
        goto fail2;
    }

    /* load WASM module */
    if (!(wasm_module = wasm_runtime_load(wasm_file, wasm_file_size, error_buf,
                                          sizeof(error_buf)))) {
        printf("%s\n", error_buf);
        goto fail2;
    }

    if (!(comp_data = aot_create_comp_data(wasm_module))) {
        printf("%s\n", aot_get_last_error());
        goto fail3;
    }

#if WASM_ENABLE_DEBUG_AOT != 0
    if (!create_dwarf_extractor(comp_data, wasm_file_name)) {
        printf("%s:create dwarf extractor failed\n", wasm_file_name);
    }
#endif

    bh_print_time("Begin to create compile context");

    if (!(comp_ctx = aot_create_comp_context(comp_data, &option))) {
        printf("%s\n", aot_get_last_error());
        goto fail4;
    }

    bh_print_time("Begin to compile");

    if (!aot_compile_wasm(comp_ctx)) {
        printf("%s\n", aot_get_last_error());
        goto fail5;
    }

    switch (option.output_format) {
        case AOT_LLVMIR_UNOPT_FILE:
        case AOT_LLVMIR_OPT_FILE:
            if (!aot_emit_llvm_file(comp_ctx, out_file_name)) {
                printf("%s\n", aot_get_last_error());
                goto fail5;
            }
            break;
        case AOT_OBJECT_FILE:
            if (!aot_emit_object_file(comp_ctx, out_file_name)) {
                printf("%s\n", aot_get_last_error());
                goto fail5;
            }
            break;
        case AOT_FORMAT_FILE:
            if (!aot_emit_aot_file(comp_ctx, comp_data, out_file_name)) {
                printf("%s\n", aot_get_last_error());
                goto fail5;
            }
            break;
        default:
            break;
    }

    bh_print_time("Compile end");

    printf("Compile success, file %s was generated.\n", out_file_name);
    exit_status = EXIT_SUCCESS;

fail5:
    /* Destroy compiler context */
    aot_destroy_comp_context(comp_ctx);

fail4:
    /* Destroy compile data */
    aot_destroy_comp_data(comp_data);

fail3:
    /* Unload WASM module */
    wasm_runtime_unload(wasm_module);

fail2:
    /* free the file buffer */
    if (!use_dummy_wasm) {
        wasm_runtime_free(wasm_file);
    }

fail1:
#if BH_HAS_DLFCN
    unregister_and_unload_native_libs(native_handle_count, native_handle_list);
#endif
    /* Destroy runtime environment */
    wasm_runtime_destroy();

fail0:
    /* free option.custom_sections */
    if (option.custom_sections) {
        free(option.custom_sections);
    }

    bh_print_time("wamrc return");
    return exit_status;
}