From 6f97822c181bc9f481b31e292b7f357321ae927c Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Thu, 5 Sep 2024 14:44:06 +0100 Subject: [PATCH] Add wamrc parameter to configure stack frame features (#3763) Those parameters can be used to reduce the size of the AOT code. There's going to be more changes related to AOT code size reduction, this is just the initial step. p.s. https://github.com/bytecodealliance/wasm-micro-runtime/issues/3758 --- core/iwasm/compilation/aot_compiler.c | 8 ++- core/iwasm/compilation/aot_emit_exception.c | 4 +- core/iwasm/compilation/aot_emit_function.c | 39 +++++++----- core/iwasm/compilation/aot_llvm.c | 2 + core/iwasm/compilation/aot_llvm.h | 3 + core/iwasm/include/aot_comp_option.h | 18 ++++++ core/iwasm/interpreter/wasm_loader.c | 1 + core/iwasm/interpreter/wasm_mini_loader.c | 1 + wamr-compiler/main.c | 70 +++++++++++++++++++++ 9 files changed, 127 insertions(+), 19 deletions(-) diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index bb6cf100a..78b7da88d 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -337,6 +337,10 @@ aot_gen_commit_values(AOTCompFrame *frame) LLVMValueRef value; uint32 n; + if (!frame->comp_ctx->call_stack_features.values) { + return true; + } + /* First, commit reference flags * For LLVM JIT, iterate all local and stack ref flags * For AOT, ignore local(params + locals) ref flags */ @@ -629,7 +633,7 @@ aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip) offset_sp = offsetof(WASMInterpFrame, sp); } - if (commit_ip) { + if (commit_ip && comp_ctx->call_stack_features.ip) { if (!comp_ctx->is_jit_mode) { WASMModule *module = comp_ctx->comp_data->wasm_module; if (is_64bit) @@ -654,7 +658,7 @@ aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip) } } - if (commit_sp) { + if (commit_sp && comp_ctx->call_stack_features.values) { n = (uint32)(sp - frame->lp); value = I32_CONST(offset_of_local(comp_ctx, n)); if (!value) { diff --git a/core/iwasm/compilation/aot_emit_exception.c b/core/iwasm/compilation/aot_emit_exception.c index 968ee78b6..1527e83e5 100644 --- a/core/iwasm/compilation/aot_emit_exception.c +++ b/core/iwasm/compilation/aot_emit_exception.c @@ -41,7 +41,7 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } - if (comp_ctx->aot_frame) { + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.trap_ip) { /* Create exception ip phi */ if (!(func_ctx->exception_ip_phi = LLVMBuildPhi( comp_ctx->builder, is_64bit ? I64_TYPE : I32_TYPE, @@ -134,7 +134,7 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Add phi incoming value to got_exception block */ LLVMAddIncoming(func_ctx->exception_id_phi, &exce_id, &block_curr, 1); - if (comp_ctx->aot_frame) { + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.trap_ip) { const uint8 *ip = comp_ctx->aot_frame->frame_ip; LLVMValueRef exce_ip = NULL; diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index 8f6e3e456..1d565b6c0 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -682,24 +682,29 @@ alloc_frame_for_aot_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, new_frame = wasm_stack_top; - if (!(check_wasm_stack_succ = LLVMAppendBasicBlockInContext( - comp_ctx->context, func_ctx->func, "check_wasm_stack_succ"))) { - aot_set_last_error("llvm add basic block failed."); - return false; - } + if (comp_ctx->call_stack_features.bounds_checks) { + if (!(check_wasm_stack_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, + "check_wasm_stack_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } - LLVMMoveBasicBlockAfter(check_wasm_stack_succ, - LLVMGetInsertBlock(comp_ctx->builder)); + LLVMMoveBasicBlockAfter(check_wasm_stack_succ, + LLVMGetInsertBlock(comp_ctx->builder)); - if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, wasm_stack_top_max, - wasm_stack_top_bound, "cmp"))) { - aot_set_last_error("llvm build icmp failed"); - return false; - } + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, + wasm_stack_top_max, wasm_stack_top_bound, + "cmp"))) { + aot_set_last_error("llvm build icmp failed"); + return false; + } - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_OPERAND_STACK_OVERFLOW, - true, cmp, check_wasm_stack_succ))) { - return false; + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OPERAND_STACK_OVERFLOW, true, cmp, + check_wasm_stack_succ))) { + return false; + } } #if WASM_ENABLE_GC != 0 @@ -1285,6 +1290,10 @@ commit_params_to_frame_of_import_func(AOTCompContext *comp_ctx, { uint32 i, n; + if (!comp_ctx->call_stack_features.values) { + return true; + } + for (i = 0, n = 0; i < func_type->param_count; i++, n++) { switch (func_type->types[i]) { case VALUE_TYPE_I32: diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index 39f64d81d..3346086a9 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -2580,6 +2580,8 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) if (option->enable_aux_stack_frame) comp_ctx->enable_aux_stack_frame = true; + comp_ctx->call_stack_features = option->call_stack_features; + if (option->enable_perf_profiling) comp_ctx->enable_perf_profiling = true; diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 270e5ae45..65debbaa3 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -412,6 +412,9 @@ typedef struct AOTCompContext { /* Generate auxiliary stack frame */ bool enable_aux_stack_frame; + /* Auxiliary call stack features */ + AOTCallStackFeatures call_stack_features; + /* Function performance profiling */ bool enable_perf_profiling; diff --git a/core/iwasm/include/aot_comp_option.h b/core/iwasm/include/aot_comp_option.h index 617b68f97..4ab2e6ab6 100644 --- a/core/iwasm/include/aot_comp_option.h +++ b/core/iwasm/include/aot_comp_option.h @@ -6,6 +6,23 @@ #ifndef __AOT_COMP_OPTION_H__ #define __AOT_COMP_OPTION_H__ +typedef struct { + /* Enables or disables bounds checks for stack frames. When enabled, the AOT + * compiler generates code to check if the stack pointer is within the + * bounds of the current stack frame (and if not, traps). */ + bool bounds_checks; + + /* Enables or disables instruction pointer (IP) tracking.*/ + bool ip; + + /* Enables or disables tracking instruction pointer of a trap. Only takes + * effect when `ip` is enabled.*/ + bool trap_ip; + + /* Enables or disables parameters, locals and stack operands. */ + bool values; +} AOTCallStackFeatures; + typedef struct AOTCompOption { bool is_jit_mode; bool is_indirect_mode; @@ -22,6 +39,7 @@ typedef struct AOTCompOption { bool enable_gc; bool enable_aux_stack_check; bool enable_aux_stack_frame; + AOTCallStackFeatures call_stack_features; bool enable_perf_profiling; bool enable_memory_profiling; bool disable_llvm_intrinsics; diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 13947ac82..092e0d152 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -5407,6 +5407,7 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, #if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 \ || WASM_ENABLE_AOT_STACK_FRAME != 0 option.enable_aux_stack_frame = true; + memset(&option.call_stack_features, 1, sizeof(AOTCallStackFeatures)); #endif #if WASM_ENABLE_PERF_PROFILING != 0 option.enable_perf_profiling = true; diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index 8826f98db..a21f4490f 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -2149,6 +2149,7 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, #if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 \ || WASM_ENABLE_AOT_STACK_FRAME != 0 option.enable_aux_stack_frame = true; + memset(&option.call_stack_features, 1, sizeof(AOTCallStackFeatures)); #endif #if WASM_ENABLE_PERF_PROFILING != 0 option.enable_perf_profiling = true; diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index bd9e54353..3c7ef1f4d 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -162,6 +162,12 @@ print_help() printf(" GC is enabled\n"); printf(" --disable-aux-stack-check Disable auxiliary stack overflow/underflow check\n"); printf(" --enable-dump-call-stack Enable stack trace feature\n"); + printf(" --call-stack-features=\n"); + printf(" A comma-separated list of features when generating call stacks.\n"); + printf(" By default, all features are enabled. To disable all features,\n"); + printf(" provide an empty list (i.e. --call-stack-features=). This flag\n"); + printf(" only only takes effect when --enable-dump-call-stack is set.\n"); + printf(" Available features: bounds-checks, ip, trap-ip, values.\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"); @@ -259,6 +265,48 @@ split_string(char *str, int *count, const char *delimer) return res; } +static bool +parse_call_stack_features(char *features_str, + AOTCallStackFeatures *out_features) +{ + int size = 0; + char **features; + bool ret = true; + + bh_assert(features_str); + bh_assert(out_features); + + /* non-empty feature list */ + features = split_string(features_str, &size, ","); + if (!features) { + return false; + } + + while (size--) { + if (!strcmp(features[size], "bounds-checks")) { + out_features->bounds_checks = true; + } + else if (!strcmp(features[size], "ip")) { + out_features->ip = true; + } + else if (!strcmp(features[size], "trap-ip")) { + out_features->trap_ip = true; + } + else if (!strcmp(features[size], "values")) { + out_features->values = true; + } + else { + ret = false; + printf("Unsupported feature %s\n", features[size]); + goto finish; + } + } + +finish: + free(features); + return ret; +} + static uint32 resolve_segue_flags(char *str_flags) { @@ -356,6 +404,9 @@ main(int argc, char *argv[]) option.enable_ref_types = true; option.enable_gc = false; + /* Set all the features to true by default */ + memset(&option.call_stack_features, 1, sizeof(AOTCallStackFeatures)); + /* Process options */ for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { if (!strcmp(argv[0], "-o")) { @@ -470,6 +521,19 @@ main(int argc, char *argv[]) else if (!strcmp(argv[0], "--enable-dump-call-stack")) { option.enable_aux_stack_frame = true; } + else if (!strncmp(argv[0], "--call-stack-features=", 22)) { + /* Reset all the features, only enable the user-defined ones */ + memset(&option.call_stack_features, 0, + sizeof(AOTCallStackFeatures)); + + if (argv[0][22] != '\0') { + if (!parse_call_stack_features(argv[0] + 22, + &option.call_stack_features)) { + printf("Failed to parse call-stack-features\n"); + PRINT_HELP_AND_EXIT(); + } + } + } else if (!strcmp(argv[0], "--enable-perf-profiling")) { option.enable_aux_stack_frame = true; option.enable_perf_profiling = true; @@ -608,6 +672,12 @@ main(int argc, char *argv[]) #endif } + if (option.enable_gc && !option.call_stack_features.values) { + LOG_WARNING("Call stack feature 'values' must be enabled for GC. The " + "feature will be enabled automatically."); + option.call_stack_features.values = true; + } + if (sgx_mode) { option.size_level = 1; option.is_sgx_platform = true;