diff --git a/NEXT.md b/NEXT.md index 927e371..336a1ff 100644 --- a/NEXT.md +++ b/NEXT.md @@ -389,6 +389,17 @@ none. `Emit.signature` is now the single place a function's LLVM signature is spelled, because a `define` here and a `declare` there drift the moment one of them grows a case for `Unit` or for a slice parameter. +**`flan_dev.c` is compiled into every build, not only a dev one.** Nothing in a +release build calls into it — the compiler only emits a registry lookup for a +name the host was not built with, which cannot arise without cells — but the +agent package's C refers to it, and a package's C sources are collected +whatever `main` does. Leaving it out of release builds made `flan build +sand.flan` fail at the link with an undefined `flan_dev_result_get`, which +reads as a compiler bug rather than as a missing flag. The table is BSS, so the +cost is address space and not binary size; `-rdynamic` and the cells are still +what `--dev` means. `test_agent.ml` links the agent program both ways for this +reason. + **`-rdynamic` is load-bearing.** A normal executable exports nothing: `nm -D calc-me | grep 'flan\.'` is empty, so a loaded module's `declare`s would have nothing to bind to. The test passes it through `lflags`, which keeps it a diff --git a/lib/build.ml b/lib/build.ml index 3f283b7..a1dfa8d 100644 --- a/lib/build.ml +++ b/lib/build.ml @@ -120,11 +120,18 @@ let executable ?(opts = default) ?(csrcs = []) ?(lflags = []) let dir = workdir () in let ll = Filename.concat dir (Filename.basename out ^ ".ll") in write ll (Emit.program ~checks:opts.checks ~dev:opts.dev p); + (* [flan_dev.c] is compiled into every build, not only a dev one. Nothing in + a release build calls into it — the compiler only emits a registry lookup + for a name the host was not built with, which cannot arise without cells — + but the agent package's C refers to it, and a package's C sources are + collected whatever [main] does. Leaving it out made [flan build sand.flan] + fail at the link with an undefined symbol, which reads as a compiler bug + rather than as a missing flag. The table is BSS, so this costs address + space and not binary size, and [-rdynamic] and the cells are still what + [--dev] means. *) let objs = compile_c ~opts ~src:Runtime_src.source ~name:"flan_rt.c" - :: (if opts.dev then - [ compile_c ~opts ~src:Runtime_src.dev_source ~name:"flan_dev.c" ] - else []) + :: [ compile_c ~opts ~src:Runtime_src.dev_source ~name:"flan_dev.c" ] @ List.map (fun c -> compile_c ~opts ~src:(read_file c) ~name:(Filename.basename c)) diff --git a/test/test_agent.ml b/test/test_agent.ml index 8bcb818..b751d06 100644 --- a/test/test_agent.ml +++ b/test/test_agent.ml @@ -77,6 +77,19 @@ let () = (Build.executable ~opts:dev ~csrcs:l.Load.csrcs ~lflags:l.Load.lflags t.Session.host ~out:exe); + (* And the same program without [--dev], which has to *link*. A package's C + sources are collected whatever [main] does, so the agent's C is in every + build that imports it, and it refers to the dev runtime — leaving that + out made this an undefined symbol at the link rather than a missing + flag. Nothing is run: with no cells the agent refuses every module, and + linking is the whole claim. *) + (match + Build.executable ~opts:Build.default ~csrcs:l.Load.csrcs + ~lflags:l.Load.lflags t.Session.host ~out:(tmp "prog-release") + with + | _ -> () + | exception Failure m -> fail "a release build of the agent: %s" m); + (* Two evaluations from the one session, which is the daemon's loop and the thing no earlier test does. The first introduces a global the process was never built with; the second only reads it, and can only