Firstly, the generated plugin "(on Linux, my answer is focused on Linux, but can be adapted to Windows with some effort, you can use multi-platform frameworks such as Qt or POCO or Glib from GTK , and then all the plugin download options for à la dlopen with a common API that you can use on Windows, Linux, MacOSX, on Android):
- generate C code (or assemblies) in some kind of
/tmp/generated01.c file (you can even generate C ++ code using standard C ++ containers, but its compilation will be much slower, beware of name mangling , so we emit and use extern "C" functions, read C ++ dlopen mini HowTo ). See this answer for an explanation of why generating C is worthwhile (and might be better and more portable than generating assembler code). - start (using
fork + execve + waitpid or just system ) compile this generated file into a common object /tmp/genenerated01.so by running gcc -fPIC -Wall -O /tmp/generated01.c -shared -o /tmp/generated01.so ; you almost need to get position-independent code , hence the -fPIC flag. If you use dlopen for your generated assembler code, you need to improve the assembler generator to emit PIC code. dlopen what's new /tmp/generated01.so (use dynamic linker ), see dlopen (3) ; you could even remove now useless C generated file /tmp/generated01.cdlsym corresponding characters to get pointers to the generated code, see dlsym (3) ; your application will simply call the generated code using these function pointers.- when you are sure that you do not need any functions from it, and that no call frame uses it, you could
dlclose use this shared library of objects (but you can accept a leak of some address space without calling dlclose at all)
The above approach is noteworthy and can be used many times (my manydl.c demonstrates that you could dlopen a million different common objects) and is almost even compatible (even when emitting C code!) With the interactive Read-Eval-Print-Loop - on most modern desktops and laptops and servers, since most of the time the generated /tmp/generated01.c would be quite small (for example, a few hundred lines no more) to be very quickly generated and compiled (via gcc , etc.) ) I even use this in MELT for my REPL mode. On Linux, for this plugin approach, you usually need to associate the main application with -rdynamic (so dlopen -ed plugins can reference and call functions from the main application).
Then other approaches may be to use the Just-In-Time compilation library , for example p>
- GNU lightning (which produces very slow machine code very quickly - this is a very short JIT emission time, but the generated code is slow because it is very non-optimized)
- asmjit ; this is an x86-64 specification and allows the creation of individual x86-64 machine instructions.
- GNU libjit is available for several platforms and offers an interpreter mode for other platforms.
- LLVM (part of Clang / LLVM that can be used as a JIT library)
- GCCJIT (new JIT library interface for GCC )
In general, the first elements of this list can issue JIT machine code quite quickly, but this code will not work as fast as compiling with gcc -fPIC -O1 or -O2 equivalent generated C code (but will usually run 2 to 5 times slower !); the last two elements (LLVM and GCCJIT) are compiler-based: they can optimize and emit efficient code by slower JIT code emission. All JIT libraries are capable (for example, dlsym for plugins) of providing function pointers for new JIT-built functions.
Note that there is a trade-off: some methods can quickly generate some machine code if you accept this generated code to work a bit slow later; other methods (especially GCCJIT or LLVM) spend time optimizing the generated machine code, so it takes more time to emit the machine code, but this code will work faster. You should not expect both (short generation time and fast lead time), since there is no free lunch.
I believe that it is practically not worth generating some assembler codes manually . You will not be able to generate very optimized code (because optimization is a very complex art, and both GCC and Clang have millions of source codes for optimization) if you do not spend many years on it. Using some JIT library is simpler, and "compiling" in C or C ++ is also quite simple (you leave the optimization load on the C compiler that you invoke).
You can also consider rewriting your application into some language using homoiconicity and metaprogramming (e.g. multi-stage programming ), such as Common Lisp (and many others, such as those that provide eval ). Its SBCL implementation always emits machine code ...
You can also embed an interpreter such as Lua - maybe even LuaJit - or Guile in your application. The main advantage of introducing an existing language is that there are resources (books, modules, ...) and a community of people who know them (creating a good language is difficult!). In addition, the built-in interpreter library is well-designed and probably well-debugged (since a lot is used), and some of them are quite fast (using bytecode ).