You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
3.5 KiB

3 years ago
#!/usr/bin/env ocaml
;;
#use "topfind"
;;
#thread
;;
#require "core_kernel,shexp.process"
open Core_kernel
module Queue = Hash_queue.Make (String)
module Process = Shexp_process
open Process.Infix
let ( let* ) = ( >>= )
let ( and* ) = Process.fork
let ( let+ ) = ( >>| )
let ( and+ ) = Process.fork
(* C name of entry point function *)
let entry_point = Sys.argv.(1)
(* filename of executable to be linked to bitcode *)
let input = Sys.argv.(2)
(* name of input *)
let basename = Filename.basename input
(* dir containing input exe and objects *)
let input_dir = Filename.dirname input
(* dir where output should be placed *)
let output_dir = Sys.getcwd ()
(* env var used by gllvm to identify dir containing llvm exes e.g. clang *)
let llvm_compiler_path = Sys.getenv_opt "LLVM_COMPILER_PATH"
(* prefix name with LLVM_COMPILER_PATH *)
let llvm_compiler name =
match llvm_compiler_path with
| Some path -> Filename.concat path name
| None -> name
(* llvm-link exe to use *)
let llvm_link =
llvm_compiler
(Option.value (Sys.getenv_opt "LLVM_LINK_NAME") ~default:"llvm-link")
(* opt exe to use *)
let llvm_opt = llvm_compiler "opt"
(* env var used to identify dir containing gllvm exes e.g. get-bc *)
let gllvm_path = Sys.getenv_opt "GLLVM_PATH"
(* prefix name with GLLVM_PATH *)
let gllvm name =
match gllvm_path with
| Some path -> Filename.concat path name
| None -> name
(* get-bc exe to use *)
let get_bc = gllvm "get-bc"
let calculate_deps () =
(* queue of object names that need to be linked together *)
let needed = Queue.create () in
(* queue of object names whose dependencies need to be calculated *)
let frontier = Queue.create () in
(* enqueue single object, also to frontier if not yet seen *)
let enqueue obj =
match Queue.enqueue_front needed obj obj with
| `Key_already_present -> ()
| `Ok -> Queue.enqueue_back frontier obj obj |> ignore
in
(* query and enqueue dependencies of an object *)
let enqueue_dependencies obj =
Process.run "objdump" ["-p"; obj]
|- Process.iter_lines (fun line ->
( match String.chop_prefix ~prefix:" NEEDED" line with
| None -> ()
| Some obj -> enqueue (String.strip obj) )
|> Process.return )
in
(* dequeue and process until done *)
let rec loop () =
match Queue.dequeue_front frontier with
| Some obj ->
let* () = enqueue_dependencies obj in
loop ()
| None -> Process.return ()
in
enqueue basename ;
let* () = loop () in
Process.return (Queue.keys needed)
let extract_bc_deps needed =
Process.fork_all_unit
(List.map needed ~f:(fun obj ->
Process.stdout_to "/dev/null"
(Process.run get_bc ["-o=" ^ obj ^ ".bc"; obj]) ))
let link_bc_deps needed =
(* an entry in the list depends only on entries before it, so here we
preserve the order and use the `--override` flag to keep the last
definition of multiply-defined symbols *)
Process.run llvm_link
( "-o=-"
::
( match needed with
| obj :: objs ->
(obj ^ ".bc")
:: List.map objs ~f:(fun obj -> "--override=" ^ obj ^ ".bc")
| [] -> [] ) )
let remove_dead_code =
Process.run llvm_opt
[ "-o=" ^ Filename.concat output_dir (basename ^ ".bc")
; "--internalize-public-api-list=" ^ entry_point
; "--internalize"
; "--globaldce" ]
;;
let context = Process.Context.create ~cwd:(Path input_dir) () in
Process.eval ~context
(let* needed = calculate_deps () in
let* () = extract_bc_deps needed in
link_bc_deps needed |- remove_dead_code)