Item 12 asks five questions and says to answer them with a spike rather than a rewrite. spike/embed/ is that spike: one script, six binaries, each one built to fail loudly at the thing it is asking about. It is deliberately not a dune target -- the root dune only excludes old-ocaml/, so a dune file here would land in @default and make the spike part of the build. It drives ocamlfind and clang by hand against the flan.cmxa dune already produces. The probes, in the order they would kill the idea: the smallest possible link, a C main() reaching one OCaml function; the whole compiler linked in and doing real work; the same again with lib/dynload_stubs.c from the unmerged dlopen branch, because that is the only C the compiler itself is built from; the game keeping the main thread while caml_startup happens on a pthread beside it; the SIGSEGV disposition read on both sides of caml_startup; and an 8 MiB arena checked byte for byte across a compaction. No result is written down yet. This is the apparatus.
41 lines
1.9 KiB
OCaml
41 lines
1.9 KiB
OCaml
(* Step 2: reach enough of the compiler that the linker cannot drop it, and do
|
|
real compiler work in-process so the measurement is of a working compiler
|
|
rather than of dead code that happened to link.
|
|
|
|
The work is the driver's own path, the one bin/main.ml takes:
|
|
read -> Parse.program -> Load.program -> Check.program -> Emit.program. That
|
|
is the whole front end and the whole back end short of [llc]. Check.program
|
|
prepends the prelude itself, so the prelude is in the measurement without
|
|
being fed in twice. *)
|
|
|
|
let compile file =
|
|
let l = Flan.Load.program ~file (Flan.Parse.program (Flan.Reader.read_file file)) in
|
|
let p = Flan.Check.program l.Flan.Load.decls in
|
|
let ir = Flan.Emit.program ~dev:true p in
|
|
(List.length l.Flan.Load.decls, String.length ir)
|
|
|
|
(* Touched only so the linker keeps the modules a merged dev build would carry.
|
|
Nothing here is called for its effect. *)
|
|
let footprint () =
|
|
String.concat ","
|
|
[ Flan.Build.clang;
|
|
string_of_int (String.length Flan.Shim.header);
|
|
string_of_int (String.length Flan.Runtime_src.source);
|
|
string_of_int (String.length Flan.Runtime_src.dev_source);
|
|
string_of_int (List.length Flan.Session.externs);
|
|
string_of_int (Flan.Render.max_span);
|
|
string_of_int (String.length (Flan.Wire.ints [ 1; 2 ])) ]
|
|
|
|
let () =
|
|
Callback.register "spike_compile" (fun (file : string) ->
|
|
match compile file with
|
|
| d, n -> Printf.sprintf "%s: %d decls, %d bytes of LLVM IR" file d n
|
|
| exception e -> Printf.sprintf "FAILED: %s" (Printexc.to_string e));
|
|
Callback.register "spike_footprint" footprint;
|
|
(* Dev.start and Cimport are never run here, but naming them keeps the socket
|
|
server and the C importer in the link -- a dev build pays for them. *)
|
|
Callback.register "spike_unused" (fun () ->
|
|
ignore (Flan.Dev.start : ?debug:bool -> file:string -> sock:string -> unit -> unit);
|
|
ignore (Flan.Cimport.decl_source : Flan.Ast.decl -> string);
|
|
"ok")
|