I finally found out a way that we can get --gc-sections to work on hosts while still having the interpreter use the simple "pass a struct of function pointers" approach!
@Brendan Hansknecht relevant to a past discussion we were having at some point
I'm not sold this will work as expected. Or at least it will only work with some languages, require c shims, and be more annoying to develop around. This just turns the problem back into exactly what we have for the current rust compiler.
Like fundamentally I don't think this is different than the linking pains we have with basic webserver or basic cli today
Not saying it wouldn't work. I fully expect it would work. Just with all the brittleness and annoyingness we have today.
Brendan Hansknecht said:
it will only work with some languages, require c shims, and be more annoying to develop around. This just turns the problem back into exactly what we have for the current rust compiler.
oh to be clear, we're already bought into all of that because we're doing lld instead of surgical linking (with the tradeoff being that surgical linking has better speed and ergonomics but seems to be unimplementable on macOS, making it unviable as a solution)
given that we're already using lld for linking (with all the ergonomics pain points that come from host authors needing to provide a static .o file which we then give to lld to link into an executable), the delta if we want --gc-sections to work is:
pub instead of passing pointers to themdeclare weak on the top-level functions it expects the app to get from the host .def file containing those same functions' types and then gives it to lldnone of those sound particularly tricky or annoying to me; all the pain points come from using a general-purpose linker like lld instead of surgical linking
which is why we spent so much time trying to get surgical linking working, but I think our ultimate conclusion that it wasn't a viable option is still correct, so I think we're priced into the downsides of lld...and at that point getting --gc-sections to work seems like a pretty small delta! :smile:
The result would be that the release builds don't use the struct of function pointers and the dev builds build it in the shim which we have to generate anyways
I would probably just do this for for effect functions and not for roc_alloc and the other required functions. Only cause I still think it is a feature for the platform author to be able to pass different allocators to different calls to roc. Also, those functions are essentially always required anyway and won't get dead code eliminated. (And makes builtins a bit easier to manage cause they only ever call roc alloc and the other required functions)
that's a great point! :thumbs_up:
Yeah, given our linking can already be so weird... e.g. we would prefer a .a with all dependencies, require a main function, want other system executable startup deps in it. I guess this is incrementally not much cost.
And given that for dev builds we only have to compile/link once per platform version (shim).
And for release builds, linking speed is not fundamental.
This sounds good to me.
Oh, one concern
Shared libraries
What about the cases where we want roc to load as a shared library plugin (like a game engine script)
In those cases, we really want everything passed in as pointers
ah good point
weak pointers would work for that on UNIX, but it wouldn't work on Windows, so to be consistent we should always do struct-of-function-pointers when building the app into a dynamic library instead of an executable :thumbs_up:
plus there'd be no dead code elimination possible there anyway, so that benefit wouldn't even apply! :smile:
So we are accepting a separate abi for shared libraries?
yeah I think that's fine - I assume basically every platform would pick one or the other anyway
Yep
Last updated: Jun 16 2026 at 16:19 UTC