Dear Marc,
An interesting question came up when discussing treeshaked Gerbil static builds:
One could get a C-compiled program
* that is optimally and maximally tree-shaked hence omitting inclusion in the binary of evidently unused code, and
* with optimal performance in the respect of having no trampoline calls at all,
if one would just append together Gambit's runtime's .scm files with one's program's .scm files, into one gigantic total.scm file, which has (declare (optimize-dead-definitions)) and is built with --enable-single-host .
To take this conversation one step further, I like to ask, is this practically possible?
I'd really guess it is. If so, your hints about how I can actually do this would be much appreciated.
My program consists of the three vanilla Gambit .scm files a.scm b.scm c.scm .
I guess very roughly this will be a tweak of
echo "(declare (optimize-dead-definitions))" > total.scm cat gambit/lib/*.scm a.scm b.scm c.scm >> total.scm gsc -e '(compile-file-to-target "total.scm")' (aka gsc -c total.scm) cat gambit/lib/os_*.c total.c > c_total.c gcc -o mystaticexe -Igambit/include/ c_total.c
Here especially, any more C files than gambit/lib/os_*.c needed, and should the Gambit runtime .c and .scm files be appended in a particular order if so which?
Thanks! Adam
Afficher les réponses par date
I’m having a hard time parsing your question… so here is my best stab…
On Oct 19, 2019, at 3:03 PM, Adam adam.mlmb@gmail.com wrote:
Dear Marc,
An interesting question came up when discussing treeshaked Gerbil static builds:
One could get a C-compiled program
that is optimally and maximally tree-shaked hence omitting inclusion in the binary of evidently unused code, and
with optimal performance in the respect of having no trampoline calls at all,
Gambit has a 2-level trampoline. There’s a “global” trampoline in the ___trampoline function in lib/setup.c, but also a “local” trampoline in each host C function. To completely do away with the local trampoline the implementation of the ___JUMP macro in include/gambit.h must be changed to not check if the destination of the jump is the same host C function.
Note that this means it will be impossible to load dynamically loadable object files (.oN files) because then there would be more than one host C function...
if one would just append together Gambit's runtime's .scm files with one's program's .scm files, into one gigantic total.scm file, which has (declare (optimize-dead-definitions)) and is built with --enable-single-host .
To take this conversation one step further, I like to ask, is this practically possible?
I'd really guess it is. If so, your hints about how I can actually do this would be much appreciated.
Approach #1:
- Combine all the lib/_*.scm files together, plus your application code. - Note that the lib/_kernel.scm file (which is where execution starts) has to be tweaked so that it doesn’t call ##load-vm at the end. - Then compile the combined code with gsc, telling gsc that there is an empty runtime library to link with.
This is the rough picture of what has to be done. YMWV… there are lots of details to get right.
Approach #2:
- Wait until I get around implementing the -whole-program gsc option (it has been on my TODO because whole program compilation becomes much easier with a module system and a modular RTS)…
My program consists of the three vanilla Gambit .scm files a.scm b.scm c.scm .
I guess very roughly this will be a tweak of
echo "(declare (optimize-dead-definitions))" > total.scm cat gambit/lib/*.scm a.scm b.scm c.scm >> total.scm gsc -e '(compile-file-to-target "total.scm")' (aka gsc -c total.scm) cat gambit/lib/os_*.c total.c > c_total.c gcc -o mystaticexe -Igambit/include/ c_total.c
Here especially, any more C files than gambit/lib/os_*.c needed, and should the Gambit runtime .c and .scm files be appended in a particular order if so which?
Yes the order matters. Definitions of procedures usually have to come before the calls to those procedures (except when the block declaration is used).
Thanks! Adam
Dear Marc,
Yes I think you read me right that this is a "whole-program compilation" question.
On Sun, 20 Oct 2019 at 03:40, Marc Feeley feeley@iro.umontreal.ca wrote:
I’m having a hard time parsing your question… so here is my best stab…
On Oct 19, 2019, at 3:03 PM, Adam adam.mlmb@gmail.com wrote:
Dear Marc,
An interesting question came up when discussing treeshaked Gerbil static
builds:
One could get a C-compiled program
- that is optimally and maximally tree-shaked hence omitting inclusion
in the binary of evidently unused code, and
- with optimal performance in the respect of having no trampoline calls
at all,
Note that this means it will be impossible to load dynamically loadable object files (.oN files) because then there would be more than one host C function...
The question is how make one single gigangic Scheme module of Gambit runtime + user code, yes.
This is a situation where there is not one single dynamically loadable object file in the program, right, the whole program (Gambit runeitme and user code) is one big statically linked object file yes.
And hence the whole program has only one single host C function, and so it will never leave that host C function, which means there will never be any overhead of host-to-host calls, and that means higher relative program performance than if there would have been host-to-host calls.
Gambit has a 2-level trampoline. There’s a “global” trampoline in the ___trampoline function in lib/setup.c, but also a “local” trampoline in each host C function. To completely do away with the local trampoline the implementation of the ___JUMP macro in include/gambit.h must be changed to not check if the destination of the jump is the same host C function.
Thanks for describing. Both these (___trampoline and ___JUMP) are involved in a host-to-host call right, which you commonly altogether call a "trampoline". It's fine that these still are in the code, they will just be invoked a very small number of times through the program's execution - 1, 2, small number?, not once per runtime procedure call anyhow.
if one would just append together Gambit's runtime's .scm files with
one's program's .scm files, into one gigantic total.scm file, which has (declare (optimize-dead-definitions)) and is built with --enable-single-host .
To take this conversation one step further, I like to ask, is this
practically possible?
I'd really guess it is. If so, your hints about how I can actually do
this would be much appreciated.
Approach #1:
- Combine all the lib/_*.scm files together, plus your application code.
- Note that the lib/_kernel.scm file (which is where execution starts) has
to be tweaked so that it doesn’t call ##load-vm at the end.
- Then compile the combined code with gsc, telling gsc that there is an
empty runtime library to link with.
This is the rough picture of what has to be done. YMWV… there are lots of details to get right.
Approach #2:
- Wait until I get around implementing the -whole-program gsc option (it
has been on my TODO because whole program compilation becomes much easier with a module system and a modular RTS)…
I see, thank you for letting me know it's on your TODO.
This clarification may suffice.
Your "-whole-program" will do exactly this, amalgamate all Gambit runtime Scheme and user code Scheme and |compile-to-target| all of it as one module, with tree shaker enabled?
If it would be a very quick thing, feel free to clarify how you "tell gsc that there is an empty runtime library to link with", do you mean that the code will like to link in "libgambc" so that it's most straightforward to provide a dummy?
My program consists of the three vanilla Gambit .scm files a.scm b.scm c.scm .
I guess very roughly this will be a tweak of
echo "(declare (optimize-dead-definitions))" > total.scm cat gambit/lib/*.scm a.scm b.scm c.scm >> total.scm gsc -e '(compile-file-to-target "total.scm")' (aka gsc -c total.scm) cat gambit/lib/os_*.c total.c > c_total.c gcc -o mystaticexe -Igambit/include/ c_total.c
Here especially, any more C files than gambit/lib/os_*.c needed, and
should the Gambit runtime .c and .scm files be appended in a particular order if so which?
Yes the order matters. Definitions of procedures usually have to come before the calls to those procedures (except when the block declaration is used).
(So just copy the link order of .c files from Gambit's makefile.)
Thanks for confirming it's straightforward. Subsequent steps are try it, and performance benchmark.
The utility for doing this would lie in smaller binary footprint and, it would need to be tested, maybe slightly higher performance which could be useful on lowest-end devices like an Android phone.
Thanks again, Adam