From 6d2afa24ac52d18b35001eba43e5b309e3df3389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alf-Andr=C3=A9=20Walla?= Date: Wed, 15 May 2024 12:05:37 +0200 Subject: [PATCH] Add distinction between initialization-only, client- and server-only dynamic calls Also, make void vmcall returns into a simple boolean --- engine/src/script/event.hpp | 6 ++-- engine/src/script/script.cpp | 55 ++++++++++++++++++++++++++++------- engine/src/script/script.hpp | 5 +++- programs/dynamic_calls.json | 13 +++++++++ programs/dyncalls/generate.py | 32 +++++++++++++++++++- tests/events.cpp | 11 ++++--- 6 files changed, 101 insertions(+), 21 deletions(-) diff --git a/engine/src/script/event.hpp b/engine/src/script/event.hpp index 5e9d1de..f74e1d2 100644 --- a/engine/src/script/event.hpp +++ b/engine/src/script/event.hpp @@ -85,14 +85,16 @@ template inline auto Event::call(Args&&... args) { auto& script = this->script(); if (auto res = script.call(address(), std::forward(args)...)) { - if constexpr (std::is_same_v || std::is_same_v) + if constexpr (std::is_same_v) + return true; + else if constexpr (std::is_same_v) return res; else return std::optional (res.value()); } } if constexpr (std::is_same_v) - return std::optional{std::nullopt}; + return false; else return std::optional{std::nullopt}; } diff --git a/engine/src/script/script.cpp b/engine/src/script/script.cpp index f9066b6..2cb117f 100644 --- a/engine/src/script/script.cpp +++ b/engine/src/script/script.cpp @@ -167,7 +167,8 @@ void Script::machine_setup() this->add_shared_memory(); // Figure out the local indices based on dynamic call table - this->resolve_dynamic_calls(); + // We are pretending to be initializing the client-side + this->resolve_dynamic_calls(true, true, false); } void Script::could_not_find(std::string_view func) @@ -405,7 +406,7 @@ void Script::dynamic_call_error(uint32_t idx, const std::exception& e) throw; } -void Script::resolve_dynamic_calls() +void Script::resolve_dynamic_calls(bool initialization, bool client_side, bool verbose) { this->m_g_dyncall_table = machine().address_of("dyncall_table"); if (m_g_dyncall_table == 0x0) @@ -420,10 +421,7 @@ void Script::resolve_dynamic_calls() // Reserve space for host-side dynamic call handlers this->m_dyncall_array.reserve(entries); this->m_dyncall_array.clear(); - if constexpr (WARN_ON_UNIMPLEMENTED_DYNCALL) { - strf::to(stdout)( - "Resolving dynamic calls for '", name(), "' with ", entries, " entries\n"); - } + unsigned unimplemented = 0; // Copy whole table into vector std::vector table (entries); @@ -431,6 +429,36 @@ void Script::resolve_dynamic_calls() for (unsigned i = 0; i < entries; i++) { auto& entry = table.at(i); + if (entry.initialization_only && !initialization) { + if (verbose) strf::to(stdout)( + "Skipping initialization-only dynamic call '", + this->machine().memory.memstring(entry.strname), "'\n"); + this->m_dyncall_array.push_back( + [] (auto&) { + throw std::runtime_error("Initialization-only dynamic call triggered"); + }); + continue; + } + if (entry.client_side_only && !client_side) { + if (verbose) strf::to(stdout)( + "Skipping client-side-only dynamic call '", + machine().memory.memstring(entry.strname), "'\n"); + this->m_dyncall_array.push_back( + [] (auto&) { + throw std::runtime_error("Clientside-only dynamic call triggered"); + }); + continue; + } + if (entry.server_side_only && client_side) { + if (verbose) strf::to(stdout)( + "Skipping server-side-only dynamic call '", + machine().memory.memstring(entry.strname), "'\n"); + this->m_dyncall_array.push_back( + [] (auto&) { + throw std::runtime_error("Serverside-only dynamic call triggered"); + }); + continue; + } auto it = m_dynamic_functions.find(entry.hash); if (LIKELY(it != m_dynamic_functions.end())) @@ -439,18 +467,23 @@ void Script::resolve_dynamic_calls() } else { this->m_dyncall_array.push_back( [] (auto&) { - throw std::runtime_error("Unimplemented-trap"); + throw std::runtime_error("Unimplemented dynamic call triggered"); }); - if constexpr (WARN_ON_UNIMPLEMENTED_DYNCALL) { + if (verbose) { const std::string name = machine().memory.memstring(entry.strname); - strf::to(stdout)( - "WARNING: Unimplemented dynamic function '", name, "' with hash ", strf::hex(entry.hash), " and program table index ", - m_dyncall_array.size(), "\n"); + strf::to(stderr)( + "WARNING: Unimplemented dynamic function '", name, "' with hash ", + strf::hex(entry.hash), " and program table index ", i, " (total: ", + m_dyncall_array.size(), ")\n"); } + unimplemented++; } } if (m_dyncall_array.size() != entries) throw std::runtime_error("Mismatching number of dynamic call array entries"); + strf::to(stdout)( + "* Resolved dynamic calls for '", name(), "' with ", entries, " entries, ", + unimplemented, " unimplemented\n"); } void Script::set_global_setting(std::string_view setting, gaddr_t value) diff --git a/engine/src/script/script.hpp b/engine/src/script/script.hpp index 87bdcfa..5ead970 100644 --- a/engine/src/script/script.hpp +++ b/engine/src/script/script.hpp @@ -281,7 +281,7 @@ struct Script void max_depth_exceeded(gaddr_t); void machine_setup(); void machine_remote_setup(); - void resolve_dynamic_calls(); + void resolve_dynamic_calls(bool initialization, bool client_side, bool verbose); void dynamic_call_error(uint32_t idx, const std::exception& e); static long finish_benchmark(std::vector&); @@ -311,6 +311,9 @@ struct Script uint32_t hash; uint32_t resv; uint32_t strname; + bool initialization_only; + bool client_side_only; + bool server_side_only; }; std::vector m_dyncall_array; gaddr_t m_g_dyncall_table = 0x0; diff --git a/programs/dynamic_calls.json b/programs/dynamic_calls.json index 340ad62..1a9d45d 100644 --- a/programs/dynamic_calls.json +++ b/programs/dynamic_calls.json @@ -3,6 +3,19 @@ "typedef void (*timer_callback) (int, void*)", "typedef void (*gui_callback) (unsigned, void*)" ], + "initialization": [ + ], + "clientside": [ + "GUI::find", + "GUI::window", + "GUI::widget", + "GUI::button", + "GUI::label", + "GUI::widget_set_pos", + "GUI::widget_callback" + ], + "serverside": [ + ], "Timer::stop": "void sys_timer_stop (int)", "Timer::periodic": "int sys_timer_periodic (float, float, timer_callback, void*, size_t)", "Debug::breakpoint": "void sys_breakpoint (uint16_t, const char*)", diff --git a/programs/dyncalls/generate.py b/programs/dyncalls/generate.py index f851894..a2f7c53 100644 --- a/programs/dyncalls/generate.py +++ b/programs/dyncalls/generate.py @@ -136,6 +136,24 @@ def emit_inline_assembly(header, asmdef, index, fargs): with open(args.jsonfile) as f: j = json.load(f) +# List of client-side only dyncalls +client_side = [] +if "clientside" in j: + for key in j["clientside"]: + client_side.append(key) + +# List of server-side only dyncalls +server_side = [] +if "serverside" in j: + for key in j["serverside"]: + server_side.append(key) + +# List of initialization-only dyncalls +initialization = [] +if "initialization" in j: + for key in j["initialization"]: + initialization.append(key) + header = """ #pragma once #include @@ -161,7 +179,9 @@ def emit_inline_assembly(header, asmdef, index, fargs): # create dyncall prototypes and assembly for key in j: - if key != "typedef": + if key == "typedef" or key == "clientside" or key == "serverside" or key == "initialization": + continue + else: asmdef = j[key] asmname = asmdef.split(' ')[1] @@ -182,10 +202,20 @@ def emit_inline_assembly(header, asmdef, index, fargs): if args.verbose: print("Dynamic call: " + key + ", hash 0x" + crc + (" (inlined)" if inlined else "")) + # Each dynamic call has a table index where the name and hash is stored dyncall += ' .long 0x' + crc + '\\n\\\n' dyncall += ' .long ' + str(0) + '\\n\\\n' dyncall += ' .long ' + asmname + '_str\\n\\\n' + # Flags (one byte each for client-side, server-side, initialization, and padding) + is_client_side = key in client_side + is_server_side = key in server_side + is_initialization = key in initialization + dyncall += ' .byte ' + str(int(is_initialization)) + '\\n\\\n' + dyncall += ' .byte ' + str(int(is_client_side)) + '\\n\\\n' + dyncall += ' .byte ' + str(int(is_server_side)) + '\\n\\\n' + dyncall += ' .byte 0\\n\\\n' + # These dynamic calls use the table indexed variant # Each dynamic call has a table index where the name and hash is stored # and at run-time this value is lazily resolved diff --git a/tests/events.cpp b/tests/events.cpp index 3cab7ba..dc097af 100644 --- a/tests/events.cpp +++ b/tests/events.cpp @@ -37,7 +37,8 @@ TEST_CASE("Simple events", "[Events]") extern "C" void VoidFunc() { } extern "C" int FailingFunc() { - assert(0); + asm volatile("unimp"); + __builtin_unreachable(); } )M"); @@ -89,15 +90,13 @@ TEST_CASE("Simple events", "[Events]") /* Function that doesn't exist */ Event ev6(script, "VoidlessFunc"); - auto res6 = ev6.call(); - REQUIRE(!res6.has_value()); + REQUIRE(!ev6.call()); /* Function that returns void */ Event ev7(script, "VoidFunc"); - REQUIRE(ev7.call().has_value()); + REQUIRE(ev7.call()); /* Function that fails */ Event ev8(script, "FailingFunc"); - auto res8 = ev8.call(); - REQUIRE(!res8.has_value()); + REQUIRE(!ev8.call()); }