Build iserv (external interpreter) program on demand
We're in the process of making boot libraries reinstallable. E.g. GHC was built with `ghc-bignum` using the GMP backend, but the user wants to use `ghc-bignum` with its `native` (haskell) backend. To make this possible, we have to rebuild `ghc-bignum` with the `native` backend enabled and every package depending on it: `ghc-internal`, `base`, etc.
By doing this, we end up in a state where GHC can't use its internal interpreter for Template Haskell as it would require linking conflicting versions of the boot libraries. This is akin to cross-compilation and indeed we can similarly use an external-interpreter program to allow GHC to evaluate TH splices.
Currently, GHC comes with some prebuilt `ghc-iserv-*` programs to support RTS ways: profiling, debug, etc. (A vanilla GHC can't use its internal interpreter to load a library built with the profiling/debug way because of the ABI incompatibility). Distributing a set of prebuilt `iserv` programs was (quite) fine as there was only a closed number of `iserv` programs to distribute (and advanced users could build their own).
Now, however, if we support reinstallable boot libraries, it's no longer a closed world. Users will expect that changing the Cabal flag for `ghc-bignum` in their `cabal.project` will just work. So I believe **GHC should build `ghc-iserv` programs on demand.**
Currently an `iserv` program is composed of just:
```haskell
-- utils/iserv/src/Main.hs
-- |
-- The Remote GHCi server.
--
-- For details on Remote GHCi, see Note [Remote GHCi] in
-- compiler/GHC/Runtime/Interpreter.hs.
--
module Main (main) where
import GHCi.Server (defaultServer)
main :: IO ()
main = defaultServer
```
```c
// utils/iserv/cbits/iservmain.c
#include <ghcversion.h>
#include <rts/PosixSource.h>
#include <Rts.h>
#include <HsFFI.h>
int main (int argc, char *argv[])
{
RtsConfig conf = defaultRtsConfig;
// We never know what symbols GHC will look up in the future, so
// we must retain CAFs for running interpreted code.
conf.keep_cafs = 1;
conf.rts_opts_enabled = RtsOptsAll;
extern StgClosure ZCMain_main_closure;
hs_main(argc, argv, &ZCMain_main_closure, conf);
}
```
We could reduce it to just the following C file:
```c
#include <ghcversion.h>
#include <rts/PosixSource.h>
#include <Rts.h>
#include <HsFFI.h>
int main (int argc, char *argv[])
{
RtsConfig conf = defaultRtsConfig;
// We never know what symbols GHC will look up in the future, so
// we must retain CAFs for running interpreted code.
conf.keep_cafs = 1;
conf.rts_opts_enabled = RtsOptsAll;
extern StgClosure ghci_unit_idZCGHCiziServerzidefaultServer; // ghci_unit_id depends on the ghci unit we link
hs_main(argc, argv, &ghci_unit_idZCGHCiziServerzidefaultServer, conf);
}
```
By only having a C file, we wouldn't have to build another *Haskell* program in a current GHC session which could be difficult (messing with HscEnv, etc.). If we only have to build a C program, it's fine: we just need to pass the appropriate flags to link with the GHCi unit.
A proof that it works is that it is already what we do in the JS backend to spawn an external interpreter on demand (see [here](https://gitlab.haskell.org/ghc/ghc/-/blob/94da936507c685aa8101a714e7619b4d428d0187/compiler/GHC/Runtime/Interpreter/JS.hs#L59)), with the difference that it's done in JS so there is nothing to compile compared to C.
Cons:
- a bit slower than just *running* a pre-existing program. But we would cache these programs for the duration of a `ghc --make` session, and it's only a C program to compile/link, not Haskell.
Pros:
- no need to prebuild `ghc-iserv-*` programs: smaller bindists, smaller bug surface
- support for arbitrary RTS ways, dependencies, etc.
- one step closer to https://gitlab.haskell.org/ghc/ghc/-/issues/12218 (where we also statically link the dependencies required by a splice)
- (advanced users can still provide their own `ghc-iserv` via `-pgmi`, so no change here)
issue