1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
open Util
open Nativevalues
open Nativecode
open CErrors
(** This file provides facilities to access OCaml compiler and dynamic linker,
used by the native compiler. *)
let get_load_paths =
ref (fun _ -> anomaly (Pp.str "get_load_paths not initialized.") : unit -> string list)
let open_header = ["Nativevalues";
"Nativecode";
"Nativelib";
"Nativeconv"]
let = List.map mk_open open_header
let dft_output_dir = ".coq-native"
let output_dir = ref dft_output_dir
let source_ext = ".native"
let ( / ) = Filename.concat
let my_temp_dir = lazy (CUnix.mktemp_dir "Coq_native" "")
let () = at_exit (fun () ->
if not (keep_debug_files ()) && Lazy.is_val my_temp_dir then
try
let d = Lazy.force my_temp_dir in
Array.iter (fun f -> Sys.remove (Filename.concat d f)) (Sys.readdir d);
Unix.rmdir d
with e ->
Feedback.msg_warning
Pp.(str "Native compile: failed to cleanup: " ++
str(Printexc.to_string e) ++ fnl()))
let delay_cleanup_file =
let toclean = ref [] in
let () = at_exit (fun () -> List.iter (fun f -> if Sys.file_exists f then Sys.remove f) !toclean) in
fun f -> if not (keep_debug_files()) then toclean := f :: !toclean
let include_dirs = ref []
let get_include_dirs () =
let base = match !include_dirs with
| [] ->
let env = Boot.Env.init () in
[ Boot.Env.(Path.to_string (native_cmi env "kernel"))
; Boot.Env.(Path.to_string (native_cmi env "library"))
]
| _::_ as l -> l
in
if Lazy.is_val my_temp_dir
then (Lazy.force my_temp_dir) :: base
else base
let load_obj = ref (fun _x -> () : string -> unit)
let rt1 = ref (dummy_value ())
let rt2 = ref (dummy_value ())
let get_ml_filename () =
let temp_dir = Lazy.force my_temp_dir in
let filename = Filename.temp_file ~temp_dir "Coq_native" source_ext in
let prefix = Filename.chop_extension (Filename.basename filename) ^ "." in
filename, prefix
let write_ml_code fn ?(=[]) code =
let = open_header@header in
let ch_out = open_out fn in
let fmt = Format.formatter_of_out_channel ch_out in
List.iter (pp_global fmt) (header@code);
close_out ch_out
let error_native_compiler_failed e =
let msg = match e with
| Inl (Unix.WEXITED 127) -> Pp.(strbrk "The OCaml compiler was not found. Make sure it is installed, together with findlib.")
| Inl (Unix.WEXITED n) ->
Pp.(strbrk "Native compiler exited with status" ++ str" " ++ int n
++ strbrk (if n = 2 then " (in case of stack overflow, increasing stack size (typically with \"ulimit -s\") often helps)" else ""))
| Inl (Unix.WSIGNALED n) -> Pp.(strbrk "Native compiler killed by signal" ++ str" " ++ int n)
| Inl (Unix.WSTOPPED n) -> Pp.(strbrk "Native compiler stopped by signal" ++ str" " ++ int n)
| Inr e -> Pp.(strbrk "Native compiler failed with error: " ++ strbrk (Unix.error_message e))
in
CErrors.user_err msg
let call_compiler ?profile:(profile=false) ml_filename =
let require_load_path = !get_load_paths () in
let install_load_path = List.map (fun dn -> dn / dft_output_dir) require_load_path @ require_load_path in
let include_dirs = List.flatten (List.map (fun x -> ["-I"; x]) (get_include_dirs () @ install_load_path)) in
let f = Filename.chop_extension ml_filename in
let link_filename = f ^ ".cmo" in
let link_filename = Dynlink.adapt_filename link_filename in
let remove f = if Sys.file_exists f then Sys.remove f in
remove link_filename;
remove (f ^ ".cmi");
let initial_args =
if Dynlink.is_native then
["opt"; "-shared"]
else
["ocamlc"; "-c"]
in
let profile_args =
if profile then
["-g"]
else
[]
in
let flambda_args = if Sys.(backend_type = Native) then ["-Oclassic"] else [] in
let args =
initial_args @
profile_args @
flambda_args @
("-o"::link_filename
::"-rectypes"
::"-w"::"a"
::include_dirs) @
["-impl"; ml_filename] in
debug_native_compiler (fun () -> Pp.str (Envars.ocamlfind () ^ " " ^ (String.concat " " args)));
try
let res = CUnix.sys_command (Envars.ocamlfind ()) args in
match res with
| Unix.WEXITED 0 -> link_filename
| Unix.WEXITED _n | Unix.WSIGNALED _n | Unix.WSTOPPED _n ->
error_native_compiler_failed (Inl res)
with Unix.Unix_error (e,_,_) ->
error_native_compiler_failed (Inr e)
let compile fn code ~profile:profile =
write_ml_code fn code;
let r = call_compiler ~profile fn in
delay_cleanup_file fn;
r
type native_library = Nativecode.global list * Nativevalues.symbols
let compile_library (code, symb) fn =
let = mk_library_header symb in
let fn = fn ^ source_ext in
let basename = Filename.basename fn in
let dirname =
if Filename.is_relative !output_dir then Filename.dirname fn / !output_dir else !output_dir in
let () =
try Unix.mkdir dirname 0o755
with Unix.Unix_error (Unix.EEXIST, _, _) -> ()
in
let fn = dirname / basename in
write_ml_code fn ~header code;
let _ = call_compiler fn in
delay_cleanup_file fn
let execute_library ~prefix f upds =
rt1 := dummy_value ();
rt2 := dummy_value ();
if not (Sys.file_exists f) then
CErrors.user_err Pp.(str "Cannot find native compiler file " ++ str f);
if Dynlink.is_native then Dynlink.loadfile f else !load_obj f;
register_native_file prefix;
update_locations upds;
(!rt1, !rt2)
let link_library dirname prefix =
let basename = Dynlink.adapt_filename (prefix ^ "cmo") in
let install_location = dirname / dft_output_dir / basename in
let build_location = dirname / !output_dir / basename in
let f = if Sys.file_exists build_location then build_location else install_location in
try
if Dynlink.is_native then Dynlink.loadfile f else !load_obj f;
register_native_file prefix
with
| Dynlink.Error _ as exn ->
debug_native_compiler (fun () -> CErrors.iprint (Exninfo.capture exn))
let delayed_link = ref []
let link_libraries () =
let delayed = List.rev !delayed_link in
delayed_link := [];
List.iter (fun (dirname, libname) ->
let prefix = mod_uid_of_dirpath libname ^ "." in
if not (Nativecode.is_loaded_native_file prefix) then
link_library dirname prefix
) delayed
let enable_library dirname libname =
delayed_link := (dirname, libname) :: !delayed_link