Ok, so I'm finally going to replace the current backend for my visual scripting with a proper bytecode-interpreter/VM. While I got far with my current approach of pretty much misusing the c++-template-system as a poor mans compiler is reaching its limit (mostly in terms of performance; which in term also limits my ability to implement certain new features).
While I think I do have a certain idea on how to tackle this transformation, on thing thats still a bit unclear is the topic of calling native functions via my own bytecode. So lets talk about the requirements first:
- C++20, x86 on Windows (Visual Studio) is all thats needed for now (though I could switch to x64 if it makes things easier; I'll stick with VS 100%)
- Users must be free to register eigther global functions. lambdas or member functions w/o being required to write wrappers (though template-metaprocessing as well as reflection can obviously be used to generate the necessary code)
- Only interested in one calling-convention for now. I'm using cdecl, but I could do any one that is the easiest
- I have a limited know of data-types that could be passed, though there are a few variants (ie. primitive types int/float/uint8_t by value or by ref; objecty by pointer or ref or ** for modifying what object is stored at a certain location; structs by value or ref or const&)
- I don't really want to limit the amount of parameters to a function; but if it makes it easier for now I could have a limit of like 6 parameters for native functions
Ok, now to the question: How would I go about preparing the stack/arguments for calling a function that I have the address for, as well as all arguments on a stack? Lets maybe break the question down to a few key points:
- How do I even start? I looked a bit at the assembly that is involved in c++-code calling functions; but how would I achieve this myself? Do I have to write inline-assembly or something even more complicated?
- How would I go about passing arguments? Again I looked a bit at the assembly, and it seems for functions that take primitive types (starting easy), the compiler seems to be pushing arguments onto more and more registers. But like, how would I know which registers to use; what am I doing when I run out of registers and how do I handle more complex cases like struct-by-value etc…? While I could find that out probably by trial of error, I was wondering if there was some documentation/tutorial/articles that handles this in-depth?
- A mix of 1-2: Given any number of parameters not know until I run my own compiler, how do I generate the code necessary to handle those parameters in c++?
So, can somebody give me some pointers? I think I can still use my template-based approach for accessing arguments of the stack for the start so this shouldn't be a dealbreaker, but if I'm already going all-in for speed I'd just rather do it right from the start.