Most of these are undocumented ways to get extra information from the compiler about a program:
$ ocamlc -dlambda file.ml # lambda representation, dumped to stderr $ ocamlc -dparsetree file.ml # parsed AST, to stderr $ ocamlc -dtypedtree file.ml # parsed AST, to stderr $ ocamlopt -dcmm file.ml # C-- IC, to stderr
For example, the C-- emitted from the following
let no_allocation _ = 'a'
let has_allocation n = n, n
let maybe_throws "123" = 'a'
seems to confirm the names of those functions:
(function{o4.ml:1,18-25} camlO4.no_allocation_11 (param/296: val) 195)
(function{o4.ml:3,19-27} camlO4.has_allocation_18 (n/290: val)
(alloc{o4.ml:3,23-27} 2048 n/290 n/290))
(function{o4.ml:5,17-28} camlO4.maybe_throws_25 (param/292: val)
(catch
(let size/299 (>>u (load int (+a param/292 -8)) 10)
(if (>= size/299 2) (exit 4)
(let cell/298 (load_mut int (+a param/292 0))
(if (== cell/298 288230376155066929) 195 (exit 4)))))
with(4) (raise_notrace{o4.ml:5,17-22} "camlO4__Pmakeblock_46")))
NB. 195 = Char.code 'a' lsl 1 lor 1
And documented ways:
$ ocamlopt -S file.ml # save asm in file.s $ ocamlc -annot file.ml # save inferred types in file.annot $ ocamlobjinfo file.bc # bytecode information
For very convenient profiling,
$ ocamlcp file.ml # or ocamloptp $ ./a.out $ ocamlprof file.ml
ocamlprof reprints the file, retaining code, formatting, comments, while adding its own comments to show code path count. For example, the numbers in the following code were added by ocamlprof to show how this code handled Advent of Code 2005, day 5:
let ranges =
let rec aux acc =
(* 175 *) match read_line () with
| "" -> (* 1 *) acc
| line -> (* 174 *) aux (Scanf.sscanf line "%d-%d%!" Pair.make :: acc)
in
aux [] |> List.rev
let ids =
let rec aux () =
(* 1001 *) match read_line () with
| line -> (* 1000 *) Scanf.sscanf line "%d" Fun.id :: aux ()
| exception End_of_file -> (* 1 *) []
in
aux ()
Of course with native code, native tools apply:
$ objdump -dwr a.out # dump reconstructed asm $ ldd a.out # show (ld.so mandatory) dynamic object dependencies $ gdb ./a.out $ strings a.out
-dlambda helps reveal the polymorphic comparisons in the following example:
let check_test (a : int array) (b : int array) : int array =
let compare x y = if x < y then 0 else 1 in (* calls caml_lessthan *)
Array.init (Array.length a) (fun i -> compare a.(i) b.(i))
module Points1 = Set.Make (struct
type t = int * int
let compare = compare (* calls caml_compare *)
end)
module Points2 = Set.Make (struct
type t = int * int
let compare (x1, y1) (x2, y2) =
match Int.compare x1 x2 with
| 0 -> Int.compare y1 y2
| c -> c
end)
AI:
- If a comparator participates in ordering, sorting, maps, or sets, polymorphic compare is almost always wrong.
- “But the type is known” does not imply specialization in OCaml.