From 56a51a47d3f0c780999b682907098a4ae43e67c1 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 28 Oct 2021 10:59:37 -0500 Subject: [PATCH] added optional linking to wasmer on Linux --- build.bat | 6 +-- build.sh | 27 ++++++++--- docs/compile_time_vars | 31 ------------- include/astnodes.h | 1 + include/{wasm.h => wasm_emit.h} | 5 +++ src/onyx.c | 52 +++++++++++++++------ src/{wasm.c => wasm_emit.c} | 2 +- src/wasm_output.c | 36 ++++++++------- src/wasm_runtime.c | 80 +++++++++++++++++++++++++++++++++ 9 files changed, 171 insertions(+), 69 deletions(-) delete mode 100644 docs/compile_time_vars rename include/{wasm.h => wasm_emit.h} (99%) rename src/{wasm.c => wasm_emit.c} (99%) create mode 100644 src/wasm_runtime.c diff --git a/build.bat b/build.bat index 4c826e11..cea0527d 100644 --- a/build.bat +++ b/build.bat @@ -1,11 +1,11 @@ @echo off -set SOURCE_FILES=src/onyx.c src/astnodes.c src/builtins.c src/checker.c src/clone.c src/doc.c src/entities.c src/errors.c src/lex.c src/parser.c src/symres.c src/types.c src/utils.c src/wasm.c +set SOURCE_FILES=src/onyx.c src/astnodes.c src/builtins.c src/checker.c src/clone.c src/doc.c src/entities.c src/errors.c src/lex.c src/parser.c src/symres.c src/types.c src/utils.c src/wasm_emit.c if "%1" == "1" ( - set FLAGS=/O2 /MT /Z7 -) else ( set FLAGS=/Od /MDd /Z7 +) else ( + set FLAGS=/O2 /MT /Z7 ) del *.pdb > NUL 2> NUL diff --git a/build.sh b/build.sh index 1f30e66e..fe07982b 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,21 @@ #!/bin/sh -echo "Installing core libs" +# Where the core libraries for Onyx will go. CORE_DIR='/usr/share/onyx' + +# Where the onyx executable will be placed. +BIN_DIR='/usr/bin' + +echo "Installing core libs" sudo mkdir -p "$CORE_DIR" sudo cp -r ./core/ "$CORE_DIR" [ "$1" = "libs_only" ] && exit 0 -C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm" +C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm_emit" CC='gcc' +LIBS= +INCLUDES= WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op' @@ -20,22 +27,32 @@ else TARGET='./bin/onyx' fi +if [ ! -z "$ONYX_ENABLE_RUN_WITH_WASMER" ]; then + C_FILES="$C_FILES wasm_runtime" + FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER" + LIBS="$(wasmer config --libs) -Wl,-rpath=$(wasmer config --libdir)" + INCLUDES="$(wasmer config --cflags)" +fi + BUILD_DIR='./build' mkdir -p "$BUILD_DIR" for file in $C_FILES ; do echo "Compiling $file.c" - $CC -o $BUILD_DIR/$file.o $FLAGS -c src/$file.c + $CC -o $BUILD_DIR/$file.o \ + $FLAGS \ + "-DCORE_INSTALLATION=\"$CORE_DIR\"" \ + -c src/$file.c \ + $INCLUDES $LIBS done echo "Linking $TARGET" -$CC -o $TARGET $FLAGS $(for file in $C_FILES ; do printf "$BUILD_DIR/%s.o " $file ; done) +$CC -o $TARGET $FLAGS $(for file in $C_FILES ; do printf "$BUILD_DIR/%s.o " $file ; done) $LIBS echo "Removing object files" for file in $C_FILES ; do rm -f "$BUILD_DIR/$file".o 2>/dev/null ; done echo "Installing onyx executable" -BIN_DIR='/usr/bin' sudo cp ./bin/onyx "$BIN_DIR/onyx" # Otherwise the prompt ends on the same line diff --git a/docs/compile_time_vars b/docs/compile_time_vars deleted file mode 100644 index 42ecb1bb..00000000 --- a/docs/compile_time_vars +++ /dev/null @@ -1,31 +0,0 @@ -With the recent addition of compile time if statements, there should be a set of variables -that give information about the current compilation. These would include things such as: - - Chosen runtime (JS, WASI, others in the future) - - 32-Bit pointers are enabled - -The standard library will be rewritten to use these variables to conditionally include file -that are only supported on a particular system. - -For the moment, I think that the choice of runtime will be given by compiler flags. It will -set the variables described below to their value. - -There should be an #error directive that when hit just produces a compile time error and prevents -compilation. It would be useful to ensure certain files that are only for a particular -backend will not compile with the wrong one, such as webgl.onyx. - -Another thing to think about is having things like 'defined(...)' in C. In other words, a -method for knowing whether or not a symbol exists. Other things similar to this idea: - - defined(...) if the symbol is defined as anything - - is_procedure(...) if the symbol is a procedure - - argument_count(...) the number of required arguments the procedure has - - main :: () { - .... - } - - #if argument_count(main) > 0 { - main(args); - } else { - main(); - } - diff --git a/include/astnodes.h b/include/astnodes.h index 82e8f252..2f1514ee 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -1239,6 +1239,7 @@ struct Package { typedef enum CompileAction CompileAction; enum CompileAction { ONYX_COMPILE_ACTION_COMPILE, + ONYX_COMPILE_ACTION_RUN, ONYX_COMPILE_ACTION_DOCUMENT, ONYX_COMPILE_ACTION_PRINT_HELP, }; diff --git a/include/wasm.h b/include/wasm_emit.h similarity index 99% rename from include/wasm.h rename to include/wasm_emit.h index 9861affa..f5b9660d 100644 --- a/include/wasm.h +++ b/include/wasm_emit.h @@ -674,6 +674,11 @@ typedef struct OnyxWasmModule { OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc); void onyx_wasm_module_free(OnyxWasmModule* module); +void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer); void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file); +#ifdef ENABLE_RUN_WITH_WASMER +void onyx_run_wasm(bh_buffer code_buffer); +#endif + #endif diff --git a/src/onyx.c b/src/onyx.c index 7c19d377..d0ce5a64 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -6,7 +6,7 @@ #include "errors.h" #include "parser.h" #include "utils.h" -#include "wasm.h" +#include "wasm_emit.h" #include "doc.h" #define VERSION "v0.1.0-beta" @@ -84,12 +84,19 @@ static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *arg bh_arr_push(options.included_folders, "."); if (argc == 1) return options; + i32 arg_parse_start = 1; if (!strcmp(argv[1], "help")) options.action = ONYX_COMPILE_ACTION_PRINT_HELP; + #ifdef ENABLE_RUN_WITH_WASMER + else if (!strcmp(argv[1], "run")) { + options.action = ONYX_COMPILE_ACTION_RUN; + arg_parse_start = 2; + } + #endif else options.action = ONYX_COMPILE_ACTION_COMPILE; if (options.action != ONYX_COMPILE_ACTION_PRINT_HELP) { - fori(i, 1, argc) { + fori(i, arg_parse_start, argc) { if (!strcmp(argv[i], "-o")) { options.target_file = argv[++i]; } @@ -496,6 +503,22 @@ static i32 onyx_compile() { entity_heap_insert_existing(&context.entities, ent); } + u64 duration = bh_time_duration(start_time); + + if (context.options->verbose_output > 0) { + // TODO: Replace these with bh_printf when padded formatting is added. + printf("\nStatistics:\n"); + printf(" Time taken: %lf seconds\n", (double) duration / 1000); + printf(" Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration)); + printf(" Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration)); + printf("\n"); + } + + return ONYX_COMPILER_PROGRESS_SUCCESS; +} + +CompilerProgress onyx_flush_module() { + // NOTE: Output to file bh_file output_file; if (bh_file_create(&output_file, context.options->target_file) != BH_FILE_ERROR_NONE) @@ -532,17 +555,6 @@ static i32 onyx_compile() { onyx_wasm_module_write_to_file(context.wasm_module, output_file); } - u64 duration = bh_time_duration(start_time); - - if (context.options->verbose_output > 0) { - // TODO: Replace these with bh_printf when padded formatting is added. - printf("\nStatistics:\n"); - printf(" Time taken: %lf seconds\n", (double) duration / 1000); - printf(" Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration)); - printf(" Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration)); - printf("\n"); - } - if (context.options->documentation_file != NULL) { OnyxDocumentation docs = onyx_docs_generate(); docs.format = Doc_Format_Human; @@ -572,7 +584,21 @@ int main(int argc, char *argv[]) { case ONYX_COMPILE_ACTION_COMPILE: compiler_progress = onyx_compile(); + if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) { + onyx_flush_module(); + } + break; + + #ifdef ENABLE_RUN_WITH_WASMER + case ONYX_COMPILE_ACTION_RUN: + compiler_progress = onyx_compile(); + if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) { + bh_buffer code_buffer; + onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer); + onyx_run_wasm(code_buffer); + } break; + #endif default: break; } diff --git a/src/wasm.c b/src/wasm_emit.c similarity index 99% rename from src/wasm.c rename to src/wasm_emit.c index 20438a33..68a1c894 100644 --- a/src/wasm.c +++ b/src/wasm_emit.c @@ -14,7 +14,7 @@ #define BH_DEBUG -#include "wasm.h" +#include "wasm_emit.h" #include "utils.h" #define WASM_TYPE_INT32 0x7F diff --git a/src/wasm_output.c b/src/wasm_output.c index 8d11edf6..ceb2093a 100644 --- a/src/wasm_output.c +++ b/src/wasm_output.c @@ -655,24 +655,28 @@ static i32 output_datasection(OnyxWasmModule* module, bh_buffer* buff) { return buff->length - prev_len; } +void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer) { + bh_buffer_init(buffer, global_heap_allocator, 128); + bh_buffer_append(buffer, WASM_MAGIC_STRING, 4); + bh_buffer_append(buffer, WASM_VERSION, 4); + + output_typesection(module, buffer); + output_importsection(module, buffer); + output_funcsection(module, buffer); + output_tablesection(module, buffer); + output_memorysection(module, buffer); + output_globalsection(module, buffer); + output_exportsection(module, buffer); + output_startsection(module, buffer); + output_elemsection(module, buffer); + output_datacountsection(module, buffer); + output_codesection(module, buffer); + output_datasection(module, buffer); +} + void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) { bh_buffer master_buffer; - bh_buffer_init(&master_buffer, global_heap_allocator, 128); - bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4); - bh_buffer_append(&master_buffer, WASM_VERSION, 4); - - output_typesection(module, &master_buffer); - output_importsection(module, &master_buffer); - output_funcsection(module, &master_buffer); - output_tablesection(module, &master_buffer); - output_memorysection(module, &master_buffer); - output_globalsection(module, &master_buffer); - output_exportsection(module, &master_buffer); - output_startsection(module, &master_buffer); - output_elemsection(module, &master_buffer); - output_datacountsection(module, &master_buffer); - output_codesection(module, &master_buffer); - output_datasection(module, &master_buffer); + onyx_wasm_module_write_to_buffer(module, &master_buffer); bh_file_write(&file, master_buffer.data, master_buffer.length); } diff --git a/src/wasm_runtime.c b/src/wasm_runtime.c new file mode 100644 index 00000000..3d79902c --- /dev/null +++ b/src/wasm_runtime.c @@ -0,0 +1,80 @@ +#include "bh.h" +#include "wasm.h" +#include "wasmer.h" + +#ifndef WASMER_VERSION + #error "Currently, building the Onyx compile with built-in execution support requires the Wasmer library to be compiled and linked." +#endif + +void onyx_run_wasm(bh_buffer wasm_bytes) { + wasm_config_t* config = wasm_config_new(); + + // Prefer the LLVM compile because it is faster. This should be configurable from the command line and/or a top-level directive. + if (wasmer_is_compiler_available(LLVM)) { + wasm_config_set_compiler(config, LLVM); + } + + wasi_config_t* wasi_config = wasi_config_new("onyx"); + wasi_config_preopen_dir(wasi_config, "."); + + wasi_env_t* wasi_env = wasi_env_new(wasi_config); + + wasm_engine_t* engine = wasm_engine_new_with_config(config); + if (!engine) goto error_handling; + + wasm_store_t* store = wasm_store_new(engine); + if (!store) goto error_handling; + + wasm_byte_vec_t wasm_data; + wasm_data.size = wasm_bytes.length; + wasm_data.data = wasm_bytes.data; + + wasm_module_t* module = wasm_module_new(store, &wasm_data); + if (!module) goto error_handling; + + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + wasi_get_imports(store, module, wasi_env, &imports); + + wasm_trap_t* traps = NULL; + + wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &traps); + if (!instance) goto error_handling; + + // Find the start function + i32 start_function_idx = -1; + wasm_exporttype_vec_t export_types; + wasm_module_exports(module, &export_types); + fori (i, 0, (i64) export_types.size) { + wasm_exporttype_t* export_type = export_types.data[i]; + const wasm_name_t* export_name = wasm_exporttype_name(export_type); + + if (!strcmp(export_name->data, "_start")) { + start_function_idx = i; + break; + } + } + + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + wasm_extern_t* start_extern = exports.data[start_function_idx]; + wasm_func_t* start_func = wasm_extern_as_func(start_extern); + + wasm_val_vec_t args; + wasm_val_vec_t results; + wasm_val_vec_new_uninitialized(&args, 0); + + wasm_func_call(start_func, &args, &results); + + goto cleanup; + +error_handling: + bh_printf("An error occured trying to run the WASM module...\n"); + +cleanup: + if (instance) wasm_instance_delete(instance); + if (module) wasm_module_delete(module); + if (store) wasm_store_delete(store); + if (engine) wasm_engine_delete(engine); + return; +} \ No newline at end of file -- 2.25.1