diff --git a/platforms/Arduino/Arduino.ino.template b/platforms/Arduino/Arduino.ino.template index bfb51c0a..59f83895 100644 --- a/platforms/Arduino/Arduino.ino.template +++ b/platforms/Arduino/Arduino.ino.template @@ -7,10 +7,19 @@ #include "Arduino.h" #include "bin/upload.h" -unsigned int wasm_len = upload_wasm_len; -unsigned char* wasm = upload_wasm; +struct ModuleInfo { + unsigned char* wasm; + unsigned int wasm_len; + const char* name; +}; + +ModuleInfo modules[] = { + {upload_wasm, upload_wasm_len, "main"}, +}; +const size_t module_count = sizeof(modules) / sizeof(modules[0]); WARDuino* wac = WARDuino::instance(); +std::vector loaded_modules; Module* m; #define UART_PIN 3 @@ -52,20 +61,44 @@ void setup(void) { Serial.println(ESP.getFreePsram()); } -void loop() { - m = wac->load_module(wasm, wasm_len, {}); +void loop() { + + for (size_t i = 0; i < module_count; i++) { + Serial.print("Loading module: "); + Serial.println(modules[i].name); + + Module* mod = wac->load_module( + modules[i].wasm, + modules[i].wasm_len, + modules[i].name + ); + + if (mod) { + loaded_modules.push_back(mod); + m = mod; + } else { + Serial.print(" ✗ Failed to load "); + Serial.println(modules[i].name); + } + } - printf("LOADED \n\n"); {{PAUSED}} xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 1, NULL); disableCore0WDT(); - printf("START\n\n"); + Serial.println("START\n"); Serial.println("\nFree heap:"); Serial.println(ESP.getFreeHeap()); - wac->run_module(m); - printf("END\n\n"); - wac->unload_module(m); -} + if (m) { + wac->run_module(m); + } + + Serial.println("END\n"); + + for (auto mod : loaded_modules) { + wac->unload_module(mod); + } + loaded_modules.clear(); +} \ No newline at end of file diff --git a/platforms/CLI-Emulator/main.cpp b/platforms/CLI-Emulator/main.cpp index 8586f14d..5c833e13 100644 --- a/platforms/CLI-Emulator/main.cpp +++ b/platforms/CLI-Emulator/main.cpp @@ -42,8 +42,15 @@ void print_help() { print_version(); fprintf(stdout, "\n"); fprintf(stdout, "Usage:\n"); - fprintf(stdout, " wdcli [options]\n"); + fprintf(stdout, " wdcli [options]\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "Arguments:\n"); + fprintf(stdout, " Main WebAssembly module to execute\n"); + fprintf(stdout, "\n"); fprintf(stdout, "Options:\n"); + fprintf(stdout, + " --link Link additional module(s) (can be specified " + "multiple times)\n"); fprintf(stdout, " --loop Let the runtime loop infinitely on exceptions " "(default: false)\n"); @@ -71,9 +78,21 @@ void print_help() { "(default: interpreter)\n"); fprintf(stdout, " --invoke Invoke a function from the module\n"); fprintf(stdout, " --version Get version information\n"); + fprintf(stdout, " --help Show this help message\n"); +} + +std::string getModuleName(const char *path) { + std::string p(path); + size_t lastSlash = p.find_last_of("/\\"); + std::string filename = + (lastSlash == std::string::npos) ? p : p.substr(lastSlash + 1); + size_t lastDot = filename.find_last_of("."); + if (lastDot != std::string::npos) return filename.substr(0, lastDot); + return filename; } -Module *load(WARDuino wac, const char *file_name, Options opt) { +Module *load(WARDuino *wac, const char *file_name, const char *module_name, + Options opt) { uint8_t *wasm; unsigned int file_size; @@ -104,8 +123,7 @@ Module *load(WARDuino wac, const char *file_name, Options opt) { } fclose(file); file = nullptr; - - return wac.load_module(wasm, file_size, opt); + return wac->load_module(wasm, file_size, module_name, opt); error: fclose(file); @@ -261,45 +279,48 @@ StackValue parseParameter(const char *input, uint8_t value_type) { int main(int argc, const char *argv[]) { ARGV_SHIFT(); // Skip command name + // Configuration bool return_exception = true; bool no_debug = false; bool no_socket = false; const char *socket = "8192"; bool initiallyPaused = false; - const char *file_name = nullptr; + const char *main_module = nullptr; + std::vector linked_modules; const char *proxy = nullptr; const char *baudrate = nullptr; const char *mode = "interpreter"; bool dump_info = false; - const char *fname = nullptr; - std::vector arguments = std::vector(); + const char *invoke_fname = nullptr; + std::vector invoke_raw_args; + // Parse main module (first positional argument) if (argc > 0 && argv[0][0] != '-') { - ARGV_GET(file_name); - - dbg_info("=== LOAD MODULE INTO WARDUINO ===\n"); - m = load(*wac, file_name, - {.disable_memory_bounds = false, - .mangle_table_index = false, - .dlsym_trim_underscore = false, - .return_exception = return_exception}); + main_module = argv[0]; + ARGV_SHIFT(); } // Parse options while (argc > 0) { const char *arg = argv[0]; - if (arg[0] != '-') { - break; - } ARGV_SHIFT(); if (!strcmp("--version", arg)) { print_version(); return 0; - } else if (!strcmp("--help", arg)) { + } else if (!strcmp("--help", arg) || !strcmp("-h", arg)) { print_help(); return 0; + } else if (!strcmp("--link", arg)) { + const char *link_file = nullptr; + ARGV_GET(link_file); + if (link_file) { + linked_modules.push_back(link_file); + } else { + fprintf(stderr, "wdcli: --link requires a file argument\n"); + return 1; + } } else if (!strcmp("--loop", arg)) { return_exception = false; } else if (!strcmp("--no-debug", arg)) { @@ -311,47 +332,98 @@ int main(int argc, const char *argv[]) { } else if (!strcmp("--paused", arg)) { initiallyPaused = true; } else if (!strcmp("--proxy", arg)) { - ARGV_GET(proxy); // /dev/ttyUSB0 + ARGV_GET(proxy); } else if (!strcmp("--baudrate", arg)) { ARGV_GET(baudrate); } else if (!strcmp("--mode", arg)) { ARGV_GET(mode); } else if (!strcmp("--invoke", arg)) { - ARGV_GET(fname); - - // find function - int fidx = wac->get_export_fidx(m, fname); - if (fidx < 0) { - fprintf(stderr, "wdcli: no exported function with name '%s'\n", - fname); - return 1; - } - - Block function = m->functions[fidx]; - - // consume all arguments for the function - for (uint32_t i = 0; i < function.type->param_count; ++i) { - const char *number = nullptr; - ARGV_GET(number); - - if (number[0] == '-') { - FATAL("wdcli: wrong number of arguments for '%s'\n", fname); - } - - arguments.push_back( - parseParameter(number, function.type->params[i])); + ARGV_GET(invoke_fname); + // Collect remaining args as potential parameters until next flag + while (argc > 0 && argv[0][0] != '-') { + const char *val; + ARGV_GET(val); + invoke_raw_args.push_back(val); } } else if (!strcmp("--dump-info", arg)) { dump_info = true; + } else { + fprintf(stderr, "wdcli: unknown option '%s'\n", arg); + fprintf(stderr, "Try 'wdcli --help' for more information.\n"); + return 1; } } - if (argc != 0 || file_name == nullptr) { - print_help(); + if (main_module == nullptr) { + fprintf(stderr, "wdcli: no main module specified\n"); + fprintf(stderr, "Usage: wdcli [options]\n"); + fprintf(stderr, "Try 'wdcli --help' for more information.\n"); return 1; } - m->warduino = wac; + dbg_info("=== LOAD MODULES INTO WARDUINO ===\n"); + std::vector loaded_modules; + + for (const char *file_path : linked_modules) { + std::string modName = getModuleName(file_path); + Module *new_mod = load(wac, file_path, modName.c_str(), + {.disable_memory_bounds = false, + .mangle_table_index = false, + .dlsym_trim_underscore = false, + .return_exception = return_exception}); + + if (new_mod) { + new_mod->warduino = wac; + loaded_modules.push_back(new_mod); + dbg_info(" Loaded linked module: %s\n", modName.c_str()); + } else { + fprintf(stderr, "wdcli: failed to load linked module '%s'\n", + file_path); + return 1; + } + } + + // Load main module last + std::string mainModName = getModuleName(main_module); + m = load(wac, main_module, mainModName.c_str(), + {.disable_memory_bounds = false, + .mangle_table_index = false, + .dlsym_trim_underscore = false, + .return_exception = return_exception}); + + if (m) { + m->warduino = wac; + loaded_modules.push_back(m); + dbg_info(" Loaded main module: %s\n", mainModName.c_str()); + } else { + fprintf(stderr, "wdcli: failed to load main module '%s'\n", + main_module); + return 1; + } + + std::vector parsed_args; + if (invoke_fname != nullptr) { + int fidx = wac->get_export_fidx(m, invoke_fname); + if (fidx < 0) { + fprintf(stderr, "wdcli: no exported function with name '%s'\n", + invoke_fname); + return 1; + } + Block function = m->functions[fidx]; + + if (invoke_raw_args.size() != function.type->param_count) { + FATAL( + "wdcli: wrong number of arguments for '%s' (expected %d, got " + "%zu)\n", + invoke_fname, function.type->param_count, + invoke_raw_args.size()); + } + + for (uint32_t i = 0; i < function.type->param_count; ++i) { + parsed_args.push_back( + parseParameter(invoke_raw_args[i], function.type->params[i])); + } + } if (initiallyPaused) { wac->debugger->pauseRuntime(m); @@ -388,12 +460,11 @@ int main(int argc, const char *argv[]) { json["primitive_fidx_mapping"] = fidx_mapping; std::cout << json << std::endl; - wac->unload_module(m); + for (auto mod : loaded_modules) wac->unload_module(mod); exit(0); } if (strcmp(mode, "proxy") == 0) { - // Run in proxy mode wac->debugger->proxify(); } else if (proxy) { // Connect to proxy device @@ -443,19 +514,21 @@ int main(int argc, const char *argv[]) { options.no_socket = no_socket; options.socket = std::stoi(socket); setupDebuggerCommunication(options); - communication = std::thread(startDebuggerCommunication); } - // Run Wasm module dbg_info("\n=== STARTED INTERPRETATION (main thread) ===\n"); - if (fname != nullptr) { - uint32_t fidx = wac->get_export_fidx(m, fname); - wac->invoke(m, fidx, arguments.size(), &arguments[0]); + if (invoke_fname != nullptr) { + uint32_t fidx = wac->get_export_fidx(m, invoke_fname); + wac->invoke(m, fidx, parsed_args.size(), parsed_args.data()); } else { wac->run_module(m); } - wac->unload_module(m); + + // Unload all + for (auto mod : loaded_modules) { + wac->unload_module(mod); + } wac->debugger->stop(); if (!no_debug) { @@ -464,4 +537,4 @@ int main(int argc, const char *argv[]) { } return 0; -} +} \ No newline at end of file diff --git a/platforms/ESP-IDF/main.cpp b/platforms/ESP-IDF/main.cpp index 0ee052a5..e49a9d3f 100644 --- a/platforms/ESP-IDF/main.cpp +++ b/platforms/ESP-IDF/main.cpp @@ -4,6 +4,8 @@ // #include +#include + #include "../../src/WARDuino.h" #include "driver/gpio.h" #include "driver/uart.h" @@ -16,15 +18,23 @@ volatile bool handelingInterrupt = false; -unsigned int wasm_len = upload_wasm_len; -unsigned char* wasm = upload_wasm; +struct ModuleInfo { + unsigned char* wasm; + unsigned int wasm_len; + const char* name; +}; + +ModuleInfo modules[] = { + {upload_wasm, upload_wasm_len, "main"}, +}; +const size_t module_count = sizeof(modules) / sizeof(modules[0]); extern "C" { extern void app_main(void); } WARDuino* wac = WARDuino::instance(); -Module* m; +std::vector loaded_modules; void startDebuggerStd(void* pvParameter) { Channel* duplex = new Duplex(stdin, stdout); @@ -36,7 +46,6 @@ void startDebuggerStd(void* pvParameter) { while (true) { taskYIELD(); vTaskDelay(1000 / portTICK_PERIOD_MS); - while ((valread = duplex->read(buffer, 1024)) != -1) { wac->handleInterrupt(valread, buffer); } @@ -44,13 +53,38 @@ void startDebuggerStd(void* pvParameter) { } void app_main(void) { - m = wac->load_module(wasm, wasm_len, {}); - // uint8_t command[] = {'0', '3', '\n'}; - // wac->handleInterrupt(3, command); + // Load all modules + for (size_t i = 0; i < module_count; i++) { + Module* mod = wac->load_module(modules[i].wasm, modules[i].wasm_len, + modules[i].name, + {.disable_memory_bounds = false, + .mangle_table_index = false, + .dlsym_trim_underscore = false, + .return_exception = true}); + + if (mod) { + loaded_modules.push_back(mod); + printf(" ✓ Loaded %s (%u bytes)\n", modules[i].name, + modules[i].wasm_len); + } else { + printf(" ✗ Failed to load %s\n", modules[i].name); + } + } + xTaskCreate(startDebuggerStd, "Debug Thread", 5000, NULL, 10 /**tskIDLE_PRIORITY*/, NULL); + printf("START\n\n"); - wac->run_module(m); + + if (!loaded_modules.empty()) { + Module* m = loaded_modules.back(); + wac->run_module(m); + } + printf("END\n\n"); - wac->unload_module(m); -} + + for (auto mod : loaded_modules) { + wac->unload_module(mod); + } + loaded_modules.clear(); +} \ No newline at end of file diff --git a/platforms/Zephyr/main.cpp b/platforms/Zephyr/main.cpp index 0dec5090..367cf666 100644 --- a/platforms/Zephyr/main.cpp +++ b/platforms/Zephyr/main.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include "../../src/WARDuino.h" #include "upload.h" @@ -16,6 +18,17 @@ static struct tty_serial console_serial; static uint8_t console_rxbuf[CONFIG_CONSOLE_GETCHAR_BUFSIZE]; static uint8_t console_txbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE]; +struct ModuleInfo { + unsigned char *wasm; + unsigned int wasm_len; + const char *name; +}; + +ModuleInfo modules[] = { + {upload_wasm, upload_wasm_len, "main"}, +}; +const size_t module_count = sizeof(modules) / sizeof(modules[0]); + ssize_t war_console_read(void *dummy, void *buf, size_t size) { ARG_UNUSED(dummy); return tty_read(&console_serial, buf, size); @@ -54,6 +67,7 @@ int war_console_init(void) { } WARDuino *wac = WARDuino::instance(); +std::vector loaded_modules; void startDebuggerStd() { Channel *duplex = new Duplex(stdin, stdout); @@ -76,9 +90,33 @@ K_THREAD_DEFINE(debugger_tid, DEBUGGER_STACK_SIZE, startDebuggerStd, NULL, NULL, NULL, DEBUGGER_PRIORITY, 0, 0); int main(void) { - Module *m = wac->load_module(upload_wasm, upload_wasm_len, {}); - wac->run_module(m); - wac->unload_module(m); + // Load all modules + for (size_t i = 0; i < module_count; i++) { + Module *mod = wac->load_module(modules[i].wasm, modules[i].wasm_len, + modules[i].name, + {.disable_memory_bounds = false, + .mangle_table_index = false, + .dlsym_trim_underscore = false, + .return_exception = true}); + + if (mod) { + loaded_modules.push_back(mod); + printk(" ✓ Loaded %s (%u bytes)\n", modules[i].name, + modules[i].wasm_len); + } else { + printk(" ✗ Failed to load %s\n", modules[i].name); + } + } + + if (!loaded_modules.empty()) { + Module *m = loaded_modules.back(); + wac->run_module(m); + } + + for (auto mod : loaded_modules) { + wac->unload_module(mod); + } + loaded_modules.clear(); return 0; -} +} \ No newline at end of file diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index dac064ab..ce4693f1 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -445,32 +445,35 @@ void Debugger::handleInvoke(Module *m, uint8_t *interruptData) const { void Debugger::handleInterruptRUN(const Module *m, RunningState *program_state) { + ExecutionContext *ectx = m->warduino->execution_context; this->channel->write("GO!\n"); - if (*program_state == WARDUINOpause && this->isBreakpoint(m->pc_ptr)) { - this->skipBreakpoint = m->pc_ptr; + if (*program_state == WARDUINOpause && this->isBreakpoint(ectx->pc_ptr)) { + this->skipBreakpoint = ectx->pc_ptr; } *program_state = WARDUINOrun; } void Debugger::handleSTEP(const Module *m, RunningState *program_state) { + ExecutionContext *ectx = m->warduino->execution_context; *program_state = WARDUINOstep; - this->skipBreakpoint = m->pc_ptr; + this->skipBreakpoint = ectx->pc_ptr; } void Debugger::handleSTEPOver(const Module *m, RunningState *program_state) { - this->skipBreakpoint = m->pc_ptr; - uint8_t const opcode = *m->pc_ptr; + ExecutionContext *ectx = m->warduino->execution_context; + this->skipBreakpoint = ectx->pc_ptr; + uint8_t const opcode = *ectx->pc_ptr; if (opcode == 0x10) { // step over direct call - uint8_t *ptr_cpy = m->pc_ptr + 1; + uint8_t *ptr_cpy = ectx->pc_ptr + 1; read_LEB_32(&ptr_cpy); - this->mark = m->pc_ptr + (ptr_cpy - m->pc_ptr); + this->mark = ectx->pc_ptr + (ptr_cpy - ectx->pc_ptr); *program_state = WARDUINOrun; // warning: ack will be BP hit } else if (opcode == 0x11) { // step over indirect call - uint8_t *ptr_cpy = m->pc_ptr + 1; + uint8_t *ptr_cpy = ectx->pc_ptr + 1; read_LEB_32(&ptr_cpy); read_LEB_32(&ptr_cpy); - this->mark = m->pc_ptr + (ptr_cpy - m->pc_ptr); + this->mark = ectx->pc_ptr + (ptr_cpy - ectx->pc_ptr); *program_state = WARDUINOrun; } else { // normal step @@ -493,11 +496,12 @@ void Debugger::handleInterruptBP(Module *m, uint8_t *interruptData) { } void Debugger::dump(Module *m, bool full) const { + ExecutionContext *ectx = m->warduino->execution_context; auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; this->channel->write("{"); // current PC - this->channel->write("\"pc\":%" PRIu32 ",", toVA(m->pc_ptr)); + this->channel->write("\"pc\":%" PRIu32 ",", toVA(ectx->pc_ptr)); this->dumpBreakpoints(m); @@ -517,10 +521,11 @@ void Debugger::dump(Module *m, bool full) const { } void Debugger::dumpStack(const Module *m) const { + ExecutionContext *ectx = m->warduino->execution_context; this->channel->write("{\"stack\": ["); - int32_t i = m->sp; + int32_t i = ectx->sp; while (0 <= i) { - this->printValue(&m->stack[i], i, i < 1); + this->printValue(&ectx->stack[i], i, i < 1); i--; } this->channel->write("]}\n\n"); @@ -554,10 +559,11 @@ void Debugger::dumpFunctions(Module *m) const { * {"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"ra":"%p"}%s */ void Debugger::dumpCallstack(Module *m) const { + ExecutionContext *ectx = m->warduino->execution_context; auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; this->channel->write("\"callstack\":["); - for (int i = 0; i <= m->csp; i++) { - const Frame *f = &m->callstack[i]; + for (int i = 0; i <= ectx->csp; i++) { + const Frame *f = &ectx->callstack[i]; int callsite_retaddr = -1; int retaddr = -1; // first frame has no retrun address @@ -573,25 +579,26 @@ void Debugger::dumpCallstack(Module *m) const { this->channel->write("\"start\":%" PRIu32 ",\"ra\":%d,\"callsite\":%d}%s", toVA(f->block->start_ptr), retaddr, - callsite_retaddr, (i < m->csp) ? "," : "]"); + callsite_retaddr, (i < ectx->csp) ? "," : "]"); } } void Debugger::dumpLocals(const Module *m) const { // fflush(stdout); - int firstFunFramePtr = m->csp; - while (m->callstack[firstFunFramePtr].block->block_type != 0) { + ExecutionContext *ectx = m->warduino->execution_context; + int firstFunFramePtr = ectx->csp; + while (ectx->callstack[firstFunFramePtr].block->block_type != 0) { firstFunFramePtr--; if (firstFunFramePtr < 0) { FATAL("Not in a function!"); } } - Frame *f = &m->callstack[firstFunFramePtr]; + Frame *f = &ectx->callstack[firstFunFramePtr]; this->channel->write(R"({"count":%u,"locals":[)", f->block->local_count); // fflush(stdout); // FIXME: this is needed for ESP to properly print for (uint32_t i = 0; i < f->block->local_count; i++) { char _value_str[256]; - auto v = &m->stack[m->fp + i]; + auto v = &ectx->stack[ectx->fp + i]; switch (v->value_type) { case I32: snprintf(_value_str, 255, @@ -726,7 +733,8 @@ bool Debugger::handleChangedLocal(const Module *m, uint8_t *bytes) const { uint32_t localId = read_LEB_32(&pos); this->channel->write("Local %u being changed\n", localId); - auto v = &m->stack[m->fp + localId]; + ExecutionContext *ectx = m->warduino->execution_context; + auto v = &ectx->stack[ectx->fp + localId]; switch (v->value_type) { case I32: v->value.uint32 = read_LEB_signed(&pos, 32); @@ -780,6 +788,7 @@ void Debugger::snapshot(Module *m) const { void Debugger::inspect(Module *m, const uint16_t sizeStateArray, const uint8_t *state) const { + ExecutionContext *ectx = m->warduino->execution_context; debug("asked for inspect\n"); uint16_t idx = 0; auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; @@ -790,7 +799,7 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray, while (idx < sizeStateArray) { switch (state[idx++]) { case pcState: { // PC - this->channel->write("\"pc\":%" PRIu32 "", toVA(m->pc_ptr)); + this->channel->write("\"pc\":%" PRIu32 "", toVA(ectx->pc_ptr)); addComma = true; break; @@ -811,8 +820,8 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray, case callstackState: { this->channel->write("%s\"callstack\":[", addComma ? "," : ""); addComma = true; - for (int j = 0; j <= m->csp; j++) { - const Frame *f = &m->callstack[j]; + for (int j = 0; j <= ectx->csp; j++) { + const Frame *f = &ectx->callstack[j]; const uint8_t bt = f->block->block_type; const uint32_t block_key = (bt == 0 || bt == 0xff || bt == 0xfe) @@ -825,7 +834,7 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray, bt, fidx, f->sp, f->fp, j); this->channel->write( "\"block_key\":%" PRIu32 ",\"ra\":%d}%s", block_key, ra, - (j < m->csp) ? "," : ""); + (j < ectx->csp) ? "," : ""); } this->channel->write("]"); break; @@ -833,9 +842,9 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray, case stackState: { this->channel->write("%s\"stack\":[", addComma ? "," : ""); addComma = true; - for (int j = 0; j <= m->sp; j++) { - auto v = &m->stack[j]; - printValue(v, j, j == m->sp); + for (int j = 0; j <= ectx->sp; j++) { + auto v = &ectx->stack[j]; + printValue(v, j, j == ectx->sp); } this->channel->write("]"); break; @@ -867,7 +876,7 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray, R"(%s"br_table":{"size":"0x%x","labels":[)", addComma ? "," : "", BR_TABLE_SIZE); for (uint32_t j = 0; j < BR_TABLE_SIZE; j++) { - this->channel->write("%" PRIu32 "%s", m->br_table[j], + this->channel->write("%" PRIu32 "%s", ectx->br_table[j], (j + 1) == BR_TABLE_SIZE ? "" : ","); } this->channel->write("]}"); @@ -1003,12 +1012,13 @@ void Debugger::handleSnapshotPolicy(Module *m) { } instructions_executed++; + ExecutionContext *ectx = m->warduino->execution_context; // Store arguments of last primitive call. - if ((fidx_called = getPrimitiveBeingCalled(m, m->pc_ptr))) { + if ((fidx_called = getPrimitiveBeingCalled(m, ectx->pc_ptr))) { const Type *type = m->functions[*fidx_called].type; for (uint32_t i = 0; i < type->param_count; i++) { prim_args[type->param_count - i - 1] = - m->stack[m->sp - i].value.uint32; + ectx->stack[ectx->sp - i].value.uint32; } } } else if (snapshotPolicy != SnapshotPolicy::none) { @@ -1048,9 +1058,10 @@ void Debugger::freeState(Module *m, uint8_t *interruptData) { // nullify state this->breakpoints.clear(); - m->csp = -1; - m->sp = -1; - memset(m->br_table, 0, BR_TABLE_SIZE); + ExecutionContext *ectx = m->warduino->execution_context; + ectx->csp = -1; + ectx->sp = -1; + memset(ectx->br_table, 0, BR_TABLE_SIZE); while (first_msg < endfm) { switch (*first_msg++) { @@ -1124,6 +1135,7 @@ void Debugger::freeState(Module *m, uint8_t *interruptData) { } bool Debugger::saveState(Module *m, uint8_t *interruptData) { + ExecutionContext *ectx = m->warduino->execution_context; uint8_t *program_state = nullptr; uint8_t *end_state = nullptr; program_state = interruptData + 1; // skip interruptLoadSnapshot @@ -1138,7 +1150,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { if (!isToPhysicalAddrPossible(pc, m)) { FATAL("cannot set pc on invalid address\n"); } - m->pc_ptr = toPhysicalAddress(pc, m); + ectx->pc_ptr = toPhysicalAddress(pc, m); debug("Updated pc %" PRIu32 "\n", pc); break; } @@ -1161,8 +1173,8 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { for (size_t i = 0; i < quantity; i++) { /* printf("frame IDX: %lu\n", i); */ uint8_t block_type = *program_state++; - m->csp += 1; - Frame *f = m->callstack + m->csp; + ectx->csp += 1; + Frame *f = ectx->callstack + ectx->csp; f->sp = read_B32_signed(&program_state); f->fp = read_B32_signed(&program_state); auto virtualRA = read_B32_signed(&program_state); @@ -1179,7 +1191,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { ". Exiting program\n", fidx, f->block->fidx); } - m->fp = f->sp + 1; + ectx->fp = f->sp + 1; } else if (block_type == 0xff || block_type == 0xfe) { debug("guard block %" PRIu8 "\n", block_type); auto *guard = @@ -1295,7 +1307,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { for (auto idx = begin_index; idx <= end_index; idx++) { // FIXME speedup with memcpy? uint32_t el = read_B32(&program_state); - m->br_table[idx] = el; + ectx->br_table[idx] = el; } break; } @@ -1310,8 +1322,8 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { if (type_index >= sizeof(valtypes)) { FATAL("received unknown type %" PRIu8 "\n", type_index); } - m->sp += 1; - StackValue *sv = &m->stack[m->sp]; + ectx->sp += 1; + StackValue *sv = &ectx->stack[ectx->sp]; sv->value.uint64 = 0; // init whole union to 0 size_t qb = type_index == 0 || type_index == 2 ? 4 : 8; sv->value_type = valtypes[type_index]; @@ -1551,7 +1563,8 @@ bool Debugger::handleUpdateStackValue(const Module *m, uint8_t *bytes) const { if (idx >= STACK_SIZE) { return false; } - StackValue *sv = &m->stack[idx]; + ExecutionContext *ectx = m->warduino->execution_context; + StackValue *sv = &ectx->stack[idx]; // ReSharper disable once CppTooWideScopeInitStatement constexpr bool decodeType = false; if (!deserialiseStackValue(bytes, decodeType, sv)) { diff --git a/src/Edward/proxy.cpp b/src/Edward/proxy.cpp index df3b4005..5b902124 100644 --- a/src/Edward/proxy.cpp +++ b/src/Edward/proxy.cpp @@ -67,7 +67,8 @@ void Proxy::returnResult(Module *m) { } // send the result to the client - rfc->result = &m->stack[m->sp]; + ExecutionContext *ectx = m->warduino->execution_context; + rfc->result = &ectx->stack[ectx->sp]; char *val = printValue(rfc->result); WARDuino::instance()->debugger->channel->write(R"({"success":true,%s})", val); @@ -104,9 +105,10 @@ StackValue *Proxy::readRFCArgs(Block *func, uint8_t *data) { void Proxy::setupCalleeArgs(Module *m, RFC *callee) { // adding arguments to the stack + ExecutionContext *ectx = m->warduino->execution_context; StackValue *args = callee->args; for (uint32_t i = 0; i < callee->type->param_count; i++) - m->stack[++m->sp] = args[i]; + ectx->stack[++ectx->sp] = args[i]; } void Proxy::pushProxyGuard(Module *m) { @@ -115,5 +117,6 @@ void Proxy::pushProxyGuard(Module *m) { } auto *guard = (Block *)malloc(sizeof(struct Block)); guard->block_type = 0xfe; // 0xfe proxy guard - m->warduino->interpreter->push_block(m, guard, m->sp); + ExecutionContext *ectx = m->warduino->execution_context; + m->warduino->interpreter->push_block(m, guard, ectx->sp); } diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index 2eba848d..328026e3 100644 --- a/src/Interpreter/instructions.cpp +++ b/src/Interpreter/instructions.cpp @@ -11,12 +11,13 @@ // performs proxy calls to an MCU bool proxy_call(Module *m, uint32_t fidx) { dbg_info("Remote Function Call %d\n", fidx); + ExecutionContext *ectx = m->warduino->execution_context; ProxySupervisor *supervisor = m->warduino->debugger->supervisor; RFC *rfc; Type *type = m->functions[fidx].type; if (type->param_count > 0) { - m->sp -= type->param_count; - StackValue *args = &m->stack[m->sp + 1]; + ectx->sp -= type->param_count; + StackValue *args = &ectx->stack[ectx->sp + 1]; rfc = new RFC(fidx, type, args); } else { rfc = new RFC(fidx, type); @@ -34,7 +35,7 @@ bool proxy_call(Module *m, uint32_t fidx) { } if (rfc->type->result_count > 0) { - m->stack[++m->sp] = *rfc->result; + ectx->stack[++ectx->sp] = *rfc->result; } return true; } @@ -95,14 +96,15 @@ Formal specification: * 0x02 */ bool i_instr_block(Module *m, uint8_t *block_ptr) { - read_LEB_32(&m->pc_ptr); // ignore block type - if (m->csp >= CALLSTACK_SIZE) { + ExecutionContext *ectx = m->warduino->execution_context; + read_LEB_32(&ectx->pc_ptr); // ignore block type + if (ectx->csp >= CALLSTACK_SIZE) { sprintf(exception, "call stack exhausted"); return false; } auto block_itr = m->block_lookup.find(block_ptr); ASSERT(block_itr != m->block_lookup.end(), "could not find block"); - m->warduino->interpreter->push_block(m, block_itr->second, m->sp); + m->warduino->interpreter->push_block(m, block_itr->second, ectx->sp); return true; } @@ -110,12 +112,14 @@ bool i_instr_block(Module *m, uint8_t *block_ptr) { * 0x03 */ bool i_instr_loop(Module *m, uint8_t *block_ptr) { - read_LEB_32(&m->pc_ptr); // ignore block type - if (m->csp >= CALLSTACK_SIZE) { + ExecutionContext *ectx = m->warduino->execution_context; + read_LEB_32(&ectx->pc_ptr); // ignore block type + if (ectx->csp >= CALLSTACK_SIZE) { sprintf(exception, "call stack exhausted"); return false; } - m->warduino->interpreter->push_block(m, m->block_lookup[block_ptr], m->sp); + m->warduino->interpreter->push_block(m, m->block_lookup[block_ptr], + ectx->sp); return true; } @@ -123,24 +127,25 @@ bool i_instr_loop(Module *m, uint8_t *block_ptr) { * 0x04 if */ bool i_instr_if(Module *m, uint8_t *block_ptr) { - read_LEB_32(&m->pc_ptr); // ignore block type + ExecutionContext *ectx = m->warduino->execution_context; + read_LEB_32(&ectx->pc_ptr); // ignore block type Block *block = m->block_lookup[block_ptr]; - if (m->csp >= CALLSTACK_SIZE) { + if (ectx->csp >= CALLSTACK_SIZE) { sprintf(exception, "call stack exhausted"); return false; } - m->warduino->interpreter->push_block(m, block, m->sp); + m->warduino->interpreter->push_block(m, block, ectx->sp); - uint32_t cond = m->stack[m->sp--].value.uint32; + uint32_t cond = ectx->stack[ectx->sp--].value.uint32; if (cond == 0) { // if false (I32) // branch to else block or after end of if if (block->else_ptr == nullptr) { // no else block, pop if block and skip end - m->csp -= 1; - m->pc_ptr = block->br_ptr + 1; + ectx->csp -= 1; + ectx->pc_ptr = block->br_ptr + 1; } else { - m->pc_ptr = block->else_ptr; + ectx->pc_ptr = block->else_ptr; } } // if true, keep going @@ -155,10 +160,11 @@ bool i_instr_if(Module *m, uint8_t *block_ptr) { * 0x05 else */ bool i_instr_else(Module *m) { - Block *block = m->callstack[m->csp].block; - m->pc_ptr = block->br_ptr; + ExecutionContext *ectx = m->warduino->execution_context; + Block *block = ectx->callstack[ectx->csp].block; + ectx->pc_ptr = block->br_ptr; #if TRACE - debug(" - of %s jump to 0x%p\n", block_repr(block), m->pc_ptr); + debug(" - of %s jump to 0x%p\n", block_repr(block), ectx->pc_ptr); #endif return true; } @@ -167,6 +173,7 @@ bool i_instr_else(Module *m) { * 0x0b end */ bool i_instr_end(Module *m, bool *prog_done) { + ExecutionContext *ectx = m->warduino->execution_context; Block *block = m->warduino->interpreter->pop_block(m); if (block == nullptr) { return false; // an exception (set by pop_block) @@ -176,12 +183,13 @@ bool i_instr_end(Module *m, bool *prog_done) { #endif if (block->block_type == 0x00) { // Function #if TRACE - dbg_warn( - " << fn0x%x(%d) %s = %s\n", block->fidx, block->fidx, - block->export_name ? block->export_name : "", - block->type->result_count > 0 ? value_repr(&m->stack[m->sp]) : "_"); + dbg_warn(" << fn0x%x(%d) %s = %s\n", block->fidx, block->fidx, + block->export_name ? block->export_name : "", + block->type->result_count > 0 + ? value_repr(&ectx->stack[ectx->sp]) + : "_"); #endif - if (m->csp == -1) { + if (ectx->csp == -1) { // Return to top-level *prog_done = true; return true; // continue execution but brake dispatch loop @@ -201,12 +209,13 @@ bool i_instr_end(Module *m, bool *prog_done) { * 0x0c br */ bool i_instr_br(Module *m) { - uint32_t depth = read_LEB_32(&m->pc_ptr); - m->csp -= depth; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t depth = read_LEB_32(&ectx->pc_ptr); + ectx->csp -= depth; // set to end for pop_block - m->pc_ptr = m->callstack[m->csp].block->br_ptr; + ectx->pc_ptr = ectx->callstack[ectx->csp].block->br_ptr; #if TRACE - debug(" - to: 0x%p\n", m->pc_ptr); + debug(" - to: 0x%p\n", ectx->pc_ptr); #endif return true; } @@ -215,13 +224,14 @@ bool i_instr_br(Module *m) { * 0x0d br_if */ bool i_instr_br_if(Module *m) { - uint32_t depth = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t depth = read_LEB_32(&ectx->pc_ptr); - uint32_t cond = m->stack[m->sp--].value.uint32; + uint32_t cond = ectx->stack[ectx->sp--].value.uint32; if (cond) { // if true - m->csp -= depth; + ectx->csp -= depth; // set to end for pop_block - m->pc_ptr = m->callstack[m->csp].block->br_ptr; + ectx->pc_ptr = ectx->callstack[ectx->csp].block->br_ptr; } #if TRACE debug(" - depth: 0x%x, cond: 0x%x, to: 0x%p\n", depth, cond, @@ -234,7 +244,8 @@ bool i_instr_br_if(Module *m) { * 0x0e br_table */ bool i_instr_br_table(Module *m) { - uint32_t count = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t count = read_LEB_32(&ectx->pc_ptr); if (count > BR_TABLE_SIZE) { // TODO: check this prior to runtime sprintf(exception, "br_table size %" PRIu32 " exceeds max %d\n", count, @@ -242,20 +253,20 @@ bool i_instr_br_table(Module *m) { return false; } for (uint32_t i = 0; i < count; i++) { - m->br_table[i] = read_LEB_32(&m->pc_ptr); + ectx->br_table[i] = read_LEB_32(&ectx->pc_ptr); } - uint32_t depth = read_LEB_32(&m->pc_ptr); + uint32_t depth = read_LEB_32(&ectx->pc_ptr); - int32_t didx = m->stack[m->sp--].value.int32; + int32_t didx = ectx->stack[ectx->sp--].value.int32; if (didx >= 0 && didx < (int32_t)count) { - depth = m->br_table[didx]; + depth = ectx->br_table[didx]; } - m->csp -= depth; + ectx->csp -= depth; // set to end for pop_block - m->pc_ptr = m->callstack[m->csp].block->br_ptr; + ectx->pc_ptr = ectx->callstack[ectx->csp].block->br_ptr; #if TRACE - debug(" - count: %d, didx: %d, to: 0x%p\n", count, didx, m->pc_ptr); + debug(" - count: %d, didx: %d, to: 0x%p\n", count, didx, ectx->pc_ptr); #endif return true; } @@ -264,14 +275,16 @@ bool i_instr_br_table(Module *m) { * 0x0f return */ bool i_instr_return(Module *m) { - while (m->csp >= 0 && m->callstack[m->csp].block->block_type != 0x00) { - m->csp--; + ExecutionContext *ectx = m->warduino->execution_context; + while (ectx->csp >= 0 && + ectx->callstack[ectx->csp].block->block_type != 0x00) { + ectx->csp--; } // Set the program count to the end of the function // The actual pop_block and return is handled by the end opcode. - m->pc_ptr = m->callstack[0].block->end_ptr; + ectx->pc_ptr = ectx->callstack[0].block->end_ptr; #if TRACE - debug(" - to: 0x%p\n", m->pc_ptr); + debug(" - to: 0x%p\n", ectx->pc_ptr); #endif return true; } @@ -280,26 +293,50 @@ bool i_instr_return(Module *m) { * 0x10 call */ bool i_instr_call(Module *m) { - uint32_t fidx = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t fidx = read_LEB_32(&ectx->pc_ptr); if (m->warduino->debugger->isProxied(fidx)) { return proxy_call(m, fidx); } if (fidx < m->import_count) { + Block *func = &m->functions[fidx]; + // Mocking only works on primitives, no need to check for it otherwise. - if (m->sp >= 0) { - uint32_t arg = m->stack[m->sp].value.uint32; + if (ectx->sp >= 0) { + uint32_t arg = ectx->stack[ectx->sp].value.uint32; if (m->warduino->debugger->isMocked(fidx, arg)) { - m->stack[m->sp].value.uint32 = + ectx->stack[ectx->sp].value.uint32 = m->warduino->debugger->getMockedValue(fidx, arg); return true; } } + if (func->import_module != nullptr && + strcmp(func->import_module, "env") != 0) { + // Cross-module call! + Module *target = m->warduino->get_module(func->import_module); + if (target != nullptr) { + // Find the exported function in target module + uint32_t target_fidx = + m->warduino->get_export_fidx(target, func->import_field); + if (target_fidx != (uint32_t)-1) { + // Switch to target module and call + m->warduino->switch_to_module(target); + m->warduino->interpreter->setup_call(target, target_fidx); + return true; + } + } else { + sprintf(exception, "import module %s not found", + func->import_module); + return false; + } + } + return ((Primitive)m->functions[fidx].func_ptr)(m); } else { - if (m->csp >= CALLSTACK_SIZE) { + if (ectx->csp >= CALLSTACK_SIZE) { sprintf(exception, "call stack exhausted"); return false; } @@ -315,10 +352,10 @@ bool i_instr_call(Module *m) { * 0x11 call_indirect */ bool i_instr_call_indirect(Module *m) { - uint32_t tidx = read_LEB_32(&m->pc_ptr); // TODO: use tidx? - (void)tidx; - read_LEB_32(&m->pc_ptr); // reserved immediate - uint32_t val = m->stack[m->sp--].value.uint32; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t tidx = read_LEB_32(&ectx->pc_ptr); + read_LEB_32(&ectx->pc_ptr); // reserved immediate + uint32_t val = ectx->stack[ectx->sp--].value.uint32; if (m->options.mangle_table_index) { // val is the table address + the index (not sized for the // pointer size) so get the actual (sized) index @@ -338,18 +375,23 @@ bool i_instr_call_indirect(Module *m) { } uint32_t fidx = m->table.entries[val]; + + Module *target_module = m; + if (m->table.imported && m->table.owner != nullptr) { + target_module = m->table.owner; + } #if TRACE debug(" - call_indirect tidx: %d, val: 0x%x, fidx: 0x%x\n", tidx, val, fidx); #endif - if (fidx < m->import_count) { + if (fidx < target_module->import_count) { // THUNK thunk_out(m, fidx); // import/thunk call } else { - Block *func = &m->functions[fidx]; + Block *func = &target_module->functions[fidx]; Type *ftype = func->type; - if (m->csp >= CALLSTACK_SIZE) { + if (ectx->csp >= CALLSTACK_SIZE) { sprintf(exception, "call stack exhausted"); return false; } @@ -360,17 +402,22 @@ bool i_instr_call_indirect(Module *m) { return false; } - m->warduino->interpreter->setup_call(m, fidx); // regular function call + if (target_module != m) { + m->warduino->switch_to_module(target_module); + } + + m->warduino->interpreter->setup_call(target_module, + fidx); // regular function call // Validate signatures match if ((int)(ftype->param_count + func->local_count) != - m->sp - m->fp + 1) { + ectx->sp - ectx->fp + 1) { sprintf(exception, "indirect call type mismatch (param counts differ)"); return false; } for (uint32_t f = 0; f < ftype->param_count; f++) { - if (ftype->params[f] != m->stack[m->fp + f].value_type) { + if (ftype->params[f] != ectx->stack[ectx->fp + f].value_type) { sprintf(exception, "indirect call type mismatch (param types differ)"); return false; @@ -392,7 +439,8 @@ bool i_instr_call_indirect(Module *m) { * remove a value from the stack */ bool i_instr_drop(Module *m) { - m->sp--; + ExecutionContext *ectx = m->warduino->execution_context; + ectx->sp--; return true; } @@ -406,10 +454,11 @@ bool i_instr_drop(Module *m) { * else : push val_2 to the stack */ bool i_instr_select(Module *m) { - uint32_t cond = m->stack[m->sp--].value.uint32; - m->sp--; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t cond = ectx->stack[ectx->sp--].value.uint32; + ectx->sp--; if (!cond) { // use a instead of b - m->stack[m->sp] = m->stack[m->sp + 1]; + ectx->stack[ectx->sp] = ectx->stack[ectx->sp + 1]; } return true; } @@ -419,12 +468,13 @@ bool i_instr_select(Module *m) { * move the i-th local to the top of the stack */ bool i_instr_get_local(Module *m) { - int32_t arg = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + int32_t arg = read_LEB_32(&ectx->pc_ptr); #if TRACE debug(" - arg: 0x%x, got %s\n", arg, - value_repr(&m->stack[m->fp + arg])); + value_repr(&ectx->stack[ectx->fp + arg])); #endif - m->stack[++m->sp] = m->stack[m->fp + arg]; + ectx->stack[++ectx->sp] = ectx->stack[ectx->fp + arg]; return true; } @@ -432,11 +482,12 @@ bool i_instr_get_local(Module *m) { * 0x21 set_local */ bool i_instr_set_local(Module *m) { - int32_t arg = read_LEB_32(&m->pc_ptr); - m->stack[m->fp + arg] = m->stack[m->sp--]; + ExecutionContext *ectx = m->warduino->execution_context; + int32_t arg = read_LEB_32(&ectx->pc_ptr); + ectx->stack[ectx->fp + arg] = ectx->stack[ectx->sp--]; #if TRACE debug(" - arg: 0x%x, to %s (stack loc: %d)\n", arg, - value_repr(&m->stack[m->sp + 1]), m->fp + arg); + value_repr(&ectx->stack[ectx->sp + 1]), ectx->fp + arg); #endif return true; } @@ -445,10 +496,12 @@ bool i_instr_set_local(Module *m) { * 0x0d tee_local */ bool i_instr_tee_local(Module *m) { - int32_t arg = read_LEB_32(&m->pc_ptr); - m->stack[m->fp + arg] = m->stack[m->sp]; + ExecutionContext *ectx = m->warduino->execution_context; + int32_t arg = read_LEB_32(&ectx->pc_ptr); + ectx->stack[ectx->fp + arg] = ectx->stack[ectx->sp]; #if TRACE - debug(" - arg: 0x%x, to %s\n", arg, value_repr(&m->stack[m->sp])); + debug(" - arg: 0x%x, to %s\n", arg, + value_repr(&ectx->stack[ectx->sp])); #endif return true; } @@ -457,11 +510,12 @@ bool i_instr_tee_local(Module *m) { * 0x23 get_global */ bool i_instr_get_global(Module *m) { - int32_t arg = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + int32_t arg = read_LEB_32(&ectx->pc_ptr); #if TRACE debug(" - arg: 0x%x, got %s\n", arg, value_repr(m->globals[arg])); #endif - m->stack[++m->sp] = *m->globals[arg]->value; + ectx->stack[++ectx->sp] = *m->globals[arg]->value; return true; } @@ -469,10 +523,12 @@ bool i_instr_get_global(Module *m) { * 0x24 set_global */ bool i_instr_set_global(Module *m) { - uint32_t arg = read_LEB_32(&m->pc_ptr); - *m->globals[arg]->value = m->stack[m->sp--]; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t arg = read_LEB_32(&ectx->pc_ptr); + *m->globals[arg]->value = ectx->stack[ectx->sp--]; #if TRACE - debug(" - arg: 0x%x, got %s\n", arg, value_repr(&m->stack[m->sp + 1])); + debug(" - arg: 0x%x, got %s\n", arg, + value_repr(&ectx->stack[ectx->sp + 1])); #endif return true; } @@ -481,9 +537,10 @@ bool i_instr_set_global(Module *m) { * 0x3f current_memory */ bool i_instr_current_memory(Module *m) { - read_LEB_32(&m->pc_ptr); // ignore reserved - m->stack[++m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = m->memory.pages; + ExecutionContext *ectx = m->warduino->execution_context; + read_LEB_32(&ectx->pc_ptr); // ignore reserved + ectx->stack[++ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = m->memory.pages; return true; } @@ -491,14 +548,15 @@ bool i_instr_current_memory(Module *m) { * 0x40 grow_memory */ bool i_instr_grow_memory(Module *m) { - read_LEB_32(&m->pc_ptr); // ignore reserved + ExecutionContext *ectx = m->warduino->execution_context; + read_LEB_32(&ectx->pc_ptr); // ignore reserved uint32_t prev_pages = m->memory.pages; - uint32_t delta = m->stack[m->sp].value.uint32; - m->stack[m->sp].value.uint32 = prev_pages; + uint32_t delta = ectx->stack[ectx->sp].value.uint32; + ectx->stack[ectx->sp].value.uint32 = prev_pages; if (delta == 0) { return true; // No change } else if (delta + prev_pages > m->memory.maximum) { - m->stack[m->sp].value.uint32 = static_cast(-1); + ectx->stack[ectx->sp].value.uint32 = static_cast(-1); return true; } m->memory.pages += delta; @@ -512,9 +570,10 @@ bool i_instr_grow_memory(Module *m) { * 0x0d XXX */ bool i_instr_mem_load(Module *m, uint8_t opcode) { - uint32_t flags = read_LEB_32(&m->pc_ptr); - uint32_t offset = read_LEB_32(&m->pc_ptr); - uint32_t addr = m->stack[m->sp--].value.uint32; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t flags = read_LEB_32(&ectx->pc_ptr); + uint32_t offset = read_LEB_32(&ectx->pc_ptr); + uint32_t addr = ectx->stack[ectx->sp--].value.uint32; if (flags != 2 && TRACE) { dbg_info( " - unaligned load - flags: 0x%x," @@ -527,11 +586,12 @@ bool i_instr_mem_load(Module *m, uint8_t opcode) { } bool i_instr_mem_store(Module *m, uint8_t opcode) { - StackValue *sval = &m->stack[m->sp--]; - uint32_t flags = read_LEB_32(&m->pc_ptr); - uint32_t offset = read_LEB_32(&m->pc_ptr); + ExecutionContext *ectx = m->warduino->execution_context; + StackValue *sval = &ectx->stack[ectx->sp--]; + uint32_t flags = read_LEB_32(&ectx->pc_ptr); + uint32_t offset = read_LEB_32(&ectx->pc_ptr); - uint32_t addr = m->stack[m->sp--].value.uint32; + uint32_t addr = ectx->stack[ectx->sp--].value.uint32; if (flags != 2 && TRACE) { dbg_info( @@ -554,26 +614,27 @@ bool i_instr_mem_store(Module *m, uint8_t opcode) { * 0x41...0x44 const */ bool i_instr_const(Module *m, uint8_t opcode) { - StackValue *target = &m->stack[++m->sp]; + ExecutionContext *ectx = m->warduino->execution_context; + StackValue *target = &ectx->stack[++ectx->sp]; switch (opcode) { case 0x41: // i32.const target->value_type = I32; - target->value.uint32 = read_LEB_signed(&m->pc_ptr, 32); + target->value.uint32 = read_LEB_signed(&ectx->pc_ptr, 32); break; case 0x42: // i64.const target->value_type = I64; - target->value.int64 = read_LEB_signed(&m->pc_ptr, 64); + target->value.int64 = read_LEB_signed(&ectx->pc_ptr, 64); break; case 0x43: // f32.const target->value_type = F32; - memcpy(&target->value.uint32, m->pc_ptr, 4); - m->pc_ptr += 4; + memcpy(&target->value.uint32, ectx->pc_ptr, 4); + ectx->pc_ptr += 4; break; case 0x44: // f64.const target->value_type = F64; - memcpy(&target->value.uint64, m->pc_ptr, 8); - m->pc_ptr += 8; + memcpy(&target->value.uint64, ectx->pc_ptr, 8); + ectx->pc_ptr += 8; break; default: return false; @@ -585,15 +646,16 @@ bool i_instr_const(Module *m, uint8_t opcode) { * 0x45 eqz */ bool i_instr_unary_u32(Module *m, uint8_t opcode) { + ExecutionContext *ectx = m->warduino->execution_context; switch (opcode) { case 0x45: // i32.eqz - m->stack[m->sp].value.uint32 = - static_cast(m->stack[m->sp].value.uint32 == 0); + ectx->stack[ectx->sp].value.uint32 = + static_cast(ectx->stack[ectx->sp].value.uint32 == 0); break; case 0x50: // i64.eqz - m->stack[m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = - static_cast(m->stack[m->sp].value.uint64 == 0); + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = + static_cast(ectx->stack[ectx->sp].value.uint64 == 0); break; default: return false; @@ -605,10 +667,11 @@ bool i_instr_unary_u32(Module *m, uint8_t opcode) { * 0x0d binop32 */ bool i_instr_math_u32(Module *m, uint8_t opcode) { - uint32_t a = m->stack[m->sp - 1].value.uint32; - uint32_t b = m->stack[m->sp].value.uint32; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t a = ectx->stack[ectx->sp - 1].value.uint32; + uint32_t b = ectx->stack[ectx->sp].value.uint32; uint32_t c; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0x46: c = static_cast(a == b); @@ -643,8 +706,8 @@ bool i_instr_math_u32(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = c; return true; } @@ -652,10 +715,11 @@ bool i_instr_math_u32(Module *m, uint8_t opcode) { * 0x0d binop64 */ bool i_instr_math_u64(Module *m, uint8_t opcode) { - uint64_t d = m->stack[m->sp - 1].value.uint64; - uint64_t e = m->stack[m->sp].value.uint64; + ExecutionContext *ectx = m->warduino->execution_context; + uint64_t d = ectx->stack[ectx->sp - 1].value.uint64; + uint64_t e = ectx->stack[ectx->sp].value.uint64; uint32_t c; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0x51: c = static_cast(d == e); @@ -690,8 +754,8 @@ bool i_instr_math_u64(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = c; return true; } @@ -699,10 +763,11 @@ bool i_instr_math_u64(Module *m, uint8_t opcode) { * 0x0d binop64 */ bool i_instr_math_f32(Module *m, uint8_t opcode) { - float g = m->stack[m->sp - 1].value.f32; - float h = m->stack[m->sp].value.f32; + ExecutionContext *ectx = m->warduino->execution_context; + float g = ectx->stack[ectx->sp - 1].value.f32; + float h = ectx->stack[ectx->sp].value.f32; uint32_t c; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0x5b: c = static_cast(g == h); @@ -725,8 +790,8 @@ bool i_instr_math_f32(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = c; return true; } @@ -734,11 +799,12 @@ bool i_instr_math_f32(Module *m, uint8_t opcode) { * 0x0d binopf64 */ bool i_instr_math_f64(Module *m, uint8_t opcode) { - double j = m->stack[m->sp - 1].value.f64; - double k = m->stack[m->sp].value.f64; + ExecutionContext *ectx = m->warduino->execution_context; + double j = ectx->stack[ectx->sp - 1].value.f64; + double k = ectx->stack[ectx->sp].value.f64; uint32_t c; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0x61: c = static_cast(j == k); @@ -761,13 +827,14 @@ bool i_instr_math_f64(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value_type = I32; - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = c; return true; } bool i_instr_unary_i32(Module *m, uint8_t opcode) { - uint32_t a = m->stack[m->sp].value.uint32; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t a = ectx->stack[ectx->sp].value.uint32; uint32_t c; switch (opcode) { case 0x67: @@ -782,12 +849,13 @@ bool i_instr_unary_i32(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value.uint32 = c; return true; } bool i_instr_unary_i64(Module *m, uint8_t opcode) { - uint64_t d = m->stack[m->sp].value.uint64; + ExecutionContext *ectx = m->warduino->execution_context; + uint64_t d = ectx->stack[ectx->sp].value.uint64; uint64_t f; switch (opcode) { case 0x79: @@ -802,7 +870,7 @@ bool i_instr_unary_i64(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value.uint64 = f; + ectx->stack[ectx->sp].value.uint64 = f; return true; } @@ -810,51 +878,64 @@ bool i_instr_unary_i64(Module *m, uint8_t opcode) { * 0x0d XXX */ bool i_instr_unary_floating(Module *m, uint8_t opcode) { + ExecutionContext *ectx = m->warduino->execution_context; switch (opcode) { // unary f32 case 0x8b: - m->stack[m->sp].value.f32 = fabs(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + fabs(ectx->stack[ectx->sp].value.f32); break; // f32.abs case 0x8c: - m->stack[m->sp].value.f32 = -m->stack[m->sp].value.f32; + ectx->stack[ectx->sp].value.f32 = -ectx->stack[ectx->sp].value.f32; break; // f32.neg case 0x8d: - m->stack[m->sp].value.f32 = ceil(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + ceil(ectx->stack[ectx->sp].value.f32); break; // f32.ceil case 0x8e: - m->stack[m->sp].value.f32 = floor(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + floor(ectx->stack[ectx->sp].value.f32); break; // f32.floor case 0x8f: - m->stack[m->sp].value.f32 = trunc(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + trunc(ectx->stack[ectx->sp].value.f32); break; // f32.trunc case 0x90: - m->stack[m->sp].value.f32 = rint(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + rint(ectx->stack[ectx->sp].value.f32); break; // f32.nearest case 0x91: - m->stack[m->sp].value.f32 = sqrt(m->stack[m->sp].value.f32); + ectx->stack[ectx->sp].value.f32 = + sqrt(ectx->stack[ectx->sp].value.f32); break; // f32.sqrt // unary f64 case 0x99: - m->stack[m->sp].value.f64 = fabs(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + fabs(ectx->stack[ectx->sp].value.f64); break; // f64.abs case 0x9a: - m->stack[m->sp].value.f64 = -m->stack[m->sp].value.f64; + ectx->stack[ectx->sp].value.f64 = -ectx->stack[ectx->sp].value.f64; break; // f64.neg case 0x9b: - m->stack[m->sp].value.f64 = ceil(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + ceil(ectx->stack[ectx->sp].value.f64); break; // f64.ceil case 0x9c: - m->stack[m->sp].value.f64 = floor(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + floor(ectx->stack[ectx->sp].value.f64); break; // f64.floor case 0x9d: - m->stack[m->sp].value.f64 = trunc(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + trunc(ectx->stack[ectx->sp].value.f64); break; // f64.trunc case 0x9e: - m->stack[m->sp].value.f64 = rint(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + rint(ectx->stack[ectx->sp].value.f64); break; // f64.nearest case 0x9f: - m->stack[m->sp].value.f64 = sqrt(m->stack[m->sp].value.f64); + ectx->stack[ectx->sp].value.f64 = + sqrt(ectx->stack[ectx->sp].value.f64); break; // f64.sqrt default: return false; @@ -867,10 +948,11 @@ bool i_instr_unary_floating(Module *m, uint8_t opcode) { */ bool i_instr_binary_i32(Module *m, uint8_t opcode) { // TODO: verify if this should not be done with int32_t instead - uint32_t a = m->stack[m->sp - 1].value.uint32; - uint32_t b = m->stack[m->sp].value.uint32; + ExecutionContext *ectx = m->warduino->execution_context; + uint32_t a = ectx->stack[ectx->sp - 1].value.uint32; + uint32_t b = ectx->stack[ectx->sp].value.uint32; uint32_t c; - m->sp -= 1; + ectx->sp -= 1; if (opcode >= 0x6d && opcode <= 0x70 && b == 0) { sprintf(exception, "integer divide by zero"); return false; @@ -939,7 +1021,7 @@ bool i_instr_binary_i32(Module *m, uint8_t opcode) { // sprintf(exception, "integer overflow"); // return false; //} - m->stack[m->sp].value.uint32 = c; + ectx->stack[ectx->sp].value.uint32 = c; return true; } @@ -947,10 +1029,11 @@ bool i_instr_binary_i32(Module *m, uint8_t opcode) { * 0x0d XXX */ bool i_instr_binary_i64(Module *m, uint8_t opcode) { - uint64_t d = m->stack[m->sp - 1].value.uint64; - uint64_t e = m->stack[m->sp].value.uint64; + ExecutionContext *ectx = m->warduino->execution_context; + uint64_t d = ectx->stack[ectx->sp - 1].value.uint64; + uint64_t e = ectx->stack[ectx->sp].value.uint64; uint64_t f; - m->sp -= 1; + ectx->sp -= 1; if (opcode >= 0x7f && opcode <= 0x82 && e == 0) { sprintf(exception, "integer divide by zero"); return false; @@ -1012,7 +1095,7 @@ bool i_instr_binary_i64(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value.uint64 = f; + ectx->stack[ectx->sp].value.uint64 = f; return true; } @@ -1021,10 +1104,11 @@ bool i_instr_binary_i64(Module *m, uint8_t opcode) { * 0x0d XXX */ bool i_instr_binary_f32(Module *m, uint8_t opcode) { - float g = m->stack[m->sp - 1].value.f32; - float h = m->stack[m->sp].value.f32; + ExecutionContext *ectx = m->warduino->execution_context; + float g = ectx->stack[ectx->sp - 1].value.f32; + float h = ectx->stack[ectx->sp].value.f32; float i; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0x92: i = g + h; @@ -1050,7 +1134,7 @@ bool i_instr_binary_f32(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value.f32 = i; + ectx->stack[ectx->sp].value.f32 = i; return true; } @@ -1058,10 +1142,11 @@ bool i_instr_binary_f32(Module *m, uint8_t opcode) { * 0x0d XXX */ bool i_instr_binary_f64(Module *m, uint8_t opcode) { - double j = m->stack[m->sp - 1].value.f64; - double k = m->stack[m->sp].value.f64; + ExecutionContext *ectx = m->warduino->execution_context; + double j = ectx->stack[ectx->sp - 1].value.f64; + double k = ectx->stack[ectx->sp].value.f64; double l; - m->sp -= 1; + ectx->sp -= 1; switch (opcode) { case 0xa0: l = j + k; @@ -1087,7 +1172,7 @@ bool i_instr_binary_f64(Module *m, uint8_t opcode) { default: return false; } - m->stack[m->sp].value.f64 = l; + ectx->stack[ectx->sp].value.f64 = l; return true; } @@ -1096,170 +1181,182 @@ bool i_instr_binary_f64(Module *m, uint8_t opcode) { * 0x0d XXX */ bool i_instr_conversion(Module *m, uint8_t opcode) { + ExecutionContext *ectx = m->warduino->execution_context; switch (opcode) { case 0xa7: - m->stack[m->sp].value.uint64 &= 0x00000000ffffffff; - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint64 &= 0x00000000ffffffff; + ectx->stack[ectx->sp].value_type = I32; break; // i32.wrap/i64 case 0xa8: - if (std::isnan(m->stack[m->sp].value.f32)) { + if (std::isnan(ectx->stack[ectx->sp].value.f32)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f32 >= INT32_MAX || - m->stack[m->sp].value.f32 < INT32_MIN) { + } else if (ectx->stack[ectx->sp].value.f32 >= INT32_MAX || + ectx->stack[ectx->sp].value.f32 < INT32_MIN) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.int32 = m->stack[m->sp].value.f32; - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value.int32 = ectx->stack[ectx->sp].value.f32; + ectx->stack[ectx->sp].value_type = I32; break; // i32.trunc_s/f32 case 0xa9: - if (std::isnan(m->stack[m->sp].value.f32)) { + if (std::isnan(ectx->stack[ectx->sp].value.f32)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f32 >= UINT32_MAX || - m->stack[m->sp].value.f32 <= -1) { + } else if (ectx->stack[ectx->sp].value.f32 >= UINT32_MAX || + ectx->stack[ectx->sp].value.f32 <= -1) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.uint32 = m->stack[m->sp].value.f32; - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = + ectx->stack[ectx->sp].value.f32; + ectx->stack[ectx->sp].value_type = I32; break; // i32.trunc_u/f32 case 0xaa: - if (std::isnan(m->stack[m->sp].value.f64)) { + if (std::isnan(ectx->stack[ectx->sp].value.f64)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f64 > INT32_MAX || - m->stack[m->sp].value.f64 < INT32_MIN) { + } else if (ectx->stack[ectx->sp].value.f64 > INT32_MAX || + ectx->stack[ectx->sp].value.f64 < INT32_MIN) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.int32 = m->stack[m->sp].value.f64; - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value.int32 = ectx->stack[ectx->sp].value.f64; + ectx->stack[ectx->sp].value_type = I32; break; // i32.trunc_s/f64 case 0xab: - if (std::isnan(m->stack[m->sp].value.f64)) { + if (std::isnan(ectx->stack[ectx->sp].value.f64)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f64 > UINT32_MAX || - m->stack[m->sp].value.f64 <= -1) { + } else if (ectx->stack[ectx->sp].value.f64 > UINT32_MAX || + ectx->stack[ectx->sp].value.f64 <= -1) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.uint32 = m->stack[m->sp].value.f64; - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value.uint32 = + ectx->stack[ectx->sp].value.f64; + ectx->stack[ectx->sp].value_type = I32; break; // i32.trunc_u/f64 case 0xac: - m->stack[m->sp].value.uint64 = m->stack[m->sp].value.uint32; - sext_32_64(&m->stack[m->sp].value.uint64); - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.uint64 = + ectx->stack[ectx->sp].value.uint32; + sext_32_64(&ectx->stack[ectx->sp].value.uint64); + ectx->stack[ectx->sp].value_type = I64; break; // i64.extend_s/i32 case 0xad: - m->stack[m->sp].value.uint64 = m->stack[m->sp].value.uint32; - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.uint64 = + ectx->stack[ectx->sp].value.uint32; + ectx->stack[ectx->sp].value_type = I64; break; // i64.extend_u/i32 case 0xae: - if (std::isnan(m->stack[m->sp].value.f32)) { + if (std::isnan(ectx->stack[ectx->sp].value.f32)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f32 >= INT64_MAX || - m->stack[m->sp].value.f32 < INT64_MIN) { + } else if (ectx->stack[ectx->sp].value.f32 >= INT64_MAX || + ectx->stack[ectx->sp].value.f32 < INT64_MIN) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.int64 = m->stack[m->sp].value.f32; - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.int64 = ectx->stack[ectx->sp].value.f32; + ectx->stack[ectx->sp].value_type = I64; break; // i64.trunc_s/f32 case 0xaf: - if (std::isnan(m->stack[m->sp].value.f32)) { + if (std::isnan(ectx->stack[ectx->sp].value.f32)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f32 >= UINT64_MAX || - m->stack[m->sp].value.f32 <= -1) { + } else if (ectx->stack[ectx->sp].value.f32 >= UINT64_MAX || + ectx->stack[ectx->sp].value.f32 <= -1) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.uint64 = m->stack[m->sp].value.f32; - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.uint64 = + ectx->stack[ectx->sp].value.f32; + ectx->stack[ectx->sp].value_type = I64; break; // i64.trunc_u/f32 case 0xb0: - if (std::isnan(m->stack[m->sp].value.f64)) { + if (std::isnan(ectx->stack[ectx->sp].value.f64)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f64 >= INT64_MAX || - m->stack[m->sp].value.f64 < INT64_MIN) { + } else if (ectx->stack[ectx->sp].value.f64 >= INT64_MAX || + ectx->stack[ectx->sp].value.f64 < INT64_MIN) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.int64 = m->stack[m->sp].value.f64; - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.int64 = ectx->stack[ectx->sp].value.f64; + ectx->stack[ectx->sp].value_type = I64; break; // i64.trunc_s/f64 case 0xb1: - if (std::isnan(m->stack[m->sp].value.f64)) { + if (std::isnan(ectx->stack[ectx->sp].value.f64)) { sprintf(exception, "invalid conversion to integer"); return false; - } else if (m->stack[m->sp].value.f64 >= UINT64_MAX || - m->stack[m->sp].value.f64 <= -1) { + } else if (ectx->stack[ectx->sp].value.f64 >= UINT64_MAX || + ectx->stack[ectx->sp].value.f64 <= -1) { sprintf(exception, "integer overflow"); return false; } - m->stack[m->sp].value.uint64 = m->stack[m->sp].value.f64; - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value.uint64 = + ectx->stack[ectx->sp].value.f64; + ectx->stack[ectx->sp].value_type = I64; break; // i64.trunc_u/f64 case 0xb2: - m->stack[m->sp].value.f32 = m->stack[m->sp].value.int32; - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value.f32 = ectx->stack[ectx->sp].value.int32; + ectx->stack[ectx->sp].value_type = F32; break; // f32.convert_s/i32 case 0xb3: - m->stack[m->sp].value.f32 = m->stack[m->sp].value.uint32; - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value.f32 = + ectx->stack[ectx->sp].value.uint32; + ectx->stack[ectx->sp].value_type = F32; break; // f32.convert_u/i32 case 0xb4: - m->stack[m->sp].value.f32 = m->stack[m->sp].value.int64; - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value.f32 = ectx->stack[ectx->sp].value.int64; + ectx->stack[ectx->sp].value_type = F32; break; // f32.convert_s/i64 case 0xb5: - m->stack[m->sp].value.f32 = m->stack[m->sp].value.uint64; - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value.f32 = + ectx->stack[ectx->sp].value.uint64; + ectx->stack[ectx->sp].value_type = F32; break; // f32.convert_u/i64 case 0xb6: - m->stack[m->sp].value.f32 = (float)m->stack[m->sp].value.f64; - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value.f32 = + (float)ectx->stack[ectx->sp].value.f64; + ectx->stack[ectx->sp].value_type = F32; break; // f32.demote/f64 case 0xb7: - m->stack[m->sp].value.f64 = m->stack[m->sp].value.int32; - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value.f64 = ectx->stack[ectx->sp].value.int32; + ectx->stack[ectx->sp].value_type = F64; break; // f64.convert_s/i32 case 0xb8: - m->stack[m->sp].value.f64 = m->stack[m->sp].value.uint32; - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value.f64 = + ectx->stack[ectx->sp].value.uint32; + ectx->stack[ectx->sp].value_type = F64; break; // f64.convert_u/i32 case 0xb9: - m->stack[m->sp].value.f64 = m->stack[m->sp].value.int64; - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value.f64 = ectx->stack[ectx->sp].value.int64; + ectx->stack[ectx->sp].value_type = F64; break; // f64.convert_s/i64 case 0xba: - m->stack[m->sp].value.f64 = m->stack[m->sp].value.uint64; - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value.f64 = + ectx->stack[ectx->sp].value.uint64; + ectx->stack[ectx->sp].value_type = F64; break; // f64.convert_u/i64 case 0xbb: - m->stack[m->sp].value.f64 = m->stack[m->sp].value.f32; - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value.f64 = ectx->stack[ectx->sp].value.f32; + ectx->stack[ectx->sp].value_type = F64; break; // f64.promote/f32 // reinterpretations case 0xbc: - m->stack[m->sp].value_type = I32; + ectx->stack[ectx->sp].value_type = I32; break; // i32.reinterpret/f32 case 0xbd: - m->stack[m->sp].value_type = I64; + ectx->stack[ectx->sp].value_type = I64; break; // i64.reinterpret/f64 case 0xbe: // memmove(&m->stack[m->sp].value.f32, // &m->stack[m->sp].value.uint32, 4); - m->stack[m->sp].value_type = F32; + ectx->stack[ectx->sp].value_type = F32; break; // f32.reinterpret/i32 case 0xbf: - m->stack[m->sp].value_type = F64; + ectx->stack[ectx->sp].value_type = F64; break; // f64.reinterpret/i64 default: return false; @@ -1269,7 +1366,8 @@ bool i_instr_conversion(Module *m, uint8_t opcode) { } bool i_instr_extension(Module *m, uint8_t opcode) { - auto &v = m->stack[m->sp].value; + ExecutionContext *ectx = m->warduino->execution_context; + auto &v = ectx->stack[ectx->sp].value; switch (opcode) { case 0xc0: // i32.extend8_s diff --git a/src/Interpreter/interpreter.cpp b/src/Interpreter/interpreter.cpp index 834c4f4f..436f2f95 100644 --- a/src/Interpreter/interpreter.cpp +++ b/src/Interpreter/interpreter.cpp @@ -10,15 +10,20 @@ #include "instructions.h" void Interpreter::push_block(Module *m, Block *block, int sp) { - m->csp += 1; - m->callstack[m->csp].block = block; - m->callstack[m->csp].sp = sp; - m->callstack[m->csp].fp = m->fp; - m->callstack[m->csp].ra_ptr = m->pc_ptr; + ExecutionContext *ectx = m->warduino->execution_context; + + ectx->csp += 1; + ectx->callstack[ectx->csp].block = block; + ectx->callstack[ectx->csp].sp = sp; + ectx->callstack[ectx->csp].fp = ectx->fp; + ectx->callstack[ectx->csp].ra_ptr = ectx->pc_ptr; + + ectx->callstack[ectx->csp].module = m; } Block *Interpreter::pop_block(Module *m) { - Frame *frame = &m->callstack[m->csp--]; + ExecutionContext *ectx = m->warduino->execution_context; + Frame *frame = &ectx->callstack[ectx->csp--]; Type *t = frame->block->type; if (frame->block->block_type == 0xff) { @@ -26,7 +31,7 @@ Block *Interpreter::pop_block(Module *m) { // free if event guard free(frame->block); frame->block = nullptr; - frame = &m->callstack[m->csp--]; + frame = &ectx->callstack[ectx->csp--]; t = frame->block->type; } @@ -36,17 +41,17 @@ Block *Interpreter::pop_block(Module *m) { // free if proxy guard free(frame->block); frame->block = nullptr; - frame = &m->callstack[m->csp--]; + frame = &ectx->callstack[ectx->csp--]; t = frame->block->type; } // TODO: validate return value if there is one - m->fp = frame->fp; // Restore frame pointer + ectx->fp = frame->fp; // Restore frame pointer // Validate the return value if (t->result_count == 1) { - if (m->stack[m->sp].value_type != t->results[0]) { + if (ectx->stack[ectx->sp].value_type != t->results[0]) { sprintf(exception, "call type mismatch"); return nullptr; } @@ -55,61 +60,72 @@ Block *Interpreter::pop_block(Module *m) { // Restore stack pointer if (t->result_count == 1) { // Save top value as result - if (frame->sp < m->sp) { - m->stack[frame->sp + 1] = m->stack[m->sp]; - m->sp = frame->sp + 1; + if (frame->sp < ectx->sp) { + ectx->stack[frame->sp + 1] = ectx->stack[ectx->sp]; + ectx->sp = frame->sp + 1; } } else { - if (frame->sp < m->sp) { - m->sp = frame->sp; + if (frame->sp < ectx->sp) { + ectx->sp = frame->sp; } } if (frame->block->block_type == 0x00) { // Function, set pc to return address - m->pc_ptr = frame->ra_ptr; + ectx->pc_ptr = frame->ra_ptr; + + if (ectx->csp >= 0) { + Module *caller_module = ectx->callstack[ectx->csp].module; + if (caller_module != m) { + m->warduino->switch_to_module(caller_module); + } + } } return frame->block; } void Interpreter::setup_call(Module *m, uint32_t fidx) { + ExecutionContext *ectx = m->warduino->execution_context; Block *func = &m->functions[fidx]; Type *type = func->type; // Push current frame on the call stack - push_block(m, func, m->sp - type->param_count); + push_block(m, func, ectx->sp - type->param_count); #if TRACE dbg_warn(" >> fn0x%x(%d) %s(", fidx, fidx, func->export_name ? func->export_name : "") for (int p = ((int)type->param_count) - 1; p >= 0; p--) { - dbg_warn("%s%s", value_repr(&m->stack[m->sp - p]), p ? " " : ""); + dbg_warn("%s%s", value_repr(&ectx->stack[ectx->sp - p]), p ? " " : ""); } dbg_warn("), %d locals, %d results\n", func->local_count, type->result_count); #endif // Push locals (dropping extras) - m->fp = m->sp - ((int)type->param_count) + 1; + ectx->fp = ectx->sp - ((int)type->param_count) + 1; // TODO: validate arguments vs formal params // Push function locals for (uint32_t lidx = 0; lidx < func->local_count; lidx++) { - m->sp += 1; + ectx->sp += 1; #if DEBUG || TRACE || WARN || INFO - if (m->sp >= STACK_SIZE) { - FATAL("WebAssembly stack overflow m->sp = %d, STACK_SIZE = %d\n", - m->sp, STACK_SIZE); + if (ectx->sp >= STACK_SIZE) { + FATAL("WebAssembly stack overflow ectx->sp = %d, STACK_SIZE = %d\n", + ectx->sp, STACK_SIZE); } #endif - memset(&m->stack[m->sp], 0, sizeof(StackValue)); - m->stack[m->sp].value_type = func->local_value_type[lidx]; + memset(&ectx->stack[ectx->sp], 0, sizeof(StackValue)); + ectx->stack[ectx->sp].value_type = func->local_value_type[lidx]; } // Set program counter to start of function - m->pc_ptr = func->start_ptr; + ectx->pc_ptr = func->start_ptr; + + // Store the module + ectx->callstack[ectx->csp].module = m; } uint32_t LOAD_SIZE[] = {4, 8, 4, 8, 1, 1, 2, 2, 1, 1, 2, 2, 4, 4}; @@ -167,26 +183,28 @@ bool Interpreter::load(Module *m, uint8_t type, uint32_t addr, } } - m->stack[++m->sp].value.uint64 = 0; // initialize to 0 + ExecutionContext *ectx = m->warduino->execution_context; - memcpy(&m->stack[m->sp].value, maddr, size); - m->stack[m->sp].value_type = LOAD_TYPES[abs(type - I32)]; + ectx->stack[++ectx->sp].value.uint64 = 0; // initialize to 0 + + memcpy(&ectx->stack[ectx->sp].value, maddr, size); + ectx->stack[ectx->sp].value_type = LOAD_TYPES[abs(type - I32)]; switch (type) { case I32_8_s: - sext_8_32(&m->stack[m->sp].value.uint32); + sext_8_32(&ectx->stack[ectx->sp].value.uint32); break; case I32_16_s: - sext_16_32(&m->stack[m->sp].value.uint32); + sext_16_32(&ectx->stack[ectx->sp].value.uint32); break; case I64_8_s: - sext_8_64(&m->stack[m->sp].value.uint64); + sext_8_64(&ectx->stack[ectx->sp].value.uint64); break; case I64_16_s: - sext_16_64(&m->stack[m->sp].value.uint64); + sext_16_64(&ectx->stack[ectx->sp].value.uint64); break; case I64_32_s: - sext_32_64(&m->stack[m->sp].value.uint64); + sext_32_64(&ectx->stack[ectx->sp].value.uint64); break; default: break; @@ -195,6 +213,7 @@ bool Interpreter::load(Module *m, uint8_t type, uint32_t addr, } bool Interpreter::interpret(Module *m, bool waiting) { + ExecutionContext *ectx = m->warduino->execution_context; uint8_t *block_ptr; uint8_t opcode; @@ -205,6 +224,8 @@ bool Interpreter::interpret(Module *m, bool waiting) { bool program_done = false; while ((!program_done && success) || waiting) { + m = ectx->current_module; + if (m->warduino->program_state == WARDUINOstep) { m->warduino->debugger->notifyCompleteStep(m); m->warduino->debugger->pauseRuntime(m); @@ -237,11 +258,11 @@ bool Interpreter::interpret(Module *m, bool waiting) { // Program state is not paused // If BP and not the one we just unpaused - if (m->warduino->debugger->isBreakpoint(m->pc_ptr) && - m->warduino->debugger->skipBreakpoint != m->pc_ptr && + if (m->warduino->debugger->isBreakpoint(ectx->pc_ptr) && + m->warduino->debugger->skipBreakpoint != ectx->pc_ptr && m->warduino->program_state != PROXYrun) { m->warduino->debugger->pauseRuntime(m); - m->warduino->debugger->notifyBreakpoint(m, m->pc_ptr); + m->warduino->debugger->notifyBreakpoint(m, ectx->pc_ptr); continue; } m->warduino->debugger->skipBreakpoint = nullptr; @@ -255,16 +276,16 @@ bool Interpreter::interpret(Module *m, bool waiting) { m->warduino->debugger->handleSnapshotPolicy(m); } - opcode = *m->pc_ptr; - block_ptr = m->pc_ptr; - m->pc_ptr += 1; + opcode = *ectx->pc_ptr; + block_ptr = ectx->pc_ptr; + ectx->pc_ptr += 1; dbg_dump_stack(m); - dbg_trace(" PC: %p OPCODE: <%s> in %s\n", block_ptr, - opcode_repr(opcode), - m->pc_ptr > m->bytes && m->pc_ptr < m->bytes + m->byte_count - ? "module" - : "patch"); + dbg_trace( + " PC: %p OPCODE: <%s> in %s\n", block_ptr, opcode_repr(opcode), + ectx->pc_ptr > m->bytes && ectx->pc_ptr < m->bytes + m->byte_count + ? "module" + : "patch"); switch (opcode) { // diff --git a/src/Primitives/arduino.cpp b/src/Primitives/arduino.cpp index cd8b3859..ae37b212 100644 --- a/src/Primitives/arduino.cpp +++ b/src/Primitives/arduino.cpp @@ -182,13 +182,14 @@ int global_index = 0; std::vector &external_state) // TODO: use fp -#define pop_args(n) m->sp -= n -#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value -#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg -#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg -#define pushUInt64(arg) \ - m->stack[++m->sp].value_type = I64; \ - m->stack[m->sp].value.uint64 = arg +#define get_ectx(m) (m->warduino->execution_context) +#define pop_args(n) (get_ectx(m)->sp -= n) +#define get_arg(m, arg) get_ectx(m)->stack[get_ectx(m)->sp - (arg)].value +#define pushUInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.uint32 = arg +#define pushInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.int32 = arg +#define pushUInt64(arg) \ + get_ectx(m)->stack[++get_ectx(m)->sp].value_type = I64; \ + get_ectx(m)->stack[get_ectx(m)->sp].value.uint64 = arg #define arg0 get_arg(m, 0) #define arg1 get_arg(m, 1) #define arg2 get_arg(m, 2) @@ -1005,6 +1006,7 @@ void install_isrs() { } void install_primitives() { + prim_index = 0; dbg_info("INSTALLING PRIMITIVES\n"); install_primitive(abort); install_primitive(millis); @@ -1115,13 +1117,13 @@ bool resolve_external_global(char *symbol, Global **val) { //------------------------------------------------------ void restore_external_state(Module *m, const std::vector &external_state) { - uint8_t opcode = *m->pc_ptr; + uint8_t opcode = *get_ectx(m)->pc_ptr; // TODO: Maybe primitives can also be called using the other call // instructions such as call_indirect // maybe there should just be a function that checks if a certain function // is being called that handles all these cases? if (opcode == 0x10) { // call opcode - uint8_t *pc_copy = m->pc_ptr + 1; + uint8_t *pc_copy = get_ectx(m)->pc_ptr + 1; uint32_t fidx = read_LEB_32(&pc_copy); if (fidx < m->import_count) { for (auto &primitive : primitives) { diff --git a/src/Primitives/emulated.cpp b/src/Primitives/emulated.cpp index fac101e1..382e3629 100644 --- a/src/Primitives/emulated.cpp +++ b/src/Primitives/emulated.cpp @@ -80,13 +80,14 @@ double sensor_emu = 0; std::vector &external_state) // TODO: use fp -#define pop_args(n) m->sp -= n -#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value -#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg -#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg -#define pushUInt64(arg) \ - m->stack[++m->sp].value_type = I64; \ - m->stack[m->sp].value.uint64 = arg +#define get_ectx(m) (m->warduino->execution_context) +#define pop_args(n) (get_ectx(m)->sp -= n) +#define get_arg(m, arg) get_ectx(m)->stack[get_ectx(m)->sp - (arg)].value +#define pushUInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.uint32 = arg +#define pushInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.int32 = arg +#define pushUInt64(arg) \ + get_ectx(m)->stack[++get_ectx(m)->sp].value_type = I64; \ + get_ectx(m)->stack[get_ectx(m)->sp].value.uint64 = arg #define arg0 get_arg(m, 0) #define arg1 get_arg(m, 1) #define arg2 get_arg(m, 2) @@ -656,6 +657,7 @@ def_prim(chip_ledc_attach_pin, twoToNoneU32) { // Installing all the primitives //------------------------------------------------------ void install_primitives() { + prim_index = 0; dbg_info("INSTALLING PRIMITIVES\n"); dbg_info("INSTALLING FAKE ARDUINO\n"); install_primitive(abort); diff --git a/src/Primitives/idf.cpp b/src/Primitives/idf.cpp index f42280ad..44c780b6 100644 --- a/src/Primitives/idf.cpp +++ b/src/Primitives/idf.cpp @@ -80,13 +80,14 @@ double sensor_emu = 0; std::vector &external_state) // TODO: use fp -#define pop_args(n) m->sp -= n -#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value -#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg -#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg -#define pushUInt64(arg) \ - m->stack[++m->sp].value_type = I64; \ - m->stack[m->sp].value.uint64 = arg +#define get_ectx(m) (m->warduino->execution_context) +#define pop_args(n) (get_ectx(m)->sp -= n) +#define get_arg(m, arg) get_ectx(m)->stack[get_ectx(m)->sp - (arg)].value +#define pushUInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.uint32 = arg +#define pushInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.int32 = arg +#define pushUInt64(arg) \ + get_ectx(m)->stack[++get_ectx(m)->sp].value_type = I64; \ + get_ectx(m)->stack[get_ectx(m)->sp].value.uint64 = arg #define arg0 get_arg(m, 0) #define arg1 get_arg(m, 1) #define arg2 get_arg(m, 2) @@ -271,6 +272,7 @@ def_prim(chip_digital_read, oneToOneU32) { // Installing all the primitives //------------------------------------------------------ void install_primitives() { + prim_index = 0; dbg_info("INSTALLING PRIMITIVES\n"); install_primitive(chip_delay); install_primitive(chip_pin_mode); @@ -334,13 +336,13 @@ bool resolve_external_global(char *symbol, Global **val) { //------------------------------------------------------ void restore_external_state(Module *m, const std::vector &external_state) { - uint8_t opcode = *m->pc_ptr; + uint8_t opcode = *(get_ectx(m)->pc_ptr); // TODO: Maybe primitives can also be called using the other call // instructions such as call_indirect // maybe there should just be a function that checks if a certain function // is being called that handles all these cases? if (opcode == 0x10) { // call opcode - uint8_t *pc_copy = m->pc_ptr + 1; + uint8_t *pc_copy = get_ectx(m)->pc_ptr + 1; uint32_t fidx = read_LEB_32(&pc_copy); if (fidx < m->import_count) { for (auto &primitive : primitives) { diff --git a/src/Primitives/primitives.h b/src/Primitives/primitives.h index 11138798..d68c1295 100644 --- a/src/Primitives/primitives.h +++ b/src/Primitives/primitives.h @@ -77,8 +77,9 @@ void invoke_primitive(Module *m, const std::string &function_name, Ts... args) { std::vector argStack; create_stack(&argStack, args...); + ExecutionContext *ectx = m->warduino->execution_context; for (auto arg : argStack) { - m->stack[++m->sp] = arg; + ectx->stack[++ectx->sp] = arg; } primitive(m); } diff --git a/src/Primitives/zephyr.cpp b/src/Primitives/zephyr.cpp index a6c42ee0..704467f1 100644 --- a/src/Primitives/zephyr.cpp +++ b/src/Primitives/zephyr.cpp @@ -87,13 +87,14 @@ double sensor_emu = 0; std::vector &external_state) // TODO: use fp -#define pop_args(n) m->sp -= n -#define get_arg(m, arg) m->stack[(m)->sp - (arg)].value -#define pushUInt32(arg) m->stack[++m->sp].value.uint32 = arg -#define pushInt32(arg) m->stack[++m->sp].value.int32 = arg -#define pushUInt64(arg) \ - m->stack[++m->sp].value_type = I64; \ - m->stack[m->sp].value.uint64 = arg +#define get_ectx(m) (m->warduino->execution_context) +#define pop_args(n) (get_ectx(m)->sp -= n) +#define get_arg(m, arg) get_ectx(m)->stack[get_ectx(m)->sp - (arg)].value +#define pushUInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.uint32 = arg +#define pushInt32(arg) get_ectx(m)->stack[++get_ectx(m)->sp].value.int32 = arg +#define pushUInt64(arg) \ + get_ectx(m)->stack[++get_ectx(m)->sp].value_type = I64; \ + get_ectx(m)->stack[get_ectx(m)->sp].value.uint64 = arg #define arg0 get_arg(m, 0) #define arg1 get_arg(m, 1) #define arg2 get_arg(m, 2) @@ -326,7 +327,7 @@ def_prim(chip_digital_read, oneToOneU32) { def_prim(print_string, twoToNoneU32) { uint32_t addr = arg1.uint32; uint32_t size = arg0.uint32; - std::string text = parse_utf8_string(m->memory.bytes, size, addr); + std::string text = parse_utf8_string(get_ectx(m)->memory.bytes, size, addr); debug("EMU: print string at %i: ", addr); printf("%s", text.c_str()); pop_args(2); @@ -582,6 +583,7 @@ def_prim(ev3_touch_sensor, oneToOneU32) { // Installing all the primitives //------------------------------------------------------ void install_primitives() { + prim_index = 0; dbg_info("INSTALLING PRIMITIVES\n"); install_primitive(chip_delay); install_primitive(chip_pin_mode); diff --git a/src/WARDuino.h b/src/WARDuino.h index 57e18a25..715cf64f 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -94,14 +94,30 @@ class WARDuino { Debugger *debugger; Interpreter *interpreter; RunningState program_state = WARDUINOrun; + ExecutionContext *execution_context = nullptr; + ~WARDuino(); static WARDuino *instance(); + static void shutdown(); void setInterpreter(Interpreter *interpreter); + Module *get_current_module() { + return execution_context ? execution_context->current_module : nullptr; + } + + void switch_to_module(Module *m) { + if (execution_context) { + execution_context->current_module = m; + } + } + int run_module(Module *m); - Module *load_module(uint8_t *bytes, uint32_t byte_count, Options options); + Module *load_module(uint8_t *bytes, uint32_t byte_count, + const char *module_name, Options options); + + Module *get_module(const char *name); void unload_module(Module *m); @@ -119,4 +135,8 @@ class WARDuino { void instantiate_module(Module *m, uint8_t *bytes, uint32_t byte_count); void free_module_state(Module *m); + + void init_execution_context(); + + void free_execution_context(); }; diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index 934008e6..089fe91f 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -21,7 +21,9 @@ void push_guard(Module *m) { guard->import_field = nullptr; guard->import_module = nullptr; guard->func_ptr = nullptr; - WARDuino::instance()->interpreter->push_block(m, guard, m->sp); + + ExecutionContext *ectx = m->warduino->execution_context; + WARDuino::instance()->interpreter->push_block(m, guard, ectx->sp); } // CallbackHandler class @@ -210,16 +212,17 @@ void Callback::resolve_event(const Event &e) { } // Push arguments (5 args) - module->stack[++module->sp].value.uint32 = start - topic.length(); - module->stack[module->sp].value_type = I32; - module->stack[++module->sp].value.uint32 = topic.length(); - module->stack[module->sp].value_type = I32; - module->stack[++module->sp].value.uint32 = start; - module->stack[module->sp].value_type = I32; - module->stack[++module->sp].value.uint32 = payload.length(); - module->stack[module->sp].value_type = I32; - module->stack[++module->sp].value.uint32 = payload.length(); - module->stack[module->sp].value_type = I32; + ExecutionContext *ectx = module->warduino->execution_context; + ectx->stack[++ectx->sp].value.uint32 = start - topic.length(); + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[++ectx->sp].value.uint32 = topic.length(); + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[++ectx->sp].value.uint32 = start; + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[++ectx->sp].value.uint32 = payload.length(); + ectx->stack[ectx->sp].value_type = I32; + ectx->stack[++ectx->sp].value.uint32 = payload.length(); + ectx->stack[ectx->sp].value_type = I32; // Setup function uint32_t fidx = module->table.entries[table_index]; diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 5d4ddc39..0026782d 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -256,6 +256,7 @@ void find_blocks(Module *m) { void WARDuino::run_init_expr(Module *m, uint8_t type, uint8_t **pc) { // Run the init_expr + ExecutionContext *ectx = execution_context; RunningState current = instance()->program_state; WARDuino::instance()->program_state = WARDUINOinit; Block block; @@ -263,17 +264,17 @@ void WARDuino::run_init_expr(Module *m, uint8_t type, uint8_t **pc) { block.type = get_block_type(type); block.start_ptr = *pc; - m->pc_ptr = *pc; - interpreter->push_block(m, &block, m->sp); + ectx->pc_ptr = *pc; + interpreter->push_block(m, &block, ectx->sp); // WARNING: running code here to get initial value! - dbg_info(" running init_expr at 0x%p: %s\n", m->pc_ptr, + dbg_info(" running init_expr at 0x%p: %s\n", ectx->pc_ptr, block_repr(&block)); interpreter->interpret(m); - *pc = m->pc_ptr; + *pc = ectx->pc_ptr; - ASSERT(m->stack[m->sp].value_type == type, - "init_expr type mismatch 0x%x != 0x%x", m->stack[m->sp].value_type, - type); + ASSERT(ectx->stack[ectx->sp].value_type == type, + "init_expr type mismatch 0x%x != 0x%x", + ectx->stack[ectx->sp].value_type, type); WARDuino::instance()->program_state = current; } @@ -313,17 +314,6 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, uint32_t word; uint8_t valueType; - // Allocate stacks - m->stack = (StackValue *)acalloc(STACK_SIZE, sizeof(StackValue), "Stack"); - m->callstack = (Frame *)acalloc(CALLSTACK_SIZE, sizeof(Frame), "Callstack"); - m->br_table = - (uint32_t *)acalloc(BR_TABLE_SIZE, sizeof(uint32_t), "Branch table"); - - // Empty stacks - m->sp = -1; - m->fp = -1; - m->csp = -1; - m->bytes = bytes; m->byte_count = byte_count; // run constructor with already allocated memory @@ -344,6 +334,8 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, RunningState oldState = this->program_state; this->program_state = WARDUINOrun; + execution_context->current_module = m; + while (pos < bytes_end) { uint32_t id = read_LEB(&pos, 7); uint32_t section_len = read_LEB_32(&pos); @@ -420,7 +412,7 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, import_field); uint32_t type_index = 0, fidx; - uint8_t content_type = 0, mutability; + uint8_t content_type = 0, mutability = 0; switch ( external_kind) { // NOLINT(hicpp-multiway-paths-covered) @@ -435,9 +427,7 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, break; case 0x03: // Global content_type = read_LEB(&pos, 7); - // TODO: use mutability mutability = read_LEB(&pos, 1); - (void)mutability; break; } @@ -445,50 +435,82 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, char *err, *sym = (char *)malloc(module_len + field_len + 5); - // TODO add special case form primitives with resolvePrim - do { - // Try using module as handle filename - if (resolvesym(import_module, import_field, - external_kind, &val, &err)) { - break; - } + if (strcmp(import_module, "env") == 0) { + // TODO add special case form primitives with + // resolvePrim + do { + // Try using module as handle filename + if (resolvesym(import_module, import_field, + external_kind, &val, &err)) { + break; + } - // Try concatenating module and field using underscores - // Also, replace '-' with '_' - sprintf(sym, "_%s__%s_", import_module, import_field); - int sidx = -1; - while (sym[++sidx]) { - if (sym[sidx] == '-') { - sym[sidx] = '_'; + // Try concatenating module and field using + // underscores Also, replace '-' with '_' + sprintf(sym, "_%s__%s_", import_module, + import_field); + int sidx = -1; + while (sym[++sidx]) { + if (sym[sidx] == '-') { + sym[sidx] = '_'; + } + } + if (resolvesym(nullptr, sym, external_kind, &val, + &err)) { + break; } - } - if (resolvesym(nullptr, sym, external_kind, &val, - &err)) { - break; - } - // If enabled, try without the leading underscore (added - // by emscripten for external symbols) - if (m->options.dlsym_trim_underscore && - (strncmp("env", import_module, 4) == 0) && - (strncmp("_", import_field, 1) == 0)) { - sprintf(sym, "%s", import_field + 1); + // If enabled, try without the leading underscore + // (added by emscripten for external symbols) + if (m->options.dlsym_trim_underscore && + (strncmp("env", import_module, 4) == 0) && + (strncmp("_", import_field, 1) == 0)) { + sprintf(sym, "%s", import_field + 1); + if (resolvesym(nullptr, sym, external_kind, + &val, &err)) { + break; + } + } + + // Try the plain symbol by itself with module + // name/handle + sprintf(sym, "%s", import_field); if (resolvesym(nullptr, sym, external_kind, &val, &err)) { break; } - } - // Try the plain symbol by itself with module - // name/handle - sprintf(sym, "%s", import_field); - if (resolvesym(nullptr, sym, external_kind, &val, - &err)) { - break; + FATAL("Error: %s\n", err); + } while (false); + } else { + Module *target_mod = this->get_module(import_module); + if (target_mod) { + switch (external_kind) { + case 0x00: // Function + { + uint32_t tfidx = get_export_fidx( + target_mod, import_field); + if (tfidx != (uint32_t)-1) { + val = + (void *)target_mod->functions[tfidx] + .func_ptr; + } + } break; + case 0x01: // Table (Shared Table) + val = (void *)&target_mod->table; + break; + case 0x02: // Memory (Shared Memory) + val = (void *)&target_mod->memory; + break; + case 0x03: // Global + // Find global export not fully implemented + // in get_export_fidx helper This would + // require iterating target_mod exports + // looking for globals + break; + } } - - FATAL("Error: %s\n", err); - } while (false); + } debug(" found '%s.%s' as symbol '%s' at address %p\n", import_module, import_field, sym, val); @@ -520,34 +542,54 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, } case 0x01: // Table { - ASSERT(!m->table.entries, - "More than 1 table not supported\n"); - auto *tval = (Table *)val; - m->table.entries = (uint32_t *)val; - ASSERT(m->table.initial <= tval->maximum, - "Imported table is not large enough\n"); - dbg_warn(" setting table.entries to: %p\n", - *(uint32_t **)val); - m->table.entries = *(uint32_t **)val; - m->table.size = tval->size; - m->table.maximum = tval->maximum; - m->table.entries = tval->entries; + m->table.imported = true; + if (strcmp(import_module, "env") != 0) { + Table *shared_table = (Table *)val; + m->table.entries = shared_table->entries; + m->table.size = shared_table->size; + m->table.maximum = shared_table->maximum; + m->table.owner = shared_table->owner; + // We share the underlying array + } else { + ASSERT(!m->table.entries, + "More than 1 table not supported\n"); + auto *tval = (Table *)val; + m->table.entries = (uint32_t *)val; + ASSERT(m->table.initial <= tval->maximum, + "Imported table is not large enough\n"); + dbg_warn(" setting table.entries to: %p\n", + *(uint32_t **)val); + m->table.entries = *(uint32_t **)val; + m->table.size = tval->size; + m->table.maximum = tval->maximum; + m->table.entries = tval->entries; + }; break; } case 0x02: // Memory { - ASSERT(!m->memory.bytes, - "More than 1 memory not supported\n"); - auto *mval = (Memory *)val; - ASSERT(m->memory.initial <= mval->maximum, - "Imported memory is not large enough\n"); - dbg_warn( - " setting memory pages: %d, max: %d, bytes: " - "%p\n", - mval->pages, mval->maximum, mval->bytes); - m->memory.pages = mval->pages; - m->memory.maximum = mval->maximum; - m->memory.bytes = mval->bytes; + m->memory.imported = true; + if (strcmp(import_module, "env") != 0) { + Memory *shared_mem = (Memory *)val; + m->memory.bytes = shared_mem->bytes; + m->memory.pages = shared_mem->pages; + m->memory.maximum = shared_mem->maximum; + // We share the underlying byte array + } else { + ASSERT(!m->memory.bytes, + "More than 1 memory not supported\n"); + auto *mval = (Memory *)val; + ASSERT(m->memory.initial <= mval->maximum, + "Imported memory is not large enough\n"); + dbg_warn( + " setting memory pages: %d, max: %d, " + "bytes: " + "%p\n", + mval->pages, mval->maximum, mval->bytes); + m->memory.pages = mval->pages; + m->memory.maximum = mval->maximum; + m->memory.bytes = mval->bytes; + }; break; } case 0x03: // Global @@ -613,6 +655,7 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, m->options.mangle_table_index = false; m->table.entries = (uint32_t *)acalloc( m->table.size, sizeof(uint32_t), "Module->table.entries"); + m->table.owner = m; //} break; } @@ -651,7 +694,8 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, // Run the init_expr to get global value run_init_expr(m, type, &pos); - *(m->globals[gidx]->value) = m->stack[m->sp--]; + *(m->globals[gidx]->value) = + execution_context->stack[execution_context->sp--]; m->globals[gidx]->mutability = mutability; } pos = start_pos + section_len; @@ -708,7 +752,9 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, // Run the init_expr to get offset run_init_expr(m, I32, &pos); - uint32_t offset = m->stack[m->sp--].value.uint32; + uint32_t offset = + execution_context->stack[execution_context->sp--] + .value.uint32; if (m->options.mangle_table_index) { // offset is the table address + the index (not sized @@ -757,7 +803,9 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, // Run the init_expr to get the offset run_init_expr(m, I32, &pos); - uint32_t offset = m->stack[m->sp--].value.uint32; + uint32_t offset = + execution_context->stack[execution_context->sp--] + .value.uint32; // Copy the data to the memory offset uint32_t size = read_LEB_32(&pos); @@ -855,7 +903,7 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, interpreter->setup_call(m, fidx); // regular function call } - if (m->csp < 0) { + if (execution_context->csp < 0) { // start function was a direct external call result = true; } else { @@ -871,14 +919,27 @@ void WARDuino::instantiate_module(Module *m, uint8_t *bytes, this->program_state = oldState; } +Module *WARDuino::get_module(const char *name) { + if (name == nullptr) return nullptr; + for (Module *m : this->modules) { + if (m->name != nullptr && strcmp(m->name, name) == 0) { + return m; + } + } + return nullptr; +} + Module *WARDuino::load_module(uint8_t *bytes, uint32_t byte_count, - Options options) { + const char *module_name, Options options) { debug("Loading module of size %d \n", byte_count); Module *m; // Allocate the module m = (Module *)acalloc(1, sizeof(Module), "Module"); m->warduino = this; m->options = options; + if (module_name) { + m->name = strdup(module_name); + } this->instantiate_module(m, bytes, byte_count); @@ -902,20 +963,88 @@ void WARDuino::unload_module(Module *m) { WARDuino::WARDuino() { this->debugger = new Debugger(0); this->interpreter = new Interpreter(); + this->init_execution_context(); install_primitives(); initTypes(); } +WARDuino::~WARDuino() { + for (Module *m : this->modules) { + this->free_module_state(m); + free(m); + } + this->modules.clear(); + + this->free_execution_context(); +} + +void WARDuino::shutdown() { + if (singleton != nullptr) { + delete singleton; + singleton = nullptr; + } +} + +void WARDuino::init_execution_context() { + if (execution_context != nullptr) { + return; + } + + execution_context = (ExecutionContext *)acalloc(1, sizeof(ExecutionContext), + "ExecutionContext"); + + execution_context->stack = + (StackValue *)acalloc(STACK_SIZE, sizeof(StackValue), "Shared Stack"); + execution_context->callstack = + (Frame *)acalloc(CALLSTACK_SIZE, sizeof(Frame), "Shared Callstack"); + execution_context->br_table = (uint32_t *)acalloc( + BR_TABLE_SIZE, sizeof(uint32_t), "Shared Branch table"); + + execution_context->sp = -1; + execution_context->fp = -1; + execution_context->csp = -1; + execution_context->pc_ptr = nullptr; + execution_context->current_module = nullptr; +} + +void WARDuino::free_execution_context() { + if (execution_context == nullptr) { + return; + } + + if (execution_context->stack != nullptr) { + free(execution_context->stack); + } + if (execution_context->callstack != nullptr) { + for (int j = 0; j <= execution_context->csp; j++) { + Frame *f = &execution_context->callstack[j]; + if (f->block != nullptr && (f->block->block_type == 0xfe || + f->block->block_type == 0xff)) { + free(f->block); + } + } + free(execution_context->callstack); + } + if (execution_context->br_table != nullptr) { + free(execution_context->br_table); + } + + free(execution_context); + execution_context = nullptr; +} + // Return value of false means exception occurred bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, StackValue *args) { + ExecutionContext *ectx = m->warduino->execution_context; bool result; - m->sp = -1; - m->fp = -1; - m->csp = -1; + ectx->sp = -1; + ectx->fp = -1; + ectx->csp = -1; + ectx->current_module = m; for (uint32_t i = 0; i < arity; ++i) { - m->stack[++m->sp] = *args; + ectx->stack[++ectx->sp] = *args; args++; } @@ -934,12 +1063,15 @@ void WARDuino::setInterpreter(Interpreter *interpreter) { } int WARDuino::run_module(Module *m) { + ExecutionContext *ectx = m->warduino->execution_context; uint32_t fidx = this->get_main_fidx(m); + ectx->current_module = m; + // execute main if (fidx != UNDEF) { this->invoke(m, fidx); - return m->stack->value.uint32; + return ectx->stack->value.uint32; } // wait @@ -964,6 +1096,11 @@ WARDuino *WARDuino::instance() { // Removes all the state of a module void WARDuino::free_module_state(Module *m) { + if (m->name != nullptr) { + free(m->name); + m->name = nullptr; + } + if (m->types != nullptr) { for (uint32_t i = 0; i < m->type_count; i++) { free(m->types[i].params); @@ -975,7 +1112,8 @@ void WARDuino::free_module_state(Module *m) { if (m->functions != nullptr) { for (uint32_t i = 0; i < m->function_count; ++i) - free(m->functions[i].export_name); + if (m->functions[i].export_name != nullptr) + free(m->functions[i].export_name); free(m->functions); m->functions = nullptr; } @@ -985,36 +1123,25 @@ void WARDuino::free_module_state(Module *m) { m->globals = nullptr; } - if (m->table.entries != nullptr) { - free(m->table.entries); - m->table.entries = nullptr; - } - - if (m->memory.bytes != nullptr) { - free(m->memory.bytes); - m->memory.bytes = nullptr; - } - - if (m->stack != nullptr) { - free(m->stack); - m->stack = nullptr; - } - - if (m->callstack != nullptr) { - for (int j = 0; j <= m->csp; j++) { - Frame *f = &m->callstack[j]; - if (f->block != nullptr && (f->block->block_type == 0xfe || - f->block->block_type == 0xff)) { - free(f->block); - } + if (!m->table.imported) { + if (m->table.entries != nullptr) { + free(m->table.entries); + m->table.entries = nullptr; } - free(m->callstack); - m->callstack = nullptr; + m->table.elem_type = 0; + m->table.initial = 0; + m->table.maximum = 0; + m->table.size = 0; } - if (m->br_table != nullptr) { - free(m->br_table); - m->br_table = nullptr; + if (!m->memory.imported) { + if (m->memory.bytes != nullptr) { + free(m->memory.bytes); + m->memory.bytes = nullptr; + } + m->memory.pages = 0; + m->memory.initial = 0; + m->memory.maximum = 0; } m->function_count = 0; @@ -1023,23 +1150,11 @@ void WARDuino::free_module_state(Module *m) { m->import_count = 0; m->global_count = 0; - m->pc_ptr = 0; - m->sp = -1; - m->fp = -1; - m->csp = -1; if (m->exception != nullptr) { free(m->exception); // safe to remove? } - m->memory.pages = 0; - m->memory.initial = 0; - m->memory.maximum = 0; - m->table.elem_type = 0; - m->table.initial = 0; - m->table.maximum = 0; - m->table.size = 0; - m->block_lookup.clear(); } @@ -1047,6 +1162,13 @@ void WARDuino::update_module(Module *m, uint8_t *wasm, uint32_t wasm_len) { m->warduino->program_state = WARDUINOinit; this->free_module_state(m); + + ExecutionContext *ectx = m->warduino->execution_context; + ectx->sp = -1; + ectx->fp = -1; + ectx->csp = -1; + ectx->current_module = m; + this->instantiate_module(m, wasm, wasm_len); uint32_t fidx = this->get_main_fidx(m); diff --git a/src/WARDuino/internals.h b/src/WARDuino/internals.h index b099df57..b2bfd025 100644 --- a/src/WARDuino/internals.h +++ b/src/WARDuino/internals.h @@ -63,6 +63,7 @@ typedef struct Frame { int sp; int fp; uint8_t *ra_ptr; + struct Module *module; } Frame; /// @@ -73,6 +74,8 @@ typedef struct Table { uint32_t maximum = 0; // maximum table size uint32_t size = 0; // current table size uint32_t *entries = nullptr; + bool imported = false; // whether the table is imported/shared + struct Module *owner = nullptr; // module that owns the table } Table; typedef struct Memory { @@ -80,6 +83,7 @@ typedef struct Memory { uint32_t maximum = 0; // maximum size (64K pages) uint32_t pages = 0; // current size (64K pages) uint8_t *bytes = nullptr; // memory area + bool imported = false; // whether the memory is imported/shared } Memory; typedef struct Global { @@ -105,8 +109,21 @@ typedef struct Options { class WARDuino; // predeclare for it work in the module decl +typedef struct ExecutionContext { + uint8_t *pc_ptr = nullptr; + int sp = -1; + int fp = -1; + StackValue *stack = nullptr; + int csp = -1; + Frame *callstack = nullptr; + uint32_t *br_table = nullptr; + + struct Module *current_module = nullptr; +} ExecutionContext; + typedef struct Module { WARDuino *warduino = nullptr; + char *name = nullptr; char *path = nullptr; // file path of the wasm module Options options; // Config options @@ -127,14 +144,6 @@ typedef struct Module { Memory memory; uint32_t global_count = 0; // number of globals Global **globals = nullptr; // globals - // Runtime state - uint8_t *pc_ptr = nullptr; // program counter - int sp = -1; // operand stack pointer - int fp = -1; // current frame pointer into stack - StackValue *stack = nullptr; // main operand stack - int csp = -1; // callstack pointer - Frame *callstack = nullptr; // callstack - uint32_t *br_table = nullptr; // br_table branch indexes char *exception = nullptr; // exception is set when the program fails } Module; diff --git a/tests/compilation/esp32/esp32.ino b/tests/compilation/esp32/esp32.ino index c3d4c0b7..cd94cb89 100644 --- a/tests/compilation/esp32/esp32.ino +++ b/tests/compilation/esp32/esp32.ino @@ -60,7 +60,7 @@ void setup(void) { void loop() { disableCore0WDT(); - m = wac->load_module(wasm, wasm_len, {}); + m = wac->load_module(wasm, wasm_len, "main", {}); printf("LOADED \n\n"); uint8_t command[] = {'0', '3', '\n'}; diff --git a/tests/unit/addresconversions_test.cpp b/tests/unit/addresconversions_test.cpp index 8abc3cfd..76c98806 100644 --- a/tests/unit/addresconversions_test.cpp +++ b/tests/unit/addresconversions_test.cpp @@ -25,7 +25,7 @@ class ConversionFixture : public ::testing::Test { void TearDown() override { wasm_module->warduino = nullptr; - warduino->free_module_state(wasm_module); + warduino->shutdown(); this->wasm_module = nullptr; delete wasm_module; } diff --git a/tests/unit/freeingmodule_test.cpp b/tests/unit/freeingmodule_test.cpp index 1909f5a3..155422f8 100644 --- a/tests/unit/freeingmodule_test.cpp +++ b/tests/unit/freeingmodule_test.cpp @@ -31,20 +31,20 @@ class FreeingModuleFixture : public ::testing::Test { }; TEST_F(FreeingModuleFixture, FreeingModuleStateEmptiesModule) { + warduino->init_execution_context(); warduino->instantiate_module(wasm_module, dimmer_wasm, dimmer_wasm_len); warduino->free_module_state(wasm_module); - + warduino->free_execution_context(); EXPECT_EQ(wasm_module->types, nullptr); EXPECT_EQ(wasm_module->functions, nullptr); EXPECT_EQ(wasm_module->globals, nullptr); EXPECT_EQ(wasm_module->table.entries, nullptr); EXPECT_EQ(wasm_module->memory.bytes, nullptr); - EXPECT_EQ(wasm_module->stack, nullptr); - EXPECT_EQ(wasm_module->callstack, nullptr); - EXPECT_EQ(wasm_module->br_table, nullptr); + EXPECT_EQ(wasm_module->warduino->execution_context, nullptr); } TEST_F(FreeingModuleFixture, FreeingStatePreservesOptions) { + warduino->init_execution_context(); warduino->instantiate_module(wasm_module, blink_wasm, blink_wasm_len); warduino->free_module_state(wasm_module); Options opts2 = wasm_module->options; diff --git a/tests/unit/instantiatemodule_test.cpp b/tests/unit/instantiatemodule_test.cpp index b502a066..ccd5b0e5 100644 --- a/tests/unit/instantiatemodule_test.cpp +++ b/tests/unit/instantiatemodule_test.cpp @@ -39,9 +39,9 @@ TEST_F(InstantiateModuleFixture, InitiallyEmpty) { EXPECT_EQ(wasm_module->globals, nullptr); EXPECT_EQ(wasm_module->table.entries, nullptr); EXPECT_EQ(wasm_module->memory.bytes, nullptr); - EXPECT_EQ(wasm_module->stack, nullptr); - EXPECT_EQ(wasm_module->callstack, nullptr); - EXPECT_EQ(wasm_module->br_table, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->stack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->callstack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->br_table, nullptr); } TEST_F(InstantiateModuleFixture, FacLoadsWithoutTableGlobalsAndMemory) { @@ -49,9 +49,9 @@ TEST_F(InstantiateModuleFixture, FacLoadsWithoutTableGlobalsAndMemory) { ASSERT_NE(wasm_module, nullptr); EXPECT_NE(wasm_module->types, nullptr); EXPECT_NE(wasm_module->functions, nullptr); - EXPECT_NE(wasm_module->stack, nullptr); - EXPECT_NE(wasm_module->callstack, nullptr); - EXPECT_NE(wasm_module->br_table, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->stack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->callstack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->br_table, nullptr); // memory, table, and globals are not used in fac.wast EXPECT_EQ(wasm_module->memory.bytes, nullptr); @@ -63,9 +63,9 @@ TEST_F(InstantiateModuleFixture, BlinkLoadsWithoutTableAndMemory) { warduino->instantiate_module(wasm_module, blink_wasm, blink_wasm_len); EXPECT_NE(wasm_module->types, nullptr); EXPECT_NE(wasm_module->functions, nullptr); - EXPECT_NE(wasm_module->stack, nullptr); - EXPECT_NE(wasm_module->callstack, nullptr); - EXPECT_NE(wasm_module->br_table, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->stack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->callstack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->br_table, nullptr); EXPECT_NE(wasm_module->globals, nullptr); // not used in blink.wast @@ -77,9 +77,9 @@ TEST_F(InstantiateModuleFixture, FacLoadsWithoutTableMemoryAndGlobals) { warduino->instantiate_module(wasm_module, fac_wasm, fac_wasm_len); EXPECT_NE(wasm_module->types, nullptr); EXPECT_NE(wasm_module->functions, nullptr); - EXPECT_NE(wasm_module->stack, nullptr); - EXPECT_NE(wasm_module->callstack, nullptr); - EXPECT_NE(wasm_module->br_table, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->stack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->callstack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->br_table, nullptr); // not used in fac.wast EXPECT_EQ(wasm_module->memory.bytes, nullptr); @@ -92,9 +92,9 @@ TEST_F(InstantiateModuleFixture, DimmerLoadsWithTableMemoryAndGlobals) { ASSERT_NE(wasm_module, nullptr); EXPECT_NE(wasm_module->types, nullptr); EXPECT_NE(wasm_module->functions, nullptr); - EXPECT_NE(wasm_module->stack, nullptr); - EXPECT_NE(wasm_module->callstack, nullptr); - EXPECT_NE(wasm_module->br_table, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->stack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->callstack, nullptr); + EXPECT_NE(wasm_module->warduino->execution_context->br_table, nullptr); EXPECT_NE(wasm_module->globals, nullptr); EXPECT_NE(wasm_module->memory.bytes, nullptr); EXPECT_NE(wasm_module->table.entries, nullptr); diff --git a/tests/unit/shared/callstackbuilder.cpp b/tests/unit/shared/callstackbuilder.cpp index 4528b983..a1815c61 100644 --- a/tests/unit/shared/callstackbuilder.cpp +++ b/tests/unit/shared/callstackbuilder.cpp @@ -5,33 +5,35 @@ CallstackBuilder::CallstackBuilder(Module* wasm_module) : m{wasm_module} {} void CallstackBuilder::pushBlock(Block* block, int sp) { // copy of push_block instructions.cpp - m->csp += 1; - m->callstack[m->csp].block = block; - m->callstack[m->csp].sp = sp; - m->callstack[m->csp].fp = m->fp; - m->callstack[m->csp].ra_ptr = m->pc_ptr; + ExecutionContext* ectx = m->warduino->execution_context; + ectx->csp += 1; + ectx->callstack[ectx->csp].block = block; + ectx->callstack[ectx->csp].sp = sp; + ectx->callstack[ectx->csp].fp = ectx->fp; + ectx->callstack[ectx->csp].ra_ptr = ectx->pc_ptr; } void CallstackBuilder::pushFunctionCall(uint32_t fidx) { + ExecutionContext* ectx = m->warduino->execution_context; // copy of setup_call from instructions.cpp Block* func = &m->functions[fidx]; Type* type = func->type; // Push current frame on the call stack - this->pushBlock(func, m->sp - type->param_count); + this->pushBlock(func, ectx->sp - type->param_count); // Push locals (dropping extras) - m->fp = m->sp - ((int)type->param_count) + 1; + ectx->fp = ectx->sp - ((int)type->param_count) + 1; // Push function locals for (uint32_t lidx = 0; lidx < func->local_count; lidx++) { - m->sp += 1; - m->stack[m->sp].value_type = func->local_value_type[lidx]; - m->stack[m->sp].value.uint64 = 0; // Initialize whole union to 0 + ectx->sp += 1; + ectx->stack[ectx->sp].value_type = func->local_value_type[lidx]; + ectx->stack[ectx->sp].value.uint64 = 0; // Initialize whole union to 0 } // Set program counter to start of function - m->pc_ptr = func->start_ptr; + ectx->pc_ptr = func->start_ptr; } void CallstackBuilder::pushGuard(uint8_t guard_type) { @@ -46,5 +48,5 @@ void CallstackBuilder::pushGuard(uint8_t guard_type) { guard->import_field = nullptr; guard->import_module = nullptr; guard->func_ptr = nullptr; - this->pushBlock(guard, m->sp); + this->pushBlock(guard, m->warduino->execution_context->sp); } \ No newline at end of file