Skip to content
Snippets Groups Projects
main.ml 9.09 KiB
open Batteries
open BatList
open Symbols
open Parser
open Ast
open Elang
open Elang_run
open Elang_print
open Elang_gen
open Cfg
open Cfg_run
open Cfg_print
open Cfg_gen
open Cfg_constprop
open Cfg_dead_assign
open Cfg_nop_elim
open Rtl
open Rtl_run
open Rtl_print
open Rtl_gen
open Linear
open Linear_run
open Linear_print
open Linear_gen
open Linear_liveness
open Linear_dse
open Ltl
open Ltl_run
open Ltl_print
open Ltl_gen
open Ltl_debug
open Riscv
open Utils
open Archi
open Report
open Options
open Lexer_generator
open Tokenize

let speclist =
  [
    ("-show-tokens", Arg.String (fun s -> show_tokens := Some s), "Output the list of tokens recognized by the lexer.");
    ("-ast-tree", Arg.String (fun s -> ast_tree := Some s), "Output DOT file for dumping the tree.");
    ("-ast-dump", Arg.Set ast_dump, "Dumps the tree in textual form.");
    ("-e-dump", Arg.String (fun s -> e_dump := Some s), "Output Elang file.");
    ("-e-run", Arg.Set e_run, "Run Elang program.");
    ("-cfg-dump", Arg.String (fun s -> cfg_dump := Some s), "Output CFG file.");
    ("-cfg-run", Arg.Set cfg_run, "Run CFG program.");
    ("-cfg-run-after-cp", Arg.Set cfg_run_after_cp, "Run CFG program after constant propagation.");
    ("-cfg-run-after-dae", Arg.Set cfg_run_after_dae, "Run CFG program after dead assign elimination.");
    ("-cfg-run-after-ne", Arg.Set cfg_run_after_ne, "Run CFG program after nop elimination.");
    ("-rtl-dump", Arg.String (fun s -> rtl_dump := Some s), "Output RTL file.");
    ("-rtl-run", Arg.Set rtl_run, "Run RTL program.");
    ("-linear-dump", Arg.String (fun s -> linear_dump := Some s), "Output Linear file.");
    ("-linear-run", Arg.Set linear_run, "Run Linear program.");
    ("-linear-run-after-dse", Arg.Set linear_run_after_dse, "Run Linear program after dead store elimination.");
    ("-ltl-dump", Arg.String (fun s -> ltl_dump := Some s), "Output LTL file.");
    ("-ltl-run", Arg.Set ltl_run, "Run LTL program.");
    ("-ltl-debug", Arg.Set ltl_debug, "Debug LTL program.");
    ("-riscv-dump", Arg.String (fun s -> riscv_dump := Some s), "Output RISC-V file.");
    ("-riscv-run", Arg.Set riscv_run, "Run RISC-V program.");
    ("-no-dump", Arg.Set no_dump, "Do not dump anything but the .s file");
    ("-no-dot", Arg.Set no_dot, "Do not call dot on CFG dumps (default false)");
    ("-clever-regalloc", Arg.Unit (fun () -> naive_regalloc := false), "Use the graph coloring algorithm for register allocation.");
    ("-naive-regalloc", Arg.Unit (fun () -> naive_regalloc := true),
     "Use the naive algorithm for register allocation (all pseudo-registers go on the stack).");
    ("-no-cfg-constprop", Arg.Set no_cfg_constprop, "Disable CFG constprop");
    ("-no-cfg-dae", Arg.Set no_cfg_dae, "Disable CFG Dead Assign Elimination");
    ("-no-cfg-ne", Arg.Set no_cfg_ne, "Disable CFG Nop Elimination");
    ("-no-linear-dse", Arg.Set no_linear_dse, "Disable Linear Dead Store Elimination");
    ("-rig-dump", Arg.String (fun s -> rig_dump := Some s),
    "Path to output the register interference graph");
    ("-all-run", Arg.Unit (fun () ->
         e_run := true;
         cfg_run := true;
         cfg_run_after_cp := true;
         cfg_run_after_dae := true;
         cfg_run_after_ne := true;
         rtl_run := true;
         linear_run := true;
         linear_run_after_dse := true;
         ltl_run := true;
         riscv_run := true;
       ), "Run all intermediate languages");
    ("-heap", Arg.Set_int heapsize, "Heap size");
    ("-show", Arg.Set show, "Show Results");
    ("-m32", Arg.Unit (fun _ -> Archi.archi := A32), "32bit mode");
    ("-f", Arg.String (fun s -> input_file := Some s), "file to compile");
    ("-alloc-order-ts", Arg.Unit (fun _ -> Options.alloc_order_st := false), "Allocate t regs before s regs");
    ("-json", Arg.String (fun s -> output_json := s), "Output JSON summary");
    ("-nostart", Arg.Set nostart, "Don't output _start code.");
    ("-nostats", Arg.Set nostats, "Don't output stats.");
    ("-nomul", Arg.Unit (fun _ -> has_mul := false), "Target architecture without mul instruction.");
    ("-linux", Arg.Unit (fun _ -> target := Linux), "emit linux syscalls");
    ("-xv6", Arg.Unit (fun _ -> target := Xv6), "emit xv6 syscalls");
    ("--", Arg.Rest (fun p -> params := int_of_string p::!params), "Run parameters.")
  ]

let set_default r v suff =
  match !r with
    None -> r := Some (v ^ suff)
  | _ -> ()

let compile_rv basename asmfile () =
  if not !Options.nostart then begin
    let obj_file_prog = Filename.temp_file ~temp_dir:"/tmp" "" ".o" in
    let cmdas_prog = Format.sprintf "%s -I%s -o %s %s"
        (Archi.assembler ())
        (Archi.runtime_lib_include_path ())
        obj_file_prog asmfile in
    let obj_file_lib = Filename.temp_file ~temp_dir:"/tmp" "" ".o" in
    let cmdas_lib = Format.sprintf "%s -I%s -o %s %s"
        (Archi.assembler ())
        (Archi.runtime_lib_include_path ())
        obj_file_lib (Archi.runtime_lib_path ()) in
    let cmdld = Format.sprintf "%s -T %s/link.ld %s %s -o %s.exe"
        (Archi.linker ())
        Config.runtime_dir
        obj_file_prog obj_file_lib
        basename in
    Printf.printf "AS: %s\n" cmdas_prog;
    Printf.printf "AS: %s\n" cmdas_lib;
    Printf.printf "LD: %s\n" cmdld;
    let out_as_prog = cmd_to_list cmdas_prog in
    let out_as_lib = cmd_to_list cmdas_lib in
    let out_ld = cmd_to_list cmdld in
    let out = out_as_prog @ out_as_lib @ out_ld in
    match out with
      [] -> None
    | _ -> Some (String.concat "\n" out)
  end
  else None

let exec_rv_prog ltl basename oc rvp heapsize params =
  let rvp =
    match rvp with
      Some rvp -> rvp
    | None ->
      let f = Filename.temp_file ~temp_dir:"/tmp" basename ".s" in
      f
  in
  let error = ref None in
  dump (Some rvp) (dump_riscv_prog !Archi.target) ltl (fun file () ->
      error := compile_rv basename file ());
  match !error with
  | Some e -> Error ("RiscV generation error:\n" ^e)
  | None ->
    let l = cmd_to_list (Format.sprintf "%s \"%s.exe\" %s" (Archi.qemu ())  basename
                           (params |> List.map string_of_int |> String.concat " " )) in
    try
      let all_but_last = l |> List.rev |> List.tl |> List.rev in
      all_but_last |> print_list (fun oc -> Format.fprintf oc "%s") "" "\n" "" oc;
      let ret = l |> List.last |> int_of_string in
      OK (Some ret)
    with _ -> OK None


let _ =
  Arg.parse speclist (fun s -> ()) "Usage";
  Archi.archi := !archi;
  match !input_file with
  | None -> failwith "No input file specified.\n"
  | Some input ->
    add_to_report "Source" "Source" (Code (file_contents input));

    match Filename.chop_suffix_opt ".e" input with
      None -> failwith
                (Format.sprintf "File (%s) should end in .e" input)
    | Some basename ->
      params := List.rev !params;
      set_default riscv_dump basename ".s";
      if not !no_dump then begin
        set_default show_tokens basename ".lex";
        set_default ast_tree basename ".ast";
        set_default e_dump basename ".e.dump";
        set_default cfg_dump basename ".cfg";
        set_default rtl_dump basename ".rtl";
        set_default linear_dump basename ".linear";
        set_default rig_dump basename ".rig";
        set_default ltl_dump basename ".ltl";
      end;

      Printexc.record_backtrace true;
      let compiler_res =
        try
        pass_tokenize input >>= fun tokens ->
        pass_parse tokens >>= fun (ast, _) ->
        pass_elang ast >>= fun ep ->
        run "Elang" !e_run eval_eprog ep;
        pass_cfg_gen ep >>= fun cfg ->
        run "CFG" !cfg_run eval_cfgprog cfg;
        pass_constant_propagation cfg >>= fun cfg ->
        run "CFG after constant_propagation" !cfg_run_after_cp eval_cfgprog cfg;
        pass_dead_assign_elimination cfg >>= fun cfg ->
        run "CFG after dead_assign_elimination" !cfg_run_after_dae eval_cfgprog cfg;
        pass_nop_elimination cfg >>= fun cfg ->
        run "CFG after nop_elimination" !cfg_run_after_ne eval_cfgprog cfg;
        pass_rtl_gen cfg >>= fun rtl ->
        run "RTL" !rtl_run exec_rtl_prog rtl;
        pass_linearize rtl >>= fun (linear, lives) ->
        run "Linear" !linear_run exec_linear_prog linear;
        pass_linear_dse linear lives >>= fun linear ->
        run "Linear after DSE" !linear_run_after_dse exec_linear_prog linear;
        pass_ltl_gen linear
        with e ->
          let emsg = Printexc.to_string e ^ "\n" ^ Printexc.get_backtrace () in
          record_compile_result ~error:(Some emsg) "global";
          Error emsg
      in
      begin
        match compiler_res with
        | Error msg -> ()
        | OK ltl ->
          run "LTL" !ltl_run (exec_ltl_prog) ltl;
          (if !ltl_debug then debug_ltl_prog input ltl !heapsize !params);
          dump !riscv_dump (dump_riscv_prog !Archi.target) ltl (fun file () ->
              add_to_report "riscv" "RISC-V" (Code (file_contents file));
              ignore (compile_rv basename file ()));
          if not !Options.nostart then begin
            run "Risc-V" !riscv_run (exec_rv_prog ltl basename) !riscv_dump
          end
      end;
      dump (Some !output_json) (fun oc p ->
          Format.fprintf oc "%s\n" p
        ) (json_output_string ()) (fun _ () -> ());
      make_report input report ()