From d7bc3832c1e586df2092bc97d5b2929c3a4e022e Mon Sep 17 00:00:00 2001 From: MaartenS11 Date: Fri, 12 Jun 2026 18:00:31 +0200 Subject: [PATCH] Use unordered_map instead of vector for primitives Technically this should be faster than a linear search + it allows overwriting primitives that you already have. For example: ```cpp wac->interpreter->register_primitive("chip_digital_write", [](Module *m) -> bool { pop_args(2); printf("chip_digital_write\n"); return true; }, &twoToNoneU32); ``` --- src/Interpreter/interpreter.h | 35 +++-- src/Primitives/arduino.cpp | 1 + src/Primitives/emulated.cpp | 1 + src/Primitives/idf.cpp | 1 + src/Primitives/primitive_macros.h | 227 ++++++++++++++++++++++++++++++ src/Primitives/primitives.h | 227 ------------------------------ src/Primitives/zephyr.cpp | 1 + 7 files changed, 253 insertions(+), 240 deletions(-) create mode 100644 src/Primitives/primitive_macros.h diff --git a/src/Interpreter/interpreter.h b/src/Interpreter/interpreter.h index fa8c5eb94..c0c412fde 100644 --- a/src/Interpreter/interpreter.h +++ b/src/Interpreter/interpreter.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "../Utils/macros.h" #include "../WARDuino/internals.h" @@ -57,8 +59,18 @@ class Interpreter { static void report_overflow(Module *m, uint8_t *maddr); + void register_primitive(const char *name, const Primitive f, Type *t) { + PrimitiveEntry p = {}; + p.name = name; + p.t = t; + p.f = f; + p.f_reverse = nullptr; + p.f_serialize_state = nullptr; + register_primitive(p); + } + void register_primitive(const PrimitiveEntry &entry) { - primitives.push_back(entry); + primitives[entry.name] = entry; } //------------------------------------------------------ @@ -67,14 +79,11 @@ class Interpreter { // ReSharper disable once CppDFAConstantFunctionResult bool resolve_primitive(const char *symbol, Primitive *val) { debug("Resolve primitive %s\n", symbol); - - for (auto &primitive : primitives) { - // printf("Checking %s = %s \n", symbol, primitive.name); - if (!strcmp(symbol, primitive.name)) { - debug("FOUND PRIMITIVE\n"); - *val = primitive.f; - return true; - } + auto it = primitives.find(symbol); + if (it != primitives.end()) { + debug("FOUND PRIMITIVE\n"); + *val = it->second.f; + return true; } FATAL("Could not find primitive %s \n", symbol); return false; // unreachable @@ -90,8 +99,8 @@ class Interpreter { prim_names.emplace(m->functions[i].import_field); } - for (PrimitiveEntry &p : primitives) { - if (prim_names.find(p.name) != prim_names.end()) { + for (auto &[name, p] : primitives) { + if (prim_names.find(name) != prim_names.end()) { if (p.f_reverse) { dbg_info("Reversing state for primitive %s\n", p.name); p.f_reverse(m, external_state); @@ -102,7 +111,7 @@ class Interpreter { std::vector get_io_state(Module *) const { std::vector ioState; - for (auto &primitive : primitives) { + for (auto &[name, primitive] : primitives) { if (primitive.f_serialize_state) { primitive.f_serialize_state(ioState); } @@ -112,5 +121,5 @@ class Interpreter { protected: private: - std::vector primitives; + std::unordered_map primitives; }; diff --git a/src/Primitives/arduino.cpp b/src/Primitives/arduino.cpp index cb150d6ac..c017fea8e 100644 --- a/src/Primitives/arduino.cpp +++ b/src/Primitives/arduino.cpp @@ -23,6 +23,7 @@ #include "../Utils/macros.h" #include "../Utils/util.h" #include "../WARDuino/CallbackHandler.h" +#include "primitive_macros.h" #include "primitives.h" // NEOPIXEL diff --git a/src/Primitives/emulated.cpp b/src/Primitives/emulated.cpp index 35968342d..eaaa0161a 100644 --- a/src/Primitives/emulated.cpp +++ b/src/Primitives/emulated.cpp @@ -24,6 +24,7 @@ #include "../Utils/macros.h" #include "../Utils/util.h" #include "../WARDuino/CallbackHandler.h" +#include "primitive_macros.h" #include "primitives.h" #define NUM_GLOBALS 0 diff --git a/src/Primitives/idf.cpp b/src/Primitives/idf.cpp index 09ea1d8d1..273ebf967 100644 --- a/src/Primitives/idf.cpp +++ b/src/Primitives/idf.cpp @@ -25,6 +25,7 @@ #include "../Utils/macros.h" #include "../Utils/util.h" #include "driver/gpio.h" +#include "primitive_macros.h" #include "primitives.h" #define NUM_GLOBALS 0 diff --git a/src/Primitives/primitive_macros.h b/src/Primitives/primitive_macros.h new file mode 100644 index 000000000..fc4f3ecf9 --- /dev/null +++ b/src/Primitives/primitive_macros.h @@ -0,0 +1,227 @@ +#pragma once + +#define _init_primitive(prim_name) \ + PrimitiveEntry p = {}; \ + p.name = #prim_name; \ + p.t = &(prim_name##_type); \ + p.f = &(prim_name); \ + p.f_reverse = nullptr; \ + p.f_serialize_state = nullptr; + +#define install_primitive(prim_name) \ + { \ + dbg_info("installing primitive %s\n", #prim_name); \ + _init_primitive(prim_name) interpreter->register_primitive(p); \ + } + +#define install_reversible_primitive(prim_name) \ + { \ + dbg_info("installing reversible primitive %s\n", #prim_name); \ + _init_primitive(prim_name) p.f_reverse = &(prim_name##_reverse); \ + p.f_serialize_state = &(prim_name##_serialize); \ + interpreter->register_primitive(p); \ + } + +#define def_prim(function_name, type) \ + Type function_name##_type = type; \ + bool function_name([[maybe_unused]] Module *m) + +#define def_prim_reverse(function_name) \ + void function_name##_reverse(Module *m, \ + std::vector external_state) + +#define def_prim_serialize(function_name) \ + void function_name##_serialize( \ + std::vector &external_state) + +// TODO: use fp +#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) +#define arg3 get_arg(m, 3) +#define arg4 get_arg(m, 4) +#define arg5 get_arg(m, 5) +#define arg6 get_arg(m, 6) +#define arg7 get_arg(m, 7) +#define arg8 get_arg(m, 8) +#define arg9 get_arg(m, 9) + +inline uint32_t param_arr_len0[0] = {}; +inline uint32_t param_I32_arr_len1[1] = {I32}; +inline uint32_t param_I32_arr_len2[2] = {I32, I32}; +inline uint32_t param_I32_arr_len3[3] = {I32, I32, I32}; +inline uint32_t param_I32_arr_len4[4] = {I32, I32, I32, I32}; +inline uint32_t param_I32_arr_len5[5] = {I32, I32, I32, I32, I32}; +inline uint32_t param_I32_arr_len6[6] = {I32, I32, I32, I32, I32, I32}; +inline uint32_t param_I32_arr_len7[7] = {I32, I32, I32, I32, I32, I32, I32}; +inline uint32_t param_I32_arr_len10[10] = {I32, I32, I32, I32, I32, + I32, I32, I32, I32, I32}; +inline uint32_t param_I64_arr_len1[1] = {I64}; + +inline Type oneToNoneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 0, + .results = nullptr, + .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ +}; + +inline Type twoToNoneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 0, + .results = nullptr, + .mask = 0x80011 /* 0x800 = no return ; 1 = I32; 1 = I32*/ +}; + +inline Type threeToNoneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 0, + .results = nullptr, + .mask = 0x800111 /* 0x800 = no return ; 1=I32; 1=I32; 1=I32*/ +}; + +inline Type fourToNoneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 0, + .results = nullptr, + .mask = + 0x8001111 /* 0x800 = no return ; 1 = I32; 1 = I32; 1 = I32; 1 = I32*/ +}; + +inline Type fiveToNoneU32 = { + .form = FUNC, + .param_count = 5, + .params = param_I32_arr_len5, + .result_count = 0, + .results = nullptr, + .mask = 0x80011111 /* 0x800 = no return ; 1 = I32 ; 1 = I32; 1 = I32; 1 = + I32; 1 = I32*/ +}; + +inline Type sixToNoneU32 = { + .form = FUNC, + .param_count = 6, + .params = param_I32_arr_len6, + .result_count = 0, + .results = nullptr, + .mask = 0x800111111 /* 0x800 = no return ; 1 = I32 ; 1 = I32 ; 1 = I32; 1 = + I32; 1 = I32; 1 = I32*/ +}; + +inline Type sevenToNoneU32 = { + .form = FUNC, + .param_count = 7, + .params = param_I32_arr_len7, + .result_count = 0, + .results = nullptr, + .mask = 0x8001111111 /* 0x800 = no return ; 1 = I32 ; 1 = I32 ; 1 = I32; 1 = + I32; 1 = I32; 1 = I32; 1 = I32*/ +}; + +inline Type zeroToOneU32 = { + .form = FUNC, + .param_count = 0, + .params = param_arr_len0, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x810 /* 0x8 1=I32 0=endRet ; no params*/ +}; + +inline Type oneToOneU32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +inline Type oneToOneI32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +inline Type twoToOneU32 = { + .form = FUNC, + .param_count = 2, + .params = param_I32_arr_len2, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ +}; + +inline Type threeToOneU32 = { + .form = FUNC, + .param_count = 3, + .params = param_I32_arr_len3, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x810111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32*/ +}; + +inline Type fourToOneU32 = { + .form = FUNC, + .param_count = 4, + .params = param_I32_arr_len4, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32; 1=I32*/ +}; + +inline Type tenToOneU32 = { + .form = FUNC, + .param_count = 10, + .params = param_I32_arr_len10, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x8101111111111 /* 0x8 1=I32 0=endRet ; 10 params 1=I32*/ +}; + +inline Type NoneToNoneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 0, + .results = nullptr, + .mask = 0x80000}; + +inline Type oneToNoneI32 = { + .form = FUNC, + .param_count = 1, + .params = param_I32_arr_len1, + .result_count = 0, + .results = nullptr, + .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ +}; + +inline Type NoneToOneU32 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I32_arr_len1, + .mask = 0x81000}; + +inline Type NoneToOneU64 = {.form = FUNC, + .param_count = 0, + .params = nullptr, + .result_count = 1, + .results = param_I64_arr_len1, + .mask = 0x82000}; diff --git a/src/Primitives/primitives.h b/src/Primitives/primitives.h index a52b8adc3..5d987bdea 100644 --- a/src/Primitives/primitives.h +++ b/src/Primitives/primitives.h @@ -1,7 +1,6 @@ #ifndef WARDUINO_PRIM_H #define WARDUINO_PRIM_H -#include "../Utils/macros.h" #include "../WARDuino/internals.h" /** @@ -74,230 +73,4 @@ void invoke_primitive(Module *m, const std::string &function_name, Ts... args) { primitive(m); } -#define _init_primitive(prim_name) \ - PrimitiveEntry p = {}; \ - p.name = #prim_name; \ - p.t = &(prim_name##_type); \ - p.f = &(prim_name); \ - p.f_reverse = nullptr; \ - p.f_serialize_state = nullptr; - -#define install_primitive(prim_name) \ - { \ - dbg_info("installing primitive %s\n", #prim_name); \ - _init_primitive(prim_name) interpreter->register_primitive(p); \ - } - -#define install_reversible_primitive(prim_name) \ - { \ - dbg_info("installing reversible primitive %s\n", #prim_name); \ - _init_primitive(prim_name) p.f_reverse = &(prim_name##_reverse); \ - p.f_serialize_state = &(prim_name##_serialize); \ - interpreter->register_primitive(p); \ - } - -#define def_prim(function_name, type) \ - Type function_name##_type = type; \ - bool function_name([[maybe_unused]] Module *m) - -#define def_prim_reverse(function_name) \ - void function_name##_reverse(Module *m, \ - std::vector external_state) - -#define def_prim_serialize(function_name) \ - void function_name##_serialize( \ - std::vector &external_state) - -// TODO: use fp -#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) -#define arg3 get_arg(m, 3) -#define arg4 get_arg(m, 4) -#define arg5 get_arg(m, 5) -#define arg6 get_arg(m, 6) -#define arg7 get_arg(m, 7) -#define arg8 get_arg(m, 8) -#define arg9 get_arg(m, 9) - -inline uint32_t param_arr_len0[0] = {}; -inline uint32_t param_I32_arr_len1[1] = {I32}; -inline uint32_t param_I32_arr_len2[2] = {I32, I32}; -inline uint32_t param_I32_arr_len3[3] = {I32, I32, I32}; -inline uint32_t param_I32_arr_len4[4] = {I32, I32, I32, I32}; -inline uint32_t param_I32_arr_len5[5] = {I32, I32, I32, I32, I32}; -inline uint32_t param_I32_arr_len6[6] = {I32, I32, I32, I32, I32, I32}; -inline uint32_t param_I32_arr_len7[7] = {I32, I32, I32, I32, I32, I32, I32}; -inline uint32_t param_I32_arr_len10[10] = {I32, I32, I32, I32, I32, - I32, I32, I32, I32, I32}; -inline uint32_t param_I64_arr_len1[1] = {I64}; - -inline Type oneToNoneU32 = { - .form = FUNC, - .param_count = 1, - .params = param_I32_arr_len1, - .result_count = 0, - .results = nullptr, - .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ -}; - -inline Type twoToNoneU32 = { - .form = FUNC, - .param_count = 2, - .params = param_I32_arr_len2, - .result_count = 0, - .results = nullptr, - .mask = 0x80011 /* 0x800 = no return ; 1 = I32; 1 = I32*/ -}; - -inline Type threeToNoneU32 = { - .form = FUNC, - .param_count = 3, - .params = param_I32_arr_len3, - .result_count = 0, - .results = nullptr, - .mask = 0x800111 /* 0x800 = no return ; 1=I32; 1=I32; 1=I32*/ -}; - -inline Type fourToNoneU32 = { - .form = FUNC, - .param_count = 4, - .params = param_I32_arr_len4, - .result_count = 0, - .results = nullptr, - .mask = - 0x8001111 /* 0x800 = no return ; 1 = I32; 1 = I32; 1 = I32; 1 = I32*/ -}; - -inline Type fiveToNoneU32 = { - .form = FUNC, - .param_count = 5, - .params = param_I32_arr_len5, - .result_count = 0, - .results = nullptr, - .mask = 0x80011111 /* 0x800 = no return ; 1 = I32 ; 1 = I32; 1 = I32; 1 = - I32; 1 = I32*/ -}; - -inline Type sixToNoneU32 = { - .form = FUNC, - .param_count = 6, - .params = param_I32_arr_len6, - .result_count = 0, - .results = nullptr, - .mask = 0x800111111 /* 0x800 = no return ; 1 = I32 ; 1 = I32 ; 1 = I32; 1 = - I32; 1 = I32; 1 = I32*/ -}; - -inline Type sevenToNoneU32 = { - .form = FUNC, - .param_count = 7, - .params = param_I32_arr_len7, - .result_count = 0, - .results = nullptr, - .mask = 0x8001111111 /* 0x800 = no return ; 1 = I32 ; 1 = I32 ; 1 = I32; 1 = - I32; 1 = I32; 1 = I32; 1 = I32*/ -}; - -inline Type zeroToOneU32 = { - .form = FUNC, - .param_count = 0, - .params = param_arr_len0, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x810 /* 0x8 1=I32 0=endRet ; no params*/ -}; - -inline Type oneToOneU32 = { - .form = FUNC, - .param_count = 1, - .params = param_I32_arr_len1, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ -}; - -inline Type oneToOneI32 = { - .form = FUNC, - .param_count = 1, - .params = param_I32_arr_len1, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x80011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ -}; - -inline Type twoToOneU32 = { - .form = FUNC, - .param_count = 2, - .params = param_I32_arr_len2, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x81011 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32*/ -}; - -inline Type threeToOneU32 = { - .form = FUNC, - .param_count = 3, - .params = param_I32_arr_len3, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x810111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32*/ -}; - -inline Type fourToOneU32 = { - .form = FUNC, - .param_count = 4, - .params = param_I32_arr_len4, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x8101111 /* 0x8 1=I32 0=endRet ; 1=I32; 1=I32; 1=I32; 1=I32*/ -}; - -inline Type tenToOneU32 = { - .form = FUNC, - .param_count = 10, - .params = param_I32_arr_len10, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x8101111111111 /* 0x8 1=I32 0=endRet ; 10 params 1=I32*/ -}; - -inline Type NoneToNoneU32 = {.form = FUNC, - .param_count = 0, - .params = nullptr, - .result_count = 0, - .results = nullptr, - .mask = 0x80000}; - -inline Type oneToNoneI32 = { - .form = FUNC, - .param_count = 1, - .params = param_I32_arr_len1, - .result_count = 0, - .results = nullptr, - .mask = 0x8001 /* 0x800 = no return ; 1 = I32*/ -}; - -inline Type NoneToOneU32 = {.form = FUNC, - .param_count = 0, - .params = nullptr, - .result_count = 1, - .results = param_I32_arr_len1, - .mask = 0x81000}; - -inline Type NoneToOneU64 = {.form = FUNC, - .param_count = 0, - .params = nullptr, - .result_count = 1, - .results = param_I64_arr_len1, - .mask = 0x82000}; - #endif diff --git a/src/Primitives/zephyr.cpp b/src/Primitives/zephyr.cpp index f9a142d47..14f6d3704 100644 --- a/src/Primitives/zephyr.cpp +++ b/src/Primitives/zephyr.cpp @@ -33,6 +33,7 @@ #include "../Utils/util.h" #include "Mindstorms/Motor.h" #include "Mindstorms/uart_sensor.h" +#include "primitive_macros.h" #include "primitives.h" #define NUM_GLOBALS 0