(* *********************************************************************) (* *) (* The Compcert verified compiler *) (* *) (* Xavier Leroy, INRIA Paris-Rocquencourt *) (* *) (* Copyright Institut National de Recherche en Informatique et en *) (* Automatique. All rights reserved. This file is distributed *) (* under the terms of the INRIA Non-Commercial License Agreement. *) (* *) (* *********************************************************************) open Printf open Commandline open Clflags open CommonOptions open Timing open Driveraux open Frontend open Assembler open Linker open Diagnostics (* Name used for version string etc. *) let tool_name = "C verified compiler" (* Optional sdump suffix *) let sdump_suffix = ref ".json" let nolink () = !option_c || !option_S || !option_E || !option_interp let object_filename sourcename suff = if nolink () then output_filename ~final: !option_c sourcename suff ".o" else tmp_file ".o" (* From CompCert C AST to asm *) let compile_c_file sourcename ifile ofile = (* Prepare to dump Clight, RTL, etc, if requested *) let set_dest dst opt ext = dst := if !opt then Some (output_filename sourcename ".c" ext) else None in set_dest Cprint.destination option_dparse ".parsed.c"; set_dest PrintCsyntax.destination option_dcmedium ".compcert.c"; set_dest PrintClight.destination option_dclight ".light.c"; set_dest PrintCminor.destination option_dcminor ".cm"; set_dest PrintRTL.destination option_drtl ".rtl"; set_dest Regalloc.destination_alloctrace option_dalloctrace ".alloctrace"; set_dest PrintLTL.destination option_dltl ".ltl"; set_dest PrintMach.destination option_dmach ".mach"; set_dest AsmToJSON.destination option_sdump !sdump_suffix; (* Parse the ast *) let csyntax = parse_c_file sourcename ifile in (* Convert to Asm *) let asm = match Compiler.apply_partial (Compiler.transf_c_program csyntax) Asmexpand.expand_program with | Errors.OK asm -> asm | Errors.Error msg -> let loc = file_loc sourcename in fatal_error loc "%a" print_error msg in (* Dump Asm in binary and JSON format *) AsmToJSON.print_if asm sourcename; (* Print Asm in text form *) let oc = open_out ofile in PrintAsm.print_program oc asm; close_out oc (* From C source to asm *) let compile_i_file sourcename preproname = if !option_interp then begin Machine.config := Machine.compcert_interpreter !Machine.config; let csyntax = parse_c_file sourcename preproname in Interp.execute csyntax; "" end else if !option_S then begin compile_c_file sourcename preproname (output_filename ~final:true sourcename ".c" ".s"); "" end else begin let asmname = if !option_dasm then output_filename sourcename ".c" ".s" else tmp_file ".s" in compile_c_file sourcename preproname asmname; let objname = object_filename sourcename ".c" in assemble asmname objname; objname end (* Processing of a .c file *) let process_c_file sourcename = ensure_inputfile_exists sourcename; if !option_E then begin preprocess sourcename (output_filename_default "-"); "" end else begin let preproname = if !option_dprepro then output_filename sourcename ".c" ".i" else tmp_file ".i" in preprocess sourcename preproname; compile_i_file sourcename preproname end (* Processing of a .i / .p file (preprocessed C) *) let process_i_file sourcename = ensure_inputfile_exists sourcename; compile_i_file sourcename sourcename (* Processing of .S and .s files *) let process_s_file sourcename = ensure_inputfile_exists sourcename; let objname = object_filename sourcename ".s" in assemble sourcename objname; objname let process_S_file sourcename = ensure_inputfile_exists sourcename; if !option_E then begin preprocess sourcename (output_filename_default "-"); "" end else begin let preproname = tmp_file ".s" in preprocess sourcename preproname; let objname = object_filename sourcename ".S" in assemble preproname objname; objname end (* Processing of .h files *) let process_h_file sourcename = if !option_E then begin ensure_inputfile_exists sourcename; preprocess sourcename (output_filename_default "-"); "" end else fatal_error no_loc "input file %s ignored (not in -E mode)\n" sourcename let target_help = if Configuration.arch = "arm" && Configuration.model <> "armv6" then {|Target processor options: -mthumb Use Thumb2 instruction encoding -marm Use classic ARM instruction encoding |} else "" let toolchain_help = if not Configuration.gnu_toolchain then begin {|Toolchain options: -t tof:env Select target processor for the diab toolchain |} end else "" let usage_string = version_string tool_name ^ {|Usage: ccomp [options] Recognized source files: .c C source file .i or .p C source file that should not be preprocessed .s Assembly file .S or .sx Assembly file that must be preprocessed .o Object file .a Library file Processing options: -c Compile to object file only (no linking), result in .o -E Preprocess only, send result to standard output -S Compile to assembler only, save result in .s -o Generate output in |} ^ prepro_help ^ language_support_help ^ DebugInit.debugging_help ^ {|Optimization options: (use -fno- to turn off -f) -O Optimize the compiled code [on by default] -O0 Do not optimize the compiled code -O1 Perform all optimization passes except scheduling -O2 -O3 Synonymous for -O -Os Optimize for code size in preference to code speed -Obranchless Optimize to generate fewer conditional branches; try to produce branch-free instruction sequences as much as possible -finline-auto-threshold n Inline functions under size n -ftailcalls Optimize function calls in tail position [on] -fconst-prop Perform global constant propagation [on] -ffloat-const-prop Control constant propagation of floats (=0: none, =1: limited, =2: full; default is full) -fcse Perform common subexpression elimination [on] -fcse2 Perform inter-loop common subexpression elimination [off] -fcse3 Perform inter-loop common subexpression elimination [on] -fcse3-alias-analysis Perform inter-loop common subexpression elimination with alias analysis [on] -fcse3-across-calls Propagate CSE3 information across function calls [off] -fcse3-across-merges Propagate CSE3 information across control-flow merges [on] -fcse3-glb Refine CSE3 information using greatest lower bounds [on] -fcse3-trivial-ops Replace trivial operations as well using CSE3 [off] -fcse3-refine Refine CSE3 invariants by descending iteration [on] -fcse3-conditions Remove redundant conditions using CSE3 [on] -fmove-loop-invariants Perform loop-invariant code motion [off] -fredundancy Perform redundancy elimination [on] -mtune= Type of CPU (for scheduling on some architectures) -fprepass Perform prepass scheduling (only on some architectures) [on] -fprepass= Perform postpass scheduling with the specified optimization [list] (=list: list scheduling, =revlist: reverse list scheduling, =regpres: list scheduling aware of register pressure, =regpres_bis: variant of regpres, =zigzag: zigzag scheduling, =ilp: ILP, =greedy: just packing bundles) -regpres-threshold n With `-fprepass= regpres`, set threshold value for number of free registers before trying to decrease register pressure -fregpres-wait-window When register pressure is high, use a 5-cycle waiting window instead of scheduling short paths first (default no) -fpostpass Perform postpass scheduling (only for K1 architecture) [on] -fpostpass= Perform postpass scheduling with the specified optimization [list] (=list: list scheduling, =ilp: ILP, =greedy: just packing bundles) -fpredict Insert static branch prediction information [on] Also swaps ifso/ifnot branches accordingly at RTL level -ftailduplicate n Perform tail duplication for RTL code blocks of size n (not counting Inops) [0] -ftracelinearize Uses branch prediction information to improve the Linearize [on] -funrollsingle n Unrolls a single iteration of innermost loops of size n (not counting Inops) [0] -funrollbody n Unrolls once the body of innermost loops of size n (not counting Inops) [0] -flooprotate n Duplicates the header (condition computation part) of innermost loops to perform a loop rotate [0] Doesn't duplicate if the size of that header is strictly greater than n -fforward-moves Forward moves after CSE -finline Perform inlining of functions [on] -finline-functions-called-once Integrate functions only required by their single caller [on] -fif-conversion Perform if-conversion (generation of conditional moves) [on] Code generation options: (use -fno- to turn off -f) -ffpu Use FP registers for some integer operations [on] -fsmall-data Set maximal size for allocation in small data area -fsmall-const Set maximal size for allocation in small constant area -falign-functions Set alignment (in bytes) of function entry points -falign-branch-targets Set alignment (in bytes) of branch targets -falign-cond-branches Set alignment (in bytes) of conditional branches -fcommon Put uninitialized globals in the common section [on] -fprofile-arcs Profile branches [off]. -fprofile-use= filename Use profiling information in filename -fbranch-probabilities Use profiling information (if available) for branches [on] |} ^ target_help ^ toolchain_help ^ assembler_help ^ linker_help ^ {|Tracing options: -dprepro Save C file after preprocessing in .i -dparse Save C file after parsing and elaboration in .parsed.c -dc Save generated Compcert C in .compcert.c -dclight Save generated Clight in .light.c -dcminor Save generated Cminor in .cm -drtl Save RTL at various optimization points in .rtl. -dltl Save LTL after register allocation in .ltl -dmach Save generated Mach code in .mach -dasm Save generated assembly in .s -dall Save all generated intermediate files in . -sdump Save info for post-linking validation in .json |} ^ general_help ^ warning_help ^ {|Interpreter mode: -interp Execute given .c files using the reference interpreter -quiet Suppress diagnostic messages for the interpreter -trace Have the interpreter produce a detailed trace of reductions -random Randomize execution order -all Simulate all possible execution orders -main Start executing at function instead of main() |} let print_usage_and_exit () = printf "%s" usage_string; exit 0 let dump_mnemonics destfile = let oc = open_out_bin destfile in let pp = Format.formatter_of_out_channel oc in AsmToJSON.pp_mnemonics pp; Format.pp_print_flush pp (); close_out oc; exit 0 let optimization_options = [ option_ftailcalls; option_fifconversion; option_fconstprop; option_fcse; option_fcse2; option_fcse3; option_fpredict; option_ftracelinearize; option_fpostpass; option_fredundancy; option_finline; option_finline_functions_called_once; ] let set_all opts () = List.iter (fun r -> r := true) opts let unset_all opts () = List.iter (fun r -> r := false) opts let num_source_files = ref 0 let num_input_files = ref 0 let cmdline_actions = let f_opt name ref = [Exact("-f" ^ name), Set ref; Exact("-fno-" ^ name), Unset ref] in let f_opt_str name default ref strref = [Exact("-f" ^ name ^ "="), String (fun s -> (strref := (if s == "" then default else s)); ref := true) ] in let f_str name strref default = [Exact("-f" ^ name ^ "="), String (fun s -> (strref := (if s == "" then default else s))) ] in let check_align n = if n <= 0 || ((n land (n - 1)) <> 0) then error no_loc "requested alignment %d is not a power of 2" n in [ (* Getting help *) Exact "-help", Unit print_usage_and_exit; Exact "--help", Unit print_usage_and_exit;] (* Getting version info *) @ version_options tool_name @ (* Enforcing CompCert build numbers for QSKs and mnemonics dump *) (if Version.buildnr <> "" then [Exact "-dump-mnemonics", String dump_mnemonics;] else []) @ (* Processing options *) [ Exact "-c", Set option_c; Exact "-E", Set option_E; Exact "-S", Set option_S; Exact "-o", String(fun s -> option_o := Some s); Prefix "-o", Self (fun s -> let s = String.sub s 2 ((String.length s) - 2) in option_o := Some s);] (* Preprocessing options *) @ prepro_actions @ (* Language support options *) language_support_options (* Debugging options *) @ DebugInit.debugging_actions @ (* Code generation options -- more below *) [ Exact "-O0", Unit (unset_all optimization_options); Exact "-O", Unit (set_all optimization_options); _Regexp "-O1", Self (fun _ -> set_all optimization_options (); option_fpostpass := false); _Regexp "-O[123]$", Unit (set_all optimization_options); Exact "-Os", Set option_Osize; Exact "-Obranchless", Set option_Obranchless; Exact "-fprofile-use=", String (fun s -> Profilingaux.load_profiling_info s); Exact "-finline-auto-threshold", Integer (fun n -> option_inline_auto_threshold := n); Exact "-debug-compcert", Integer (fun n -> option_debug_compcert := n); Exact "-regpres-threshold", Integer (fun n -> option_regpres_threshold := n); Exact "-fsmall-data", Integer(fun n -> option_small_data := n); Exact "-fsmall-const", Integer(fun n -> option_small_const := n); Exact "-ffloat-const-prop", Integer(fun n -> option_ffloatconstprop := n); Exact "-falign-functions", Integer(fun n -> check_align n; option_falignfunctions := Some n); Exact "-falign-branch-targets", Integer(fun n -> check_align n; option_falignbranchtargets := n); Exact "-falign-cond-branches", Integer(fun n -> check_align n; option_faligncondbranchs := n);] @ f_opt "common" option_fcommon @ (* Target processor options *) (if Configuration.arch = "arm" then if Configuration.model = "armv6" then [ Exact "-marm", Ignore ] (* Thumb needs ARMv6T2 or ARMv7 *) else [ Exact "-mthumb", Set option_mthumb; Exact "-marm", Unset option_mthumb; ] else []) @ (* Toolchain options *) (if not Configuration.gnu_toolchain then [Exact "-t", String (fun arg -> push_linker_arg "-t"; push_linker_arg arg; prepro_options := arg :: "-t" :: !prepro_options; assembler_options := arg :: "-t" :: !assembler_options;)] else []) @ (* Assembling options *) assembler_actions @ (* Linking options *) linker_actions @ (* Tracing options *) [ Exact "-dprepro", Set option_dprepro; Exact "-dparse", Set option_dparse; Exact "-dc", Set option_dcmedium; Exact "-dclight", Set option_dclight; Exact "-dcminor", Set option_dcminor; Exact "-drtl", Set option_drtl; Exact "-dltl", Set option_dltl; Exact "-dalloctrace", Set option_dalloctrace; Exact "-dmach", Set option_dmach; Exact "-dasm", Set option_dasm; Exact "-dall", Self (fun _ -> option_dprepro := true; option_dparse := true; option_dcmedium := true; option_dclight := true; option_dcminor := true; option_drtl := true; option_dltl := true; option_dalloctrace := true; option_dmach := true; option_dasm := true); Exact "-sdump", Set option_sdump; Exact "-sdump-suffix", String (fun s -> option_sdump := true; sdump_suffix:= s); Exact "-sdump-folder", String (fun s -> AsmToJSON.sdump_folder := s);] @ (* General options *) general_options @ (* Diagnostic options *) warning_options @ (* Interpreter mode *) [ Exact "-interp", Set option_interp; Exact "-quiet", Unit (fun () -> Interp.trace := 0); Exact "-trace", Unit (fun () -> Interp.trace := 2); Exact "-random", Unit (fun () -> Interp.mode := Interp.Random); Exact "-all", Unit (fun () -> Interp.mode := Interp.All); Exact "-main", String (fun s -> main_function_name := s) ] (* Optimization options *) (* -f options: come in -f and -fno- variants *) @ f_opt "tailcalls" option_ftailcalls @ f_opt "if-conversion" option_fifconversion @ f_opt "const-prop" option_fconstprop @ f_opt "cse" option_fcse @ f_opt "cse2" option_fcse2 @ f_opt "cse3" option_fcse3 @ f_opt "cse3-alias-analysis" option_fcse3_alias_analysis @ f_opt "cse3-across-calls" option_fcse3_across_calls @ f_opt "cse3-across-merges" option_fcse3_across_merges @ f_opt "cse3-glb" option_fcse3_glb @ f_opt "cse3-trivial-ops" option_fcse3_trivial_ops @ f_opt "cse3-refine" option_fcse3_refine @ f_opt "cse3-conditions" option_fcse3_conditions @ f_opt "move-loop-invariants" option_fmove_loop_invariants @ f_opt "redundancy" option_fredundancy @ [ Exact "-mtune", String (fun s -> option_mtune := s) ] @ f_opt "prepass" option_fprepass @ f_opt "regpres-wait-window" option_regpres_wait_window @ f_opt "postpass" option_fpostpass @ [ Exact "-ftailduplicate", Integer (fun n -> option_ftailduplicate := n) ] @ f_opt "predict" option_fpredict @ [ Exact "-funrollsingle", Integer (fun n -> option_funrollsingle := n) ] @ [ Exact "-funrollbody", Integer (fun n -> option_funrollbody := n) ] @ [ Exact "-flooprotate", Integer (fun n -> option_flooprotate := n) ] @ f_opt "tracelinearize" option_ftracelinearize @ f_opt_str "prepass" "regpress" option_fprepass option_fprepass_sched @ f_opt_str "postpass" "list" option_fpostpass option_fpostpass_sched @ f_opt "inline" option_finline @ f_opt "inline-functions-called-once" option_finline_functions_called_once @ f_opt "globaladdrtmp" option_fglobaladdrtmp @ f_opt "globaladdroffset" option_fglobaladdroffset @ f_opt "xsaddr" option_fxsaddr @ f_str "div-i32" option_div_i32 "stsud" @ f_str "div-i64" option_div_i64 "stsud" @ f_opt "addx" option_faddx @ f_opt "madd" option_fmadd @ f_opt "nontrap-loads" option_fnontrap_loads @ f_opt "coalesce-mem" option_fcoalesce_mem @ f_opt "expanse-rtlcond" option_fexpanse_rtlcond @ f_opt "expanse-others" option_fexpanse_others @ f_opt "all-loads-nontrap" option_all_loads_nontrap @ f_opt "forward-moves" option_fforward_moves (* Code generation options *) @ f_opt "profile-arcs" option_profile_arcs @ f_opt "branch-probabilities" option_fbranch_probabilities @ f_opt "fpu" option_ffpu @ f_opt "sse" option_ffpu (* backward compatibility *) @ [ (* Catch options that are not handled *) Prefix "-", Self (fun s -> fatal_error no_loc "Unknown option `%s'" s); (* File arguments *) Suffix ".c", Self (fun s -> push_action process_c_file s; incr num_source_files; incr num_input_files); Suffix ".i", Self (fun s -> push_action process_i_file s; incr num_source_files; incr num_input_files); Suffix ".p", Self (fun s -> push_action process_i_file s; incr num_source_files; incr num_input_files); Suffix ".s", Self (fun s -> push_action process_s_file s; incr num_source_files; incr num_input_files); Suffix ".S", Self (fun s -> push_action process_S_file s; incr num_source_files; incr num_input_files); Suffix ".sx", Self (fun s -> push_action process_S_file s; incr num_source_files; incr num_input_files); Suffix ".o", Self (fun s -> push_linker_arg s; incr num_input_files); Suffix ".a", Self (fun s -> push_linker_arg s; incr num_input_files); (* GCC compatibility: .o.ext files and .so files are also object files *) _Regexp ".*\\.o\\.", Self (fun s -> push_linker_arg s; incr num_input_files); Suffix ".so", Self (fun s -> push_linker_arg s; incr num_input_files); (* GCC compatibility: .h files can be preprocessed with -E *) Suffix ".h", Self (fun s -> push_action process_h_file s; incr num_source_files; incr num_input_files); ] let _ = try Gc.set { (Gc.get()) with Gc.minor_heap_size = 524288; (* 512k *) Gc.major_heap_increment = 4194304 (* 4M *) }; Printexc.record_backtrace true; Frontend.init (); parse_cmdline cmdline_actions; DebugInit.init (); (* Initialize the debug functions *) if nolink () && !option_o <> None && !num_source_files >= 2 then fatal_error no_loc "ambiguous '-o' option (multiple source files)"; if !num_input_files = 0 then fatal_error no_loc "no input file"; if not !option_interp && !main_function_name <> "main" then fatal_error no_loc "option '-main' requires option '-interp'"; let linker_args = time "Total compilation time" perform_actions () in if not (nolink ()) && linker_args <> [] then begin linker (output_filename_default "a.out") linker_args end; check_errors () with | Sys_error msg | CmdError msg -> error no_loc "%s" msg; exit 2 | Abort -> error_summary (); exit 2 | e -> crash e