(** Driver: typed IR → an executable, via LLVM IR text and clang. The release path from plan.org, Compilation: {v flan → typed IR → .ll → clang --target={native,wasm32} v} Not the dev path — that one never invokes the clang driver, because the driver *is* the cost (52ms of the measured 68), and goes llc + ld -shared + dlopen instead for ~16ms. Nothing at milestone 2 needs it yet. *) let clang = try Sys.getenv "FLAN_CLANG" with Not_found -> "clang" let write path contents = let ch = open_out path in output_string ch contents; close_out ch (* One temporary directory per build, so the .ll is findable by name when something is wrong with it. *) let workdir () = let d = Filename.concat (Filename.get_temp_dir_name ()) (Printf.sprintf "flan-%d" (Unix.getpid ())) in (try Unix.mkdir d 0o700 with Unix.Unix_error (Unix.EEXIST, _, _) -> ()); d type opts = { target : string option; (* None is the host; "wasm32-wasi" is the other *) opt : string; keep : bool; (* leave the .ll behind *) } let default = { target = None; opt = "-O2"; keep = false } let executable ?(opts = default) (p : Tast.program) ~out = let dir = workdir () in let ll = Filename.concat dir (Filename.basename out ^ ".ll") in let rt = Filename.concat dir "flan_rt.c" in write ll (Emit.program p); write rt Runtime_src.source; let cmd = String.concat " " ([ Filename.quote clang; opts.opt; "-Wno-override-module" ] @ (match opts.target with None -> [] | Some t -> [ "--target=" ^ t ]) @ [ Filename.quote ll; Filename.quote rt; "-o"; Filename.quote out ]) in let code = Sys.command cmd in if code <> 0 then failwith (Printf.sprintf "%s failed (exit %d); the IR is at %s" clang code ll); if not opts.keep then (try Sys.remove ll; Sys.remove rt with Sys_error _ -> ()); out