2016-02-18 07:11:59 -08:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* OCaml *)
|
|
|
|
(* *)
|
|
|
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
|
|
|
(* *)
|
|
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
|
|
|
(* en Automatique. *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
1995-08-09 08:06:35 -07:00
|
|
|
|
1995-07-02 09:41:48 -07:00
|
|
|
(* From lambda to assembly code *)
|
|
|
|
|
2016-01-26 07:43:24 -08:00
|
|
|
[@@@ocaml.warning "+a-4-9-40-41-42"]
|
|
|
|
|
2000-04-21 01:13:22 -07:00
|
|
|
open Format
|
1996-02-15 08:19:09 -08:00
|
|
|
open Config
|
1995-07-02 09:41:48 -07:00
|
|
|
open Clflags
|
|
|
|
open Misc
|
|
|
|
open Cmm
|
|
|
|
|
2020-10-13 06:07:13 -07:00
|
|
|
type error =
|
|
|
|
| Assembler_error of string
|
|
|
|
| Mismatched_for_pack of string option
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
exception Error of error
|
|
|
|
|
2018-07-15 12:01:30 -07:00
|
|
|
let liveness phrase = Liveness.fundecl phrase; phrase
|
1995-08-13 02:31:50 -07:00
|
|
|
|
2012-01-20 06:23:34 -08:00
|
|
|
let dump_if ppf flag message phrase =
|
|
|
|
if !flag then Printmach.phase message ppf phrase
|
1998-11-12 08:53:20 -08:00
|
|
|
|
2012-01-20 06:23:34 -08:00
|
|
|
let pass_dump_if ppf flag message phrase =
|
|
|
|
dump_if ppf flag message phrase; phrase
|
1995-08-13 02:31:50 -07:00
|
|
|
|
2012-01-20 06:23:34 -08:00
|
|
|
let pass_dump_linear_if ppf flag message phrase =
|
|
|
|
if !flag then fprintf ppf "*** %s@.%a@." message Printlinear.fundecl phrase;
|
1995-08-13 02:31:50 -07:00
|
|
|
phrase
|
|
|
|
|
2020-10-13 06:07:13 -07:00
|
|
|
let start_from_emit = ref true
|
|
|
|
|
2020-10-07 03:32:40 -07:00
|
|
|
let should_save_before_emit () =
|
2020-10-13 06:07:13 -07:00
|
|
|
should_save_ir_after Compiler_pass.Scheduling && (not !start_from_emit)
|
2020-10-07 03:32:40 -07:00
|
|
|
|
|
|
|
let linear_unit_info =
|
|
|
|
{ Linear_format.unit_name = "";
|
|
|
|
items = [];
|
2020-10-13 06:07:13 -07:00
|
|
|
for_pack = None;
|
2020-10-07 03:32:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let reset () =
|
2020-10-13 06:07:13 -07:00
|
|
|
start_from_emit := false;
|
2020-10-07 03:32:40 -07:00
|
|
|
if should_save_before_emit () then begin
|
|
|
|
linear_unit_info.unit_name <- Compilenv.current_unit_name ();
|
|
|
|
linear_unit_info.items <- [];
|
2020-10-13 06:07:13 -07:00
|
|
|
linear_unit_info.for_pack <- !Clflags.for_package;
|
2020-10-07 03:32:40 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
let save_data dl =
|
|
|
|
if should_save_before_emit () then begin
|
|
|
|
linear_unit_info.items <- Linear_format.(Data dl) :: linear_unit_info.items
|
|
|
|
end;
|
|
|
|
dl
|
|
|
|
|
|
|
|
let save_linear f =
|
|
|
|
if should_save_before_emit () then begin
|
|
|
|
linear_unit_info.items <- Linear_format.(Func f) :: linear_unit_info.items
|
|
|
|
end;
|
|
|
|
f
|
|
|
|
|
2020-10-13 06:07:13 -07:00
|
|
|
let write_linear prefix =
|
2020-10-07 03:32:40 -07:00
|
|
|
if should_save_before_emit () then begin
|
2020-10-13 06:07:13 -07:00
|
|
|
let filename = Compiler_pass.(to_output_filename Scheduling ~prefix) in
|
2020-10-07 03:32:40 -07:00
|
|
|
linear_unit_info.items <- List.rev linear_unit_info.items;
|
|
|
|
Linear_format.save filename linear_unit_info
|
|
|
|
end
|
|
|
|
|
2019-08-26 09:12:14 -07:00
|
|
|
let should_emit () =
|
|
|
|
not (should_stop_after Compiler_pass.Scheduling)
|
|
|
|
|
|
|
|
let if_emit_do f x = if should_emit () then f x else ()
|
|
|
|
let emit_begin_assembly = if_emit_do Emit.begin_assembly
|
|
|
|
let emit_end_assembly = if_emit_do Emit.end_assembly
|
|
|
|
let emit_data = if_emit_do Emit.data
|
|
|
|
let emit_fundecl =
|
|
|
|
if_emit_do
|
|
|
|
(Profile.record ~accumulate:true "emit" Emit.fundecl)
|
|
|
|
|
2018-07-27 00:51:53 -07:00
|
|
|
let rec regalloc ~ppf_dump round fd =
|
1996-09-26 10:13:34 -07:00
|
|
|
if round > 50 then
|
1996-09-11 08:59:11 -07:00
|
|
|
fatal_error(fd.Mach.fun_name ^
|
|
|
|
": function too complex, cannot complete register allocation");
|
2018-07-27 00:51:53 -07:00
|
|
|
dump_if ppf_dump dump_live "Liveness analysis" fd;
|
2019-09-11 10:38:09 -07:00
|
|
|
let num_stack_slots =
|
|
|
|
if !use_linscan then begin
|
|
|
|
(* Linear Scan *)
|
|
|
|
Interval.build_intervals fd;
|
|
|
|
if !dump_interval then Printmach.intervals ppf_dump ();
|
|
|
|
Linscan.allocate_registers()
|
|
|
|
end else begin
|
|
|
|
(* Graph Coloring *)
|
|
|
|
Interf.build_graph fd;
|
|
|
|
if !dump_interf then Printmach.interferences ppf_dump ();
|
|
|
|
if !dump_prefer then Printmach.preferences ppf_dump ();
|
|
|
|
Coloring.allocate_registers()
|
|
|
|
end
|
|
|
|
in
|
2018-07-27 00:51:53 -07:00
|
|
|
dump_if ppf_dump dump_regalloc "After register allocation" fd;
|
2019-09-11 10:38:09 -07:00
|
|
|
let (newfd, redo_regalloc) = Reload.fundecl fd num_stack_slots in
|
2018-07-27 00:51:53 -07:00
|
|
|
dump_if ppf_dump dump_reload "After insertion of reloading code" newfd;
|
2000-04-21 01:13:22 -07:00
|
|
|
if redo_regalloc then begin
|
2018-07-27 00:51:53 -07:00
|
|
|
Reg.reinit(); Liveness.fundecl newfd; regalloc ~ppf_dump (round + 1) newfd
|
2000-04-21 01:13:22 -07:00
|
|
|
end else newfd
|
1995-07-02 09:41:48 -07:00
|
|
|
|
1995-08-13 02:31:50 -07:00
|
|
|
let (++) x f = f x
|
|
|
|
|
2018-07-27 00:51:53 -07:00
|
|
|
let compile_fundecl ~ppf_dump fd_cmm =
|
2013-06-03 11:03:59 -07:00
|
|
|
Proc.init ();
|
1995-07-02 09:41:48 -07:00
|
|
|
Reg.reset();
|
1995-08-13 02:31:50 -07:00
|
|
|
fd_cmm
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "selection" Selection.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_selection "After instruction selection"
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "comballoc" Comballoc.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_combine "After allocation combining"
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "cse" CSE.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_cse "After CSE"
|
2018-07-15 12:01:30 -07:00
|
|
|
++ Profile.record ~accumulate:true "liveness" liveness
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "deadcode" Deadcode.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_live "Liveness analysis"
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "spill" Spill.fundecl
|
2018-07-15 12:01:30 -07:00
|
|
|
++ Profile.record ~accumulate:true "liveness" liveness
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_spill "After spilling"
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "split" Split.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_if ppf_dump dump_split "After live range splitting"
|
2018-07-15 12:01:30 -07:00
|
|
|
++ Profile.record ~accumulate:true "liveness" liveness
|
2018-07-27 00:51:53 -07:00
|
|
|
++ Profile.record ~accumulate:true "regalloc" (regalloc ~ppf_dump 1)
|
2017-09-15 03:08:14 -07:00
|
|
|
++ Profile.record ~accumulate:true "available_regs" Available_regs.fundecl
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "linearize" Linearize.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_linear_if ppf_dump dump_linear "Linearized code"
|
2017-06-09 04:29:21 -07:00
|
|
|
++ Profile.record ~accumulate:true "scheduling" Scheduling.fundecl
|
2018-07-27 00:51:53 -07:00
|
|
|
++ pass_dump_linear_if ppf_dump dump_scheduling "After instruction scheduling"
|
2020-10-07 03:32:40 -07:00
|
|
|
++ save_linear
|
2019-08-26 09:12:14 -07:00
|
|
|
++ emit_fundecl
|
1995-07-02 09:41:48 -07:00
|
|
|
|
2020-10-07 03:32:40 -07:00
|
|
|
let compile_data dl =
|
|
|
|
dl
|
|
|
|
++ save_data
|
|
|
|
++ emit_data
|
|
|
|
|
2018-07-27 00:51:53 -07:00
|
|
|
let compile_phrase ~ppf_dump p =
|
|
|
|
if !dump_cmm then fprintf ppf_dump "%a@." Printcmm.phrase p;
|
1995-07-02 09:41:48 -07:00
|
|
|
match p with
|
2018-07-27 00:51:53 -07:00
|
|
|
| Cfunction fd -> compile_fundecl ~ppf_dump fd
|
2020-10-07 03:32:40 -07:00
|
|
|
| Cdata dl -> compile_data dl
|
1995-07-02 09:41:48 -07:00
|
|
|
|
2007-11-06 07:16:56 -08:00
|
|
|
|
|
|
|
(* For the native toplevel: generates generic functions unless
|
|
|
|
they are already available in the process *)
|
2018-07-27 00:51:53 -07:00
|
|
|
let compile_genfuns ~ppf_dump f =
|
2007-11-06 07:16:56 -08:00
|
|
|
List.iter
|
|
|
|
(function
|
|
|
|
| (Cfunction {fun_name = name}) as ph when f name ->
|
2018-07-27 00:51:53 -07:00
|
|
|
compile_phrase ~ppf_dump ph
|
2007-11-06 07:16:56 -08:00
|
|
|
| _ -> ())
|
2019-10-04 08:49:59 -07:00
|
|
|
(Cmm_helpers.generic_functions true [Compilenv.current_unit_infos ()])
|
2007-11-06 07:16:56 -08:00
|
|
|
|
2020-10-07 03:32:40 -07:00
|
|
|
let compile_unit ~output_prefix ~asm_filename ~keep_asm ~obj_filename gen =
|
|
|
|
reset ();
|
2019-08-26 09:12:14 -07:00
|
|
|
let create_asm = should_emit () &&
|
|
|
|
(keep_asm || not !Emitaux.binary_backend_available) in
|
2014-09-03 07:46:53 -07:00
|
|
|
Emitaux.create_asm_file := create_asm;
|
2018-07-25 07:23:07 -07:00
|
|
|
Misc.try_finally
|
2015-12-23 13:40:21 -08:00
|
|
|
~exceptionally:(fun () -> remove_file obj_filename)
|
2018-07-25 07:23:07 -07:00
|
|
|
(fun () ->
|
|
|
|
if create_asm then Emitaux.output_channel := open_out asm_filename;
|
2020-10-07 03:32:40 -07:00
|
|
|
Misc.try_finally
|
|
|
|
(fun () ->
|
|
|
|
gen ();
|
|
|
|
write_linear output_prefix)
|
2018-07-25 07:23:07 -07:00
|
|
|
~always:(fun () ->
|
|
|
|
if create_asm then close_out !Emitaux.output_channel)
|
|
|
|
~exceptionally:(fun () ->
|
|
|
|
if create_asm && not keep_asm then remove_file asm_filename);
|
2019-08-26 09:12:14 -07:00
|
|
|
if should_emit () then begin
|
|
|
|
let assemble_result =
|
|
|
|
Profile.record "assemble"
|
|
|
|
(Proc.assemble_file asm_filename) obj_filename
|
|
|
|
in
|
|
|
|
if assemble_result <> 0
|
|
|
|
then raise(Error(Assembler_error asm_filename));
|
|
|
|
end;
|
2018-07-25 07:23:07 -07:00
|
|
|
if create_asm && not keep_asm then remove_file asm_filename
|
|
|
|
)
|
2014-09-03 05:48:13 -07:00
|
|
|
|
2018-07-27 00:51:53 -07:00
|
|
|
let end_gen_implementation ?toplevel ~ppf_dump
|
2019-05-10 06:11:22 -07:00
|
|
|
(clambda : Clambda.with_constants) =
|
2019-08-26 09:12:14 -07:00
|
|
|
emit_begin_assembly ();
|
2016-01-26 07:43:24 -08:00
|
|
|
clambda
|
2019-05-10 06:11:22 -07:00
|
|
|
++ Profile.record "cmm" Cmmgen.compunit
|
2018-07-27 00:51:53 -07:00
|
|
|
++ Profile.record "compile_phrases" (List.iter (compile_phrase ~ppf_dump))
|
2015-11-23 09:31:24 -08:00
|
|
|
++ (fun () -> ());
|
2018-07-27 00:51:53 -07:00
|
|
|
(match toplevel with None -> () | Some f -> compile_genfuns ~ppf_dump f);
|
2014-09-03 05:48:13 -07:00
|
|
|
(* We add explicit references to external primitive symbols. This
|
|
|
|
is to ensure that the object files that define these symbols,
|
|
|
|
when part of a C library, won't be discarded by the linker.
|
|
|
|
This is important if a module that uses such a symbol is later
|
|
|
|
dynlinked. *)
|
2018-07-27 00:51:53 -07:00
|
|
|
compile_phrase ~ppf_dump
|
2019-10-04 08:49:59 -07:00
|
|
|
(Cmm_helpers.reference_symbols
|
2019-05-10 06:11:22 -07:00
|
|
|
(List.filter_map (fun prim ->
|
|
|
|
if not (Primitive.native_name_is_external prim) then None
|
|
|
|
else Some (Primitive.native_name prim))
|
|
|
|
!Translmod.primitive_declarations));
|
2019-08-26 09:12:14 -07:00
|
|
|
emit_end_assembly ()
|
2014-09-03 05:48:13 -07:00
|
|
|
|
2019-05-10 06:11:22 -07:00
|
|
|
type middle_end =
|
|
|
|
backend:(module Backend_intf.S)
|
|
|
|
-> filename:string
|
|
|
|
-> prefixname:string
|
|
|
|
-> ppf_dump:Format.formatter
|
|
|
|
-> Lambda.program
|
|
|
|
-> Clambda.with_constants
|
2016-01-26 07:43:24 -08:00
|
|
|
|
2020-10-13 06:07:13 -07:00
|
|
|
let asm_filename output_prefix =
|
2015-02-26 06:03:58 -08:00
|
|
|
if !keep_asm_file || !Emitaux.binary_backend_available
|
2020-10-13 06:07:13 -07:00
|
|
|
then output_prefix ^ ext_asm
|
2014-09-03 05:48:13 -07:00
|
|
|
else Filename.temp_file "camlasm" ext_asm
|
2020-10-13 06:07:13 -07:00
|
|
|
|
|
|
|
let compile_implementation ?toplevel ~backend ~filename ~prefixname ~middle_end
|
|
|
|
~ppf_dump (program : Lambda.program) =
|
|
|
|
compile_unit ~output_prefix:prefixname
|
|
|
|
~asm_filename:(asm_filename prefixname) ~keep_asm:!keep_asm_file
|
2020-10-07 03:32:40 -07:00
|
|
|
~obj_filename:(prefixname ^ ext_obj)
|
2019-05-10 06:11:22 -07:00
|
|
|
(fun () ->
|
|
|
|
Ident.Set.iter Compilenv.require_global program.required_globals;
|
|
|
|
let clambda_with_constants =
|
|
|
|
middle_end ~backend ~filename ~prefixname ~ppf_dump program
|
|
|
|
in
|
|
|
|
end_gen_implementation ?toplevel ~ppf_dump clambda_with_constants)
|
1995-07-02 09:41:48 -07:00
|
|
|
|
2020-10-13 06:07:13 -07:00
|
|
|
let linear_gen_implementation filename =
|
|
|
|
let open Linear_format in
|
|
|
|
let linear_unit_info, _ = restore filename in
|
|
|
|
(match !Clflags.for_package, linear_unit_info.for_pack with
|
|
|
|
| None, None -> ()
|
|
|
|
| Some expected, Some saved when String.equal expected saved -> ()
|
|
|
|
| _, saved -> raise(Error(Mismatched_for_pack saved)));
|
|
|
|
let emit_item = function
|
|
|
|
| Data dl -> emit_data dl
|
|
|
|
| Func f -> emit_fundecl f
|
|
|
|
in
|
|
|
|
start_from_emit := true;
|
|
|
|
emit_begin_assembly ();
|
|
|
|
Profile.record "Emit" (List.iter emit_item) linear_unit_info.items;
|
|
|
|
emit_end_assembly ()
|
|
|
|
|
|
|
|
let compile_implementation_linear output_prefix ~progname =
|
|
|
|
compile_unit ~output_prefix
|
|
|
|
~asm_filename:(asm_filename output_prefix) ~keep_asm:!keep_asm_file
|
|
|
|
~obj_filename:(output_prefix ^ ext_obj)
|
|
|
|
(fun () ->
|
|
|
|
linear_gen_implementation progname)
|
|
|
|
|
1995-07-02 09:41:48 -07:00
|
|
|
(* Error report *)
|
|
|
|
|
2000-04-21 01:13:22 -07:00
|
|
|
let report_error ppf = function
|
|
|
|
| Assembler_error file ->
|
2012-03-07 09:50:17 -08:00
|
|
|
fprintf ppf "Assembler error, input left in file %a"
|
|
|
|
Location.print_filename file
|
2020-10-13 06:07:13 -07:00
|
|
|
| Mismatched_for_pack saved ->
|
|
|
|
let msg = function
|
|
|
|
| None -> "without -for-pack"
|
|
|
|
| Some s -> "with -for-pack "^s
|
|
|
|
in
|
|
|
|
fprintf ppf
|
|
|
|
"This input file cannot be compiled %s: it was generated %s."
|
|
|
|
(msg !Clflags.for_package) (msg saved)
|
2013-09-12 08:50:47 -07:00
|
|
|
|
|
|
|
let () =
|
|
|
|
Location.register_error_of_exn
|
|
|
|
(function
|
|
|
|
| Error err -> Some (Location.error_of_printer_file report_error err)
|
|
|
|
| _ -> None
|
|
|
|
)
|