mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1471 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			Standard ML
		
	
	
	
	
	
			
		
		
	
	
			1471 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			Standard ML
		
	
	
	
	
	
| (* Copyright (C) 2010-2011,2013 Matthew Fluet.
 | |
|  * Copyright (C) 1999-2009 Henry Cejtin, Matthew Fluet, Suresh
 | |
|  *    Jagannathan, and Stephen Weeks.
 | |
|  * Copyright (C) 1997-2000 NEC Research Institute.
 | |
|  *
 | |
|  * MLton is released under a BSD-style license.
 | |
|  * See the file MLton-LICENSE for details.
 | |
|  *)
 | |
| 
 | |
| functor Main (S: MAIN_STRUCTS): MAIN =
 | |
| struct
 | |
| 
 | |
| open S
 | |
| 
 | |
| structure Compile = Compile ()
 | |
| 
 | |
| structure Place =
 | |
|    struct
 | |
|       datatype t = Files | Generated | MLB | O | OUT | SML | TypeCheck
 | |
| 
 | |
|       val toInt: t -> int =
 | |
|          fn MLB => 1
 | |
|           | SML => 1
 | |
|           | Files => 2
 | |
|           | TypeCheck => 4
 | |
|           | Generated => 5
 | |
|           | O => 6
 | |
|           | OUT => 7
 | |
| 
 | |
|       val toString =
 | |
|          fn Files => "files"
 | |
|           | SML => "sml"
 | |
|           | MLB => "mlb"
 | |
|           | Generated => "g"
 | |
|           | O => "o"
 | |
|           | OUT => "out"
 | |
|           | TypeCheck => "tc"
 | |
| 
 | |
|       fun compare (p, p') = Int.compare (toInt p, toInt p')
 | |
|    end
 | |
| 
 | |
| structure OptPred =
 | |
|    struct
 | |
|       datatype t =
 | |
|          Target of string
 | |
|        | Yes
 | |
|    end
 | |
| 
 | |
| structure Show =
 | |
|    struct
 | |
|       datatype t = Anns | PathMap
 | |
|    end
 | |
| 
 | |
| val gcc: string ref = ref "<unset>"
 | |
| val arScript: string ref = ref "<unset>"
 | |
| val asOpts: {opt: string, pred: OptPred.t} list ref = ref []
 | |
| val ccOpts: {opt: string, pred: OptPred.t} list ref = ref []
 | |
| val linkOpts: {opt: string, pred: OptPred.t} list ref = ref []
 | |
| 
 | |
| val buildConstants: bool ref = ref false
 | |
| val debugRuntime: bool ref = ref false
 | |
| datatype debugFormat = Dwarf | DwarfPlus | Dwarf2 | Stabs | StabsPlus
 | |
| val debugFormat: debugFormat option ref = ref NONE
 | |
| val expert: bool ref = ref false
 | |
| val explicitAlign: Control.align option ref = ref NONE
 | |
| val explicitChunk: Control.chunk option ref = ref NONE
 | |
| datatype explicitCodegen = Native | Explicit of Control.codegen
 | |
| val explicitCodegen: explicitCodegen option ref = ref NONE
 | |
| val keepGenerated = ref false
 | |
| val keepO = ref false
 | |
| val output: string option ref = ref NONE
 | |
| val profileSet: bool ref = ref false
 | |
| val profileTimeSet: bool ref = ref false
 | |
| val runtimeArgs: string list ref = ref ["@MLton"]
 | |
| val show: Show.t option ref = ref NONE
 | |
| val stop = ref Place.OUT
 | |
| 
 | |
| fun parseMlbPathVar (line: String.t) =
 | |
|    case String.tokens (line, Char.isSpace) of
 | |
|       [var, path] => SOME {var = var, path = path}
 | |
|     | _ => NONE
 | |
| 
 | |
| fun readMlbPathMap (file: File.t) =
 | |
|    if not (File.canRead file) then
 | |
|       Error.bug (concat ["can't read MLB path map file: ", file])
 | |
|    else
 | |
|       List.keepAllMap
 | |
|       (File.lines file, fn line =>
 | |
|        if String.forall (line, Char.isSpace)
 | |
|           then NONE
 | |
|        else
 | |
|           case parseMlbPathVar line of
 | |
|              NONE => Error.bug (concat ["strange mlb path mapping: ",
 | |
|                                         file, ":: ", line])
 | |
|            | SOME v => SOME v)
 | |
| 
 | |
| val targetMap: unit -> {arch: MLton.Platform.Arch.t,
 | |
|                         os: MLton.Platform.OS.t,
 | |
|                         target: string} list =
 | |
|    Promise.lazy
 | |
|    (fn () =>
 | |
|     let
 | |
|        val targetsDir =
 | |
|           OS.Path.mkAbsolute { path = "targets",
 | |
|                                relativeTo = !Control.libDir }
 | |
|        val potentialTargets = Dir.lsDirs targetsDir
 | |
|        fun targetMap target =
 | |
|           let
 | |
|              val targetDir =
 | |
|                 OS.Path.mkAbsolute { path = target,
 | |
|                                      relativeTo = targetsDir }
 | |
|              val osFile =
 | |
|                 OS.Path.joinDirFile { dir = targetDir,
 | |
|                                       file = "os" }
 | |
|              val archFile =
 | |
|                 OS.Path.joinDirFile { dir = targetDir,
 | |
|                                       file = "arch" }
 | |
|              val os   = File.contents osFile
 | |
|              val arch = File.contents archFile
 | |
|              val os   = List.first (String.tokens (os,   Char.isSpace))
 | |
|              val arch = List.first (String.tokens (arch, Char.isSpace))
 | |
|              val os =
 | |
|                 case MLton.Platform.OS.fromString os of
 | |
|                    NONE => Error.bug (concat ["strange os: ", os])
 | |
|                  | SOME os => os
 | |
|              val arch =
 | |
|                 case MLton.Platform.Arch.fromString arch of
 | |
|                    NONE => Error.bug (concat ["strange arch: ", arch])
 | |
|                  | SOME a => a
 | |
|           in
 | |
|              SOME { arch = arch, os = os, target = target }
 | |
|           end
 | |
|           handle _ => NONE
 | |
|     in
 | |
|        List.keepAllMap (potentialTargets, targetMap)
 | |
|     end)
 | |
| 
 | |
| fun setTargetType (target: string, usage): unit =
 | |
|    case List.peek (targetMap (), fn {target = t, ...} => target = t) of
 | |
|       NONE => usage (concat ["invalid target: ", target])
 | |
|     | SOME {arch, os, ...} =>
 | |
|          let
 | |
|             open Control
 | |
|          in
 | |
|             Target.arch := arch
 | |
|             ; Target.os := os
 | |
|          end
 | |
| 
 | |
| fun hasCodegen (cg) =
 | |
|    let
 | |
|       datatype z = datatype Control.Target.arch
 | |
|       datatype z = datatype Control.Target.os
 | |
|       datatype z = datatype Control.Format.t
 | |
|       datatype z = datatype Control.codegen
 | |
|    in
 | |
|       case !Control.Target.arch of
 | |
|          AMD64 => (case cg of
 | |
|                       x86Codegen => false
 | |
|                     | _ => true)
 | |
|        | X86 => (case cg of
 | |
|                     amd64Codegen => false
 | |
|                   | x86Codegen =>
 | |
|                       (* Darwin PIC doesn't work *)
 | |
|                       !Control.Target.os <> Darwin orelse
 | |
|                       !Control.format = Executable orelse
 | |
|                       !Control.format = Archive
 | |
|                   | _ => true)
 | |
|        | _ => (case cg of
 | |
|                   amd64Codegen => false
 | |
|                 | x86Codegen => false
 | |
|                 | _ => true)
 | |
|    end
 | |
| fun hasNativeCodegen () =
 | |
|    let
 | |
|       datatype z = datatype Control.codegen
 | |
|    in
 | |
|       hasCodegen amd64Codegen
 | |
|       orelse hasCodegen x86Codegen
 | |
|    end
 | |
| 
 | |
| 
 | |
| fun defaultAlignIs8 () =
 | |
|    let
 | |
|       datatype z = datatype Control.Target.arch
 | |
|    in
 | |
|       case !Control.Target.arch of
 | |
|          Alpha => true
 | |
|        | AMD64 => true
 | |
|        | ARM => true
 | |
|        | HPPA => true
 | |
|        | IA64 => true
 | |
|        | MIPS => true
 | |
|        | Sparc => true
 | |
|        | S390 => true
 | |
|        | _ => false
 | |
|    end
 | |
| 
 | |
| fun makeOptions {usage} =
 | |
|    let
 | |
|       val usage = fn s => (ignore (usage s); raise Fail "unreachable")
 | |
|       fun reportAnnotation (s, flag, e) =
 | |
|          case e of
 | |
|             Control.Elaborate.Bad =>
 | |
|                usage (concat ["invalid -", flag, " flag: ", s])
 | |
|           | Control.Elaborate.Deprecated ids =>
 | |
|                if !Control.warnDeprecated
 | |
|                   then
 | |
|                      Out.output
 | |
|                      (Out.error,
 | |
|                       concat ["Warning: ", "deprecated annotation: ", s, ", use ",
 | |
|                               List.toString Control.Elaborate.Id.name ids, ".\n"])
 | |
|                else ()
 | |
|           | Control.Elaborate.Good () => ()
 | |
|           | Control.Elaborate.Other =>
 | |
|                usage (concat ["invalid -", flag, " flag: ", s])
 | |
|       open Control Popt
 | |
|       datatype z = datatype MLton.Platform.Arch.t
 | |
|       datatype z = datatype MLton.Platform.OS.t
 | |
|       fun tokenizeOpt f opts =
 | |
|          List.foreach (String.tokens (opts, Char.isSpace),
 | |
|                        fn opt => f opt)
 | |
|       fun tokenizeTargetOpt f (target, opts) =
 | |
|          List.foreach (String.tokens (opts, Char.isSpace),
 | |
|                        fn opt => f (target, opt))
 | |
|    in
 | |
|       List.map
 | |
|       (
 | |
|        [
 | |
|        (Normal, "align", if defaultAlignIs8 () then " {8|4}" else " {4|8}",
 | |
|         "object alignment",
 | |
|         (SpaceString (fn s =>
 | |
|                       explicitAlign
 | |
|                       := SOME (case s of
 | |
|                                   "4" => Align4
 | |
|                                 | "8" => Align8
 | |
|                                 | _ => usage (concat ["invalid -align flag: ",
 | |
|                                                       s]))))),
 | |
|        (Expert, "ar-script", " <ar>", "path to a script producing archives",
 | |
|         SpaceString (fn s => arScript := s)),
 | |
|        (Normal, "as-opt", " <opt>", "pass option to assembler",
 | |
|         (SpaceString o tokenizeOpt)
 | |
|         (fn s => List.push (asOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "as-opt-quote", " <opt>", "pass (quoted) option to assembler",
 | |
|         SpaceString
 | |
|         (fn s => List.push (asOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "build-constants", " {false|true}",
 | |
|         "output C file that prints basis constants",
 | |
|         boolRef buildConstants),
 | |
|        (Expert, "cc", " <gcc>", "path to gcc executable",
 | |
|         SpaceString (fn s => gcc := s)),
 | |
|        (Normal, "cc-opt", " <opt>", "pass option to C compiler",
 | |
|         (SpaceString o tokenizeOpt)
 | |
|         (fn s => List.push (ccOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "cc-opt-quote", " <opt>", "pass (quoted) option to C compiler",
 | |
|         SpaceString
 | |
|         (fn s => List.push (ccOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "chunkify", " {coalesce<n>|func|one}", "set chunkify method",
 | |
|         SpaceString (fn s =>
 | |
|                      explicitChunk
 | |
|                      := SOME (case s of
 | |
|                                  "func" => ChunkPerFunc
 | |
|                                | "one" => OneChunk
 | |
|                                | _ => let
 | |
|                                          val usage = fn () =>
 | |
|                                             usage (concat ["invalid -chunkify flag: ", s])
 | |
|                                       in
 | |
|                                          if String.hasPrefix (s, {prefix = "coalesce"})
 | |
|                                             then let
 | |
|                                                     val s = String.dropPrefix (s, 8)
 | |
|                                                  in
 | |
|                                                     if String.forall (s, Char.isDigit)
 | |
|                                                        then (case Int.fromString s of
 | |
|                                                                 NONE => usage ()
 | |
|                                                               | SOME n => Coalesce
 | |
|                                                                           {limit = n})
 | |
|                                                        else usage ()
 | |
|                                                  end
 | |
|                                             else usage ()
 | |
|                                       end))),
 | |
|        (Expert, "closure-convert-globalize", " {true|false}",
 | |
|         "whether to globalize during closure conversion",
 | |
|         Bool (fn b => (closureConvertGlobalize := b))),
 | |
|        (Expert, "closure-convert-shrink", " {true|false}",
 | |
|         "whether to shrink during closure conversion",
 | |
|         Bool (fn b => (closureConvertShrink := b))),
 | |
|        (Normal, "codegen",
 | |
|         concat [" {",
 | |
|                 String.concatWith
 | |
|                 (List.keepAllMap
 | |
|                  (Native :: (List.map (Control.Codegen.all, Explicit)),
 | |
|                   fn cg =>
 | |
|                   case cg of
 | |
|                      Native => if hasNativeCodegen () then SOME "native" else NONE
 | |
|                    | Explicit cg => if hasCodegen cg
 | |
|                                        then SOME (Control.Codegen.toString cg)
 | |
|                                     else NONE),
 | |
|                  "|"),
 | |
|                 "}"],
 | |
|         "which code generator to use",
 | |
|         SpaceString (fn s =>
 | |
|                      explicitCodegen
 | |
|                      := SOME (if s = "native"
 | |
|                                  then Native
 | |
|                               else (case List.peek
 | |
|                                          (Control.Codegen.all, fn cg =>
 | |
|                                           s = Control.Codegen.toString cg) of
 | |
|                                        SOME cg => Explicit cg
 | |
|                                      | NONE => usage (concat ["invalid -codegen flag: ", s]))))),
 | |
|        (Normal, "const", " '<name> <value>'", "set compile-time constant",
 | |
|         SpaceString (fn s =>
 | |
|                      case String.tokens (s, Char.isSpace) of
 | |
|                         [name, value] =>
 | |
|                            Compile.setCommandLineConstant {name = name,
 | |
|                                                            value = value}
 | |
|                       | _ => usage (concat ["invalid -const flag: ", s]))),
 | |
|        (Expert, "contify-into-main", " {false|true}",
 | |
|         "contify functions into main",
 | |
|         boolRef contifyIntoMain),
 | |
|        (Expert, "debug", " {false|true}", "produce executable with debug info",
 | |
|         Bool (fn b => (debug := b
 | |
|                        ; debugRuntime := b))),
 | |
|        (Expert, "debug-runtime", " {false|true}", "produce executable with debug info",
 | |
|         boolRef debugRuntime),
 | |
|        (Expert, "debug-format", " {default|dwarf|dwarf+|drwaf2|stabs|stabs+}",
 | |
|         "choose debug symbol format",
 | |
|         SpaceString (fn s =>
 | |
|                         debugFormat :=
 | |
|                         (case s of
 | |
|                             "default" => NONE
 | |
|                           | "dwarf" => SOME Dwarf
 | |
|                           | "dwarf+" => SOME DwarfPlus
 | |
|                           | "dwarf2" => SOME Dwarf2
 | |
|                           | "stabs" => SOME Stabs
 | |
|                           | "stabs+" => SOME StabsPlus
 | |
|                           | _ => usage (concat ["invalid -debug-format flag: ", s])))),
 | |
|        let
 | |
|           val flag = "default-ann"
 | |
|        in
 | |
|           (Normal, flag, " <ann>", "set annotation default for mlb files",
 | |
|            SpaceString
 | |
|            (fn s => reportAnnotation (s, flag,
 | |
|                                       Control.Elaborate.processDefault s)))
 | |
|        end,
 | |
|        (Normal, "default-type", " '<ty><N>'", "set default type",
 | |
|         SpaceString
 | |
|         (fn s => (case s of
 | |
|                      "char8" => Control.defaultChar := s
 | |
|                    | "int8" => Control.defaultInt := s
 | |
|                    | "int16" => Control.defaultInt := s
 | |
|                    | "int32" => Control.defaultInt := s
 | |
|                    | "int64" => Control.defaultInt := s
 | |
|                    | "intinf" => Control.defaultInt := s
 | |
|                    | "real32" => Control.defaultReal := s
 | |
|                    | "real64" => Control.defaultReal := s
 | |
|                    | "widechar16" => Control.defaultWideChar := s
 | |
|                    | "widechar32" => Control.defaultWideChar := s
 | |
|                    | "word8" => Control.defaultWord := s
 | |
|                    | "word16" => Control.defaultWord := s
 | |
|                    | "word32" => Control.defaultWord := s
 | |
|                    | "word64" => Control.defaultWord := s
 | |
|                    | _ => usage (concat ["invalid -default-type flag: ", s])))),
 | |
|        (Expert, "diag-pass", " <pass>", "keep diagnostic info for pass",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          (case Regexp.fromString s of
 | |
|              SOME (re,_) => let val re = Regexp.compileDFA re
 | |
|                             in List.push (diagPasses, re)
 | |
|                             end
 | |
|            | NONE => usage (concat ["invalid -diag-pass flag: ", s])))),
 | |
|        let
 | |
|           val flag = "disable-ann"
 | |
|        in
 | |
|           (Normal, flag, " <ann>", "disable annotation in mlb files",
 | |
|            SpaceString
 | |
|            (fn s =>
 | |
|             reportAnnotation (s, flag,
 | |
|                               Control.Elaborate.processEnabled (s, false))))
 | |
|        end,
 | |
|        (Expert, "drop-pass", " <pass>", "omit optimization pass",
 | |
|         SpaceString
 | |
|         (fn s => (case Regexp.fromString s of
 | |
|                      SOME (re,_) => let val re = Regexp.compileDFA re
 | |
|                                     in List.push (dropPasses, re)
 | |
|                                     end
 | |
|                    | NONE => usage (concat ["invalid -drop-pass flag: ", s])))),
 | |
|        let
 | |
|           val flag = "enable-ann"
 | |
|        in
 | |
|           (Expert, flag, " <ann>", "globally enable annotation",
 | |
|            SpaceString
 | |
|            (fn s =>
 | |
|             reportAnnotation (s, flag,
 | |
|                               Control.Elaborate.processEnabled (s, true))))
 | |
|        end,
 | |
|        (Expert, "error-threshhold", " <n>", "error threshhold (20)",
 | |
|         intRef errorThreshhold),
 | |
|        (Expert, "emit-main", " {true|false}", "emit main() startup function",
 | |
|         boolRef emitMain),
 | |
|        (Expert, "expert", " {false|true}", "enable expert status",
 | |
|         boolRef expert),
 | |
|        (Normal, "export-header", " <file>", "write C header file for _export's",
 | |
|         SpaceString (fn s => exportHeader := SOME s)),
 | |
|        (Expert, "format",
 | |
|         concat [" {",
 | |
|                 String.concatWith
 | |
|                 (List.keepAllMap
 | |
|                   (Control.Format.all, fn cg => SOME (Control.Format.toString cg)),
 | |
|                  "|"),
 | |
|                 "}"],
 | |
|         "generated output format",
 | |
|         SpaceString (fn s =>
 | |
|                      Control.format
 | |
|                      := (case List.peek
 | |
|                               (Control.Format.all, fn cg =>
 | |
|                                s = Control.Format.toString cg) of
 | |
|                             SOME cg => cg
 | |
|                           | NONE => usage (concat ["invalid -format flag: ", s])))),
 | |
|        (Expert, "gc-check", " {limit|first|every}", "force GCs",
 | |
|         SpaceString (fn s =>
 | |
|                      gcCheck :=
 | |
|                      (case s of
 | |
|                          "limit" => Limit
 | |
|                        | "first" => First
 | |
|                        | "every" => Every
 | |
|                        | _ => usage (concat ["invalid -gc-check flag: ", s])))),
 | |
|        (Normal, "ieee-fp", " {false|true}", "use strict IEEE floating-point",
 | |
|         boolRef Native.IEEEFP),
 | |
|        (Expert, "indentation", " <n>", "indentation level in ILs",
 | |
|         intRef indentation),
 | |
|        (Normal, "inline", " <n>", "set inlining threshold",
 | |
|         Int (fn i => inlineNonRec := {small = i,
 | |
|                                       product = #product (!inlineNonRec)})),
 | |
|        (Expert, "inline-into-main", " {true|false}",
 | |
|         "inline functions into main",
 | |
|         boolRef inlineIntoMain),
 | |
|        (Expert, "inline-leafa-loops", " {true|false}", "leaf inline loops",
 | |
|         Bool (fn loops =>
 | |
|               case !inlineLeafA of
 | |
|                  {repeat, size, ...} =>
 | |
|                     inlineLeafA :=
 | |
|                     {loops = loops, repeat = repeat, size = size})),
 | |
|        (Expert, "inline-leafa-repeat", " {true|false}", "leaf inline repeat",
 | |
|         Bool (fn repeat =>
 | |
|               case !inlineLeafA of
 | |
|                  {loops, size, ...} =>
 | |
|                     inlineLeafA :=
 | |
|                     {loops = loops, repeat = repeat, size = size})),
 | |
|        (Expert, "inline-leafa-size", " <n>", "set leaf inlining threshold (20)",
 | |
|         SpaceString (fn s =>
 | |
|                      case !inlineLeafA of
 | |
|                         {loops, repeat, ...} =>
 | |
|                            inlineLeafA :=
 | |
|                            {loops = loops, repeat = repeat,
 | |
|                             size = (if s = "inf"
 | |
|                                        then NONE
 | |
|                                     else if String.forall (s, Char.isDigit)
 | |
|                                        then Int.fromString s
 | |
|                                     else (usage o concat)
 | |
|                                          ["invalid -inline-leaf-size flag: ", s])})),
 | |
|        (Expert, "inline-leafb-loops", " {true|false}", "leaf inline loops",
 | |
|         Bool (fn loops =>
 | |
|               case !inlineLeafB of
 | |
|                  {repeat, size, ...} =>
 | |
|                     inlineLeafB :=
 | |
|                     {loops = loops, repeat = repeat, size = size})),
 | |
|        (Expert, "inline-leafb-repeat", " {true|false}", "leaf inline repeat",
 | |
|         Bool (fn repeat =>
 | |
|               case !inlineLeafB of
 | |
|                  {loops, size, ...} =>
 | |
|                     inlineLeafB :=
 | |
|                     {loops = loops, repeat = repeat, size = size})),
 | |
|        (Expert, "inline-leafb-size", " <n>", "set leaf inlining threshold (40)",
 | |
|         SpaceString (fn s =>
 | |
|                      case !inlineLeafB of
 | |
|                         {loops, repeat, ...} =>
 | |
|                            inlineLeafB :=
 | |
|                            {loops = loops, repeat = repeat,
 | |
|                             size = (if s = "inf"
 | |
|                                        then NONE
 | |
|                                     else if String.forall (s, Char.isDigit)
 | |
|                                        then Int.fromString s
 | |
|                                     else (usage o concat)
 | |
|                                          ["invalid -inline-leaf-size flag: ", s])})),
 | |
|        (Expert, "inline-nonrec-product", " <n>", "set inlining threshold (320)",
 | |
|         Int (fn product =>
 | |
|              case !inlineNonRec of
 | |
|                 {small, ...} =>
 | |
|                    inlineNonRec := {small = small, product = product})),
 | |
|        (Expert, "inline-nonrec-small", " <n>", "set inlining threshold (60)",
 | |
|         Int (fn small =>
 | |
|              case !inlineNonRec of
 | |
|                 {product, ...} =>
 | |
|                    inlineNonRec := {small = small, product = product})),
 | |
|        (Normal, "keep", " {g|o}", "save intermediate files",
 | |
|         SpaceString (fn s =>
 | |
|                      case s of
 | |
|                         "core-ml" => keepCoreML := true
 | |
|                       | "dot" => keepDot := true
 | |
|                       | "g" => keepGenerated := true
 | |
|                       | "machine" => keepMachine := true
 | |
|                       | "o" => keepO := true
 | |
|                       | "rssa" => keepRSSA := true
 | |
|                       | "ssa" => keepSSA := true
 | |
|                       | "ssa2" => keepSSA2 := true
 | |
|                       | "sxml" => keepSXML := true
 | |
|                       | "xml" => keepXML := true
 | |
|                       | _ => usage (concat ["invalid -keep flag: ", s]))),
 | |
|        (Expert, "keep-pass", " <pass>", "keep the results of pass",
 | |
|         SpaceString
 | |
|         (fn s => (case Regexp.fromString s of
 | |
|                      SOME (re,_) => let val re = Regexp.compileDFA re
 | |
|                                     in List.push (keepPasses, re)
 | |
|                                     end
 | |
|                    | NONE => usage (concat ["invalid -keep-pass flag: ", s])))),
 | |
|        (Expert, "libname", " <basename>", "the name of the generated library",
 | |
|         SpaceString (fn s => libname := s)),
 | |
|        (Normal, "link-opt", " <opt>", "pass option to linker",
 | |
|         (SpaceString o tokenizeOpt)
 | |
|         (fn s => List.push (linkOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "link-opt-quote", " <opt>", "pass (quoted) option to linker",
 | |
|         SpaceString
 | |
|         (fn s => List.push (linkOpts, {opt = s, pred = OptPred.Yes}))),
 | |
|        (Expert, "loop-passes", " <n>", "loop optimization passes (1)",
 | |
|         Int
 | |
|         (fn i =>
 | |
|          if i >= 1
 | |
|             then loopPasses := i
 | |
|             else usage (concat ["invalid -loop-passes arg: ", Int.toString i]))),
 | |
|        (Expert, "mark-cards", " {true|false}", "mutator marks cards",
 | |
|         boolRef markCards),
 | |
|        (Expert, "max-function-size", " <n>", "max function size (blocks)",
 | |
|         intRef maxFunctionSize),
 | |
|        (Normal, "mlb-path-map", " <file>", "additional MLB path map",
 | |
|         SpaceString (fn s => mlbPathVars := !mlbPathVars @ readMlbPathMap s)),
 | |
|        (Normal, "mlb-path-var", " '<name> <value>'", "additional MLB path var",
 | |
|         SpaceString
 | |
|         (fn s => mlbPathVars := !mlbPathVars @
 | |
|                                 [case parseMlbPathVar s of
 | |
|                                     NONE => Error.bug ("strange mlb path var: " ^ s)
 | |
|                                   | SOME v => v])),
 | |
|        (Expert, "native-commented", " <n>", "level of comments  (0)",
 | |
|         intRef Native.commented),
 | |
|        (Expert, "native-copy-prop", " {true|false}",
 | |
|         "use copy propagation",
 | |
|         boolRef Native.copyProp),
 | |
|        (Expert, "native-cutoff", " <n>",
 | |
|         "live transfer cutoff distance",
 | |
|         intRef Native.cutoff),
 | |
|        (Expert, "native-live-transfer", " {0,...,8}",
 | |
|         "use live transfer",
 | |
|         intRef Native.liveTransfer),
 | |
|        (Expert, "native-live-stack", " {false|true}",
 | |
|         "track liveness of stack slots",
 | |
|         boolRef Native.liveStack),
 | |
|        (Expert, "native-move-hoist", " {true|false}",
 | |
|         "use move hoisting",
 | |
|         boolRef Native.moveHoist),
 | |
|        (Expert, "native-optimize", " <n>", "level of optimizations",
 | |
|         intRef Native.optimize),
 | |
|        (Expert, "native-split", " <n>", "split assembly files at ~n lines",
 | |
|         Int (fn i => Native.split := SOME i)),
 | |
|        (Expert, "native-shuffle", " {true|false}",
 | |
|         "shuffle registers at C-calls",
 | |
|         Bool (fn b => Native.shuffle := b)),
 | |
|        (Expert, "opt-passes", " {default|minimal}", "level of optimizations",
 | |
|         SpaceString (fn s =>
 | |
|                      let
 | |
|                         fun err s =
 | |
|                            usage (concat ["invalid -opt-passes flag: ", s])
 | |
|                      in
 | |
|                         List.foreach
 | |
|                         (!optimizationPasses, fn {il,set,...} =>
 | |
|                          case set s of
 | |
|                             Result.Yes () => ()
 | |
|                           | Result.No s' => err (concat [s', "(for ", il, ")"]))
 | |
|                      end)),
 | |
|        (Normal, "output", " <file>", "name of output file",
 | |
|         SpaceString (fn s => output := SOME s)),
 | |
|        (Expert, "polyvariance", " {true|false}", "use polyvariance",
 | |
|         Bool (fn b => if b then () else polyvariance := NONE)),
 | |
|        (Expert, "polyvariance-hofo", " {true|false}", "duplicate higher-order fns only",
 | |
|         Bool (fn hofo =>
 | |
|               case !polyvariance of
 | |
|                  SOME {product, rounds, small, ...} =>
 | |
|                     polyvariance := SOME {hofo = hofo,
 | |
|                                           product = product,
 | |
|                                           rounds = rounds,
 | |
|                                           small = small}
 | |
|                | _ => ())),
 | |
|        (Expert, "polyvariance-product", " <n>", "set polyvariance threshold (300)",
 | |
|         Int (fn product =>
 | |
|              case !polyvariance of
 | |
|                 SOME {hofo, rounds, small, ...} =>
 | |
|                    polyvariance := SOME {hofo = hofo,
 | |
|                                          product = product,
 | |
|                                          rounds = rounds,
 | |
|                                          small = small}
 | |
|               | _ => ())),
 | |
|        (Expert, "polyvariance-rounds", " <n>", "set polyvariance rounds (2)",
 | |
|         Int (fn rounds =>
 | |
|              case !polyvariance of
 | |
|                 SOME {hofo, product, small, ...} =>
 | |
|                    polyvariance := SOME {hofo = hofo,
 | |
|                                          product = product,
 | |
|                                          rounds = rounds,
 | |
|                                          small = small}
 | |
|               | _ => ())),
 | |
|        (Expert, "polyvariance-small", " <n>", "set polyvariance threshold (30)",
 | |
|         Int (fn small =>
 | |
|              case !polyvariance of
 | |
|                 SOME {hofo, product, rounds, ...} =>
 | |
|                    polyvariance := SOME {hofo = hofo,
 | |
|                                          product = product,
 | |
|                                          rounds = rounds,
 | |
|                                          small = small}
 | |
|               | _ => ())),
 | |
|        (Expert, "prefer-abs-paths", " {false|true}",
 | |
|         "prefer absolute paths when referring to files",
 | |
|         boolRef preferAbsPaths),
 | |
|        (Expert, "prof-pass", " <pass>", "keep profile info for pass",
 | |
|         SpaceString (fn s =>
 | |
|                      (case Regexp.fromString s of
 | |
|                          SOME (re,_) => let val re = Regexp.compileDFA re
 | |
|                                         in
 | |
|                                            List.push (profPasses, re)
 | |
|                                         end
 | |
|                        | NONE => usage (concat ["invalid -diag-pass flag: ", s])))),
 | |
|        (Normal, "profile", " {no|alloc|count|time}",
 | |
|         "produce executable suitable for profiling",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          if !profileSet
 | |
|             then usage "can't have multiple -profile switches"
 | |
|          else
 | |
|             (profileSet := true
 | |
|              ; profile := (case s of
 | |
|                               "no" => ProfileNone
 | |
|                             | "alloc" => ProfileAlloc
 | |
|                             | "call" => ProfileCallStack
 | |
|                             | "count" => ProfileCount
 | |
|                             | "drop" => ProfileDrop
 | |
|                             | "label" => ProfileLabel
 | |
|                             | "time" => (profileTimeSet := true
 | |
|                                          ; ProfileTimeLabel)
 | |
|                             | "time-field" => ProfileTimeField
 | |
|                             | "time-label" => ProfileTimeLabel
 | |
|                             | _ => usage (concat
 | |
|                                           ["invalid -profile arg: ", s]))))),
 | |
|        (Normal, "profile-branch", " {false|true}",
 | |
|         "profile branches in addition to functions",
 | |
|         boolRef profileBranch),
 | |
|        (Expert, "profile-c", " <regexp>",
 | |
|         "include C-calls in files matching <regexp> in profile",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          (case Regexp.fromString s of
 | |
|              SOME (re,_) => let
 | |
|                                open Regexp
 | |
|                                val re = seq [anys, re, anys]
 | |
|                                val re = compileDFA re
 | |
|                             in List.push (profileC, re)
 | |
|                             end
 | |
|            | NONE => usage (concat ["invalid -profile-c flag: ", s])))),
 | |
|        (Expert, "profile-exclude", " <regexp>",
 | |
|         "exclude files matching <regexp> from profile",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          (case Regexp.fromString s of
 | |
|              SOME (re,_) => let
 | |
|                                open Regexp
 | |
|                                val re = seq [anys, re, anys]
 | |
|                                val re = compileDFA re
 | |
|                             in List.push (profileInclExcl, (re, false))
 | |
|                             end
 | |
|            | NONE => usage (concat ["invalid -profile-exclude flag: ", s])))),
 | |
|        (Expert, "profile-il", " {source}", "where to insert profile exps",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          case s of
 | |
|             "source" => profileIL := ProfileSource
 | |
|           | "ssa" => profileIL := ProfileSSA
 | |
|           | "ssa2" => profileIL := ProfileSSA2
 | |
|           | _ => usage (concat ["invalid -profile-il arg: ", s]))),
 | |
|        (Expert, "profile-include", " <regexp>",
 | |
|         "include files matching <regexp> from profile",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          (case Regexp.fromString s of
 | |
|              SOME (re,_) => let
 | |
|                                open Regexp
 | |
|                                val re = seq [anys, re, anys]
 | |
|                                val re = compileDFA re
 | |
|                             in List.push (profileInclExcl, (re, true))
 | |
|                             end
 | |
|            | NONE => usage (concat ["invalid -profile-include flag: ", s])))),
 | |
|        (Expert, "profile-raise", " {false|true}",
 | |
|         "profile raises in addition to functions",
 | |
|         boolRef profileRaise),
 | |
|        (Normal, "profile-stack", " {false|true}", "profile the stack",
 | |
|         boolRef profileStack),
 | |
|        (Normal, "profile-val", " {false|true}",
 | |
|         "profile val bindings in addition to functions",
 | |
|         boolRef profileVal),
 | |
|        (Normal, "runtime", " <arg>", "pass arg to runtime via @MLton",
 | |
|         SpaceString (fn s => List.push (runtimeArgs, s))),
 | |
|        (Expert, "show", " {anns|path-map}", "print specified data and stop",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          show := SOME (case s of
 | |
|                           "anns" => Show.Anns
 | |
|                         | "path-map" => Show.PathMap
 | |
|                         | _ => usage (concat ["invalid -show arg: ", s])))),
 | |
|        (Normal, "show-basis", " <file>", "write out the final basis environment",
 | |
|         SpaceString (fn s => showBasis := SOME s)),
 | |
|        (Normal, "show-def-use", " <file>", "write def-use information",
 | |
|         SpaceString (fn s => showDefUse := SOME s)),
 | |
|        (Expert, "show-types", " {true|false}", "show types in ILs",
 | |
|         boolRef showTypes),
 | |
|        (Expert, "ssa-passes", " <passes>", "ssa optimization passes",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          case List.peek (!Control.optimizationPasses,
 | |
|                          fn {il, ...} => String.equals ("ssa", il)) of
 | |
|             SOME {set, ...} =>
 | |
|                (case set s of
 | |
|                    Result.Yes () => ()
 | |
|                  | Result.No s' => usage (concat ["invalid -ssa-passes arg: ", s']))
 | |
|           | NONE => Error.bug "ssa optimization passes missing")),
 | |
|        (Expert, "ssa2-passes", " <passes>", "ssa2 optimization passes",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          case List.peek (!Control.optimizationPasses,
 | |
|                          fn {il, ...} => String.equals ("ssa2", il)) of
 | |
|             SOME {set, ...} =>
 | |
|                (case set s of
 | |
|                    Result.Yes () => ()
 | |
|                  | Result.No s' => usage (concat ["invalid -ssa2-passes arg: ", s']))
 | |
|           | NONE => Error.bug "ssa2 optimization passes missing")),
 | |
|        (Normal, "stop", " {f|g|o|tc}", "when to stop",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          stop := (case s of
 | |
|                      "f" => Place.Files
 | |
|                    | "g" => Place.Generated
 | |
|                    | "o" => Place.O
 | |
|                    | "tc" => Place.TypeCheck
 | |
|                    | _ => usage (concat ["invalid -stop arg: ", s])))),
 | |
|        (Expert, "sxml-passes", " <passes>", "sxml optimization passes",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          case List.peek (!Control.optimizationPasses,
 | |
|                          fn {il, ...} => String.equals ("sxml", il)) of
 | |
|             SOME {set, ...} =>
 | |
|                (case set s of
 | |
|                    Result.Yes () => ()
 | |
|                  | Result.No s' => usage (concat ["invalid -sxml-passes arg: ", s']))
 | |
|           | NONE => Error.bug "sxml optimization passes missing")),
 | |
|        (Normal, "target",
 | |
|         concat [" {",
 | |
|                 (case targetMap () of
 | |
|                     [] => ""
 | |
|                   | [x] => #target x
 | |
|                   | x :: _ => concat [#target x, "|..."]),
 | |
|                 "}"],
 | |
|         "platform that executable will run on",
 | |
|         SpaceString
 | |
|         (fn t =>
 | |
|          (target := (if t = "self" then Self else Cross t);
 | |
|           setTargetType (t, usage)))),
 | |
|        (Normal, "target-as-opt", " <target> <opt>", "target-dependent assembler option",
 | |
|         (SpaceString2 o tokenizeTargetOpt)
 | |
|         (fn (target, opt) =>
 | |
|          List.push (asOpts, {opt = opt, pred = OptPred.Target target}))),
 | |
|        (Expert, "target-as-opt-quote", " <target> <opt>", "target-dependent assembler option (quoted)",
 | |
|         (SpaceString2
 | |
|          (fn (target, opt) =>
 | |
|           List.push (asOpts, {opt = opt, pred = OptPred.Target target})))),
 | |
|        (Normal, "target-cc-opt", " <target> <opt>", "target-dependent C compiler option",
 | |
|         (SpaceString2 o tokenizeTargetOpt)
 | |
|         (fn (target, opt) =>
 | |
|          List.push (ccOpts, {opt = opt, pred = OptPred.Target target}))),
 | |
|        (Expert, "target-cc-opt-quote", " <target> <opt>", "target-dependent C compiler option (quoted)",
 | |
|         (SpaceString2
 | |
|          (fn (target, opt) =>
 | |
|           List.push (ccOpts, {opt = opt, pred = OptPred.Target target})))),
 | |
|        (Normal, "target-link-opt", " <target> <opt>", "target-dependent linker option",
 | |
|         (SpaceString2 o tokenizeTargetOpt)
 | |
|         (fn (target, opt) =>
 | |
|          List.push (linkOpts, {opt = opt, pred = OptPred.Target target}))),
 | |
|        (Expert, "target-link-opt-quote", " <target> <opt>", "target-dependent linker option (quoted)",
 | |
|         (SpaceString2
 | |
|          (fn (target, opt) =>
 | |
|           List.push (linkOpts, {opt = opt, pred = OptPred.Target target})))),
 | |
|        (Expert, #1 trace, " name1,...", "trace compiler internals", #2 trace),
 | |
|        (Expert, "type-check", " {false|true}", "type check ILs",
 | |
|         boolRef typeCheck),
 | |
|        (Normal, "verbose", " {0|1|2|3}", "how verbose to be",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          verbosity := (case s of
 | |
|                           "0" => Silent
 | |
|                         | "1" => Top
 | |
|                         | "2" => Pass
 | |
|                         | "3" => Detail
 | |
|                         | _ => usage (concat ["invalid -verbose arg: ", s])))),
 | |
|        (Expert, "warn-ann", " {true|false}",
 | |
|         "unrecognized annotation warnings",
 | |
|         boolRef warnAnn),
 | |
|        (Expert, "warn-deprecated", " {true|false}",
 | |
|         "deprecated feature warnings",
 | |
|         boolRef warnDeprecated),
 | |
|        (Expert, "xml-passes", " <passes>", "xml optimization passes",
 | |
|         SpaceString
 | |
|         (fn s =>
 | |
|          case List.peek (!Control.optimizationPasses,
 | |
|                          fn {il, ...} => String.equals ("xml", il)) of
 | |
|             SOME {set, ...} =>
 | |
|                (case set s of
 | |
|                    Result.Yes () => ()
 | |
|                  | Result.No s' => usage (concat ["invalid -xml-passes arg: ", s']))
 | |
|           | NONE => Error.bug "xml optimization passes missing")),
 | |
|        (Expert, "zone-cut-depth", " <n>", "zone cut depth",
 | |
|         intRef zoneCutDepth)
 | |
|        ],
 | |
|        fn (style, name, arg, desc, opt) =>
 | |
|        {arg = arg, desc = desc, name = name, opt = opt, style = style})
 | |
|    end
 | |
| 
 | |
| val mainUsage =
 | |
|    "mlton [option ...] file.{c|mlb|o|sml} [file.{c|o|s|S} ...]"
 | |
| 
 | |
| val {parse, usage} =
 | |
|    Popt.makeUsage {mainUsage = mainUsage,
 | |
|                    makeOptions = makeOptions,
 | |
|                    showExpert = fn () => !expert}
 | |
| 
 | |
| val usage = fn s => (usage s; raise Fail "unreachable")
 | |
| 
 | |
| fun commandLine (args: string list): unit =
 | |
|    let
 | |
|       open Control
 | |
|       datatype z = datatype MLton.Platform.Arch.t
 | |
|       datatype z = datatype MLton.Platform.OS.t
 | |
|       val args =
 | |
|          case args of
 | |
|             lib :: args =>
 | |
|                (libDir := OS.Path.mkCanonical lib
 | |
|                 ; args)
 | |
|           | _ => Error.bug "incorrect args from shell script"
 | |
|       val () = setTargetType ("self", usage)
 | |
|       val result = parse args
 | |
| 
 | |
|       val target = !target
 | |
|       val targetStr =
 | |
|          case target of
 | |
|             Cross s => s
 | |
|           | Self => "self"
 | |
|       val targetsDir =
 | |
|          OS.Path.mkAbsolute { path = "targets",
 | |
|                               relativeTo = !libDir }
 | |
|       val targetDir =
 | |
|          OS.Path.mkAbsolute { path = targetStr,
 | |
|                               relativeTo = targetsDir }
 | |
|       val () = libTargetDir := targetDir
 | |
|       val targetArch = !Target.arch
 | |
|       val archStr = String.toLower (MLton.Platform.Arch.toString targetArch)
 | |
|       val targetOS = !Target.os
 | |
|       val OSStr = String.toLower (MLton.Platform.OS.toString targetOS)
 | |
| 
 | |
|       (* Determine whether code should be PIC (position independent) or not.
 | |
|        * This decision depends on the platform and output format.
 | |
|        *)
 | |
|       val positionIndependent =
 | |
|          case (targetOS, targetArch, !format) of
 | |
|             (* Windows is never position independent *)
 | |
|             (MinGW, _, _) => false
 | |
|           | (Cygwin, _, _) => false
 | |
|             (* Technically, Darwin should always be PIC.
 | |
|              * However, PIC on i386/darwin is unimplemented so we avoid it.
 | |
|              * PowerPC PIC is bad too, but the C codegen will use PIC behind
 | |
|              * our back unless forced, so let's just admit that it's PIC.
 | |
|              *)
 | |
|           | (Darwin, X86, Executable) => false
 | |
|           | (Darwin, X86, Archive) => false
 | |
|           | (Darwin, _, _) => true
 | |
|             (* On ELF systems, we only need PIC for LibArchive/Library *)
 | |
|           | (_, _, Library) => true
 | |
|           | (_, _, LibArchive) => true
 | |
|           | _ => false
 | |
|       val () = Control.positionIndependent := positionIndependent
 | |
| 
 | |
|       val stop = !stop
 | |
| 
 | |
|       val () =
 | |
|          align := (case !explicitAlign of
 | |
|                       NONE => if defaultAlignIs8 () then Align8 else Align4
 | |
|                     | SOME a => a)
 | |
|       val () =
 | |
|          codegen := (case !explicitCodegen of
 | |
|                         NONE =>
 | |
|                            if hasCodegen (x86Codegen)
 | |
|                               then x86Codegen
 | |
|                            else if hasCodegen (amd64Codegen)
 | |
|                                then amd64Codegen
 | |
|                            else CCodegen
 | |
|                       | SOME Native =>
 | |
|                            if hasCodegen (x86Codegen)
 | |
|                               then x86Codegen
 | |
|                            else if hasCodegen (amd64Codegen)
 | |
|                               then amd64Codegen
 | |
|                            else usage (concat ["can't use native codegen on ",
 | |
|                                                MLton.Platform.Arch.toString targetArch,
 | |
|                                                " target"])
 | |
|                       | SOME (Explicit cg) => cg)
 | |
|       val () = MLton.Rusage.measureGC (!verbosity <> Silent)
 | |
|       val () = if !profileTimeSet
 | |
|                   then (case !codegen of
 | |
|                            x86Codegen => profile := ProfileTimeLabel
 | |
|                          | amd64Codegen => profile := ProfileTimeLabel
 | |
|                          | _ => profile := ProfileTimeField)
 | |
|                   else ()
 | |
|       val () = if !exnHistory
 | |
|                   then (case !profile of
 | |
|                            ProfileNone => profile := ProfileCallStack
 | |
|                          | ProfileCallStack => ()
 | |
|                          | _ => usage "can't use -profile with Exn.keepHistory"
 | |
|                         ; profileRaise := true)
 | |
|                else ()
 | |
| 
 | |
|       val () =
 | |
|          Compile.setCommandLineConstant
 | |
|          {name = "CallStack.keep",
 | |
|           value = Bool.toString (!Control.profile = Control.ProfileCallStack)}
 | |
| 
 | |
|       val () =
 | |
|          let
 | |
|             val sizeMap =
 | |
|                List.map
 | |
|                (File.lines (OS.Path.joinDirFile {dir = !Control.libTargetDir,
 | |
|                                                  file = "sizes"}),
 | |
|                 fn line =>
 | |
|                 case String.tokens (line, Char.isSpace) of
 | |
|                    [ty, "=", size] =>
 | |
|                       (case Int.fromString size of
 | |
|                           NONE => Error.bug (concat ["strange size: ", size])
 | |
|                         | SOME size =>
 | |
|                              (ty, Bytes.toBits (Bytes.fromInt size)))
 | |
|                  | _ => Error.bug (concat ["strange size mapping: ", line]))
 | |
|             fun lookup ty' =
 | |
|                case List.peek (sizeMap, fn (ty, _) => String.equals (ty, ty')) of
 | |
|                   NONE => Error.bug (concat ["missing size mapping: ", ty'])
 | |
|                 | SOME (_, size) => size
 | |
|          in
 | |
|             Control.Target.setSizes
 | |
|             {cint = lookup "cint",
 | |
|              cpointer = lookup "cpointer",
 | |
|              cptrdiff = lookup "cptrdiff",
 | |
|              csize = lookup "csize",
 | |
|              header = lookup "header",
 | |
|              mplimb = lookup "mplimb",
 | |
|              objptr = lookup "objptr",
 | |
|              seqIndex = lookup "seqIndex"}
 | |
|          end
 | |
| 
 | |
|       fun tokenize l =
 | |
|          String.tokens (concat (List.separate (l, " ")), Char.isSpace)
 | |
| 
 | |
|       (* When cross-compiling, use the named cross compiler.
 | |
|        * Older gcc versions used -b for multiple targets.
 | |
|        * If this is still needed, a shell script wrapper can hide this.
 | |
|        *)
 | |
|       val gcc =
 | |
|          case target of
 | |
|             Cross s =>
 | |
|                let
 | |
|                   val {dir = gccDir, file = gccFile} =
 | |
|                      OS.Path.splitDirFile (!gcc)
 | |
|                in
 | |
|                   OS.Path.joinDirFile
 | |
|                   {dir = gccDir,
 | |
|                    file = s ^ "-" ^ gccFile}
 | |
|                end
 | |
|           | Self => !gcc
 | |
|       val arScript = !arScript
 | |
| 
 | |
|       fun addTargetOpts opts =
 | |
|          List.fold
 | |
|          (!opts, [], fn ({opt, pred}, ac) =>
 | |
|           if (case pred of
 | |
|                  OptPred.Target s =>
 | |
|                     let
 | |
|                        val s = String.toLower s
 | |
|                     in
 | |
|                        s = archStr orelse s = OSStr
 | |
|                     end
 | |
|                | OptPred.Yes => true)
 | |
|              then opt :: ac
 | |
|           else ac)
 | |
|       val asOpts = addTargetOpts asOpts
 | |
|       val ccOpts = addTargetOpts ccOpts
 | |
|       val ccOpts = concat ["-I",
 | |
|                            OS.Path.mkAbsolute { path = "include",
 | |
|                                                 relativeTo = !libTargetDir }]
 | |
|                    :: ccOpts
 | |
|       val linkOpts =
 | |
|          List.concat [[concat ["-L", !libTargetDir]],
 | |
|                       if !debugRuntime then
 | |
|                       ["-lmlton-gdb", "-lgdtoa-gdb"]
 | |
|                       else if positionIndependent then
 | |
|                       ["-lmlton-pic", "-lgdtoa-pic"]
 | |
|                       else
 | |
|                       ["-lmlton", "-lgdtoa"],
 | |
|                       addTargetOpts linkOpts]
 | |
|       val linkArchives =
 | |
|          if !debugRuntime then
 | |
|          [OS.Path.joinDirFile { dir = !libTargetDir, file = "libmlton-gdb.a" },
 | |
|           OS.Path.joinDirFile { dir = !libTargetDir, file = "libgdtoa-gdb.a" }]
 | |
|          else if positionIndependent then
 | |
|          [OS.Path.joinDirFile { dir = !libTargetDir, file = "libmlton-pic.a" },
 | |
|           OS.Path.joinDirFile { dir = !libTargetDir, file = "libgdtoa-pic.a" }]
 | |
|          else
 | |
|          [OS.Path.joinDirFile { dir = !libTargetDir, file =  "libmlton.a" },
 | |
|           OS.Path.joinDirFile { dir = !libTargetDir, file =  "libgdtoa.a" }]
 | |
|       val _ =
 | |
|          if not (hasCodegen (!codegen))
 | |
|             then usage (concat ["can't use ",
 | |
|                                 Control.Codegen.toString (!codegen),
 | |
|                                 " codegen on ",
 | |
|                                 MLton.Platform.Arch.toString targetArch,
 | |
|                                 " target"])
 | |
|          else ()
 | |
|       val () =
 | |
|          Control.labelsHaveExtra_ := (case targetOS of
 | |
|                                          Cygwin => true
 | |
|                                        | Darwin => true
 | |
|                                        | MinGW => true
 | |
|                                        | _ => false)
 | |
|       val _ =
 | |
|          chunk :=
 | |
|          (case !explicitChunk of
 | |
|              NONE => (case !codegen of
 | |
|                          amd64Codegen => ChunkPerFunc
 | |
|                        | CCodegen => Coalesce {limit = 4096}
 | |
|                        | x86Codegen => ChunkPerFunc
 | |
|                        )
 | |
|            | SOME c => c)
 | |
|       val _ = if not (!Control.codegen = x86Codegen) andalso !Native.IEEEFP
 | |
|                  then usage "must use x86 codegen with -ieee-fp true"
 | |
|               else ()
 | |
|       val _ =
 | |
|          if !keepDot andalso List.isEmpty (!keepPasses)
 | |
|             then keepSSA := true
 | |
|          else ()
 | |
|       val () =
 | |
|          keepDefUse
 | |
|          := (isSome (!showDefUse)
 | |
|              orelse (Control.Elaborate.enabled Control.Elaborate.warnUnused)
 | |
|              orelse (Control.Elaborate.default Control.Elaborate.warnUnused))
 | |
|       val warnMatch =
 | |
|           (Control.Elaborate.enabled Control.Elaborate.nonexhaustiveMatch)
 | |
|           orelse (Control.Elaborate.enabled Control.Elaborate.redundantMatch)
 | |
|           orelse (Control.Elaborate.default Control.Elaborate.nonexhaustiveMatch <>
 | |
|                   Control.Elaborate.DiagEIW.Ignore)
 | |
|           orelse (Control.Elaborate.default Control.Elaborate.redundantMatch <>
 | |
|                   Control.Elaborate.DiagEIW.Ignore)
 | |
|       val _ = elaborateOnly := (stop = Place.TypeCheck
 | |
|                                 andalso not (warnMatch)
 | |
|                                 andalso not (!keepDefUse))
 | |
|       val _ =
 | |
|          case targetOS of
 | |
|             Darwin => ()
 | |
|           | FreeBSD => ()
 | |
|           | HPUX => ()
 | |
|           | Linux => ()
 | |
|           | MinGW => ()
 | |
|           | NetBSD => ()
 | |
|           | OpenBSD => ()
 | |
|           | Solaris => ()
 | |
|           | _ =>
 | |
|                if !profile = ProfileTimeField
 | |
|                   orelse !profile = ProfileTimeLabel
 | |
|                   then usage (concat ["can't use -profile time on ",
 | |
|                                       MLton.Platform.OS.toString targetOS])
 | |
|                else ()
 | |
|       fun printVersion (out: Out.t): unit =
 | |
|          Out.output (out, concat [Version.banner, "\n"])
 | |
|       val () =
 | |
|          case !show of
 | |
|             NONE => ()
 | |
|           | SOME info =>
 | |
|             (case info of
 | |
|                 Show.Anns =>
 | |
|                 Layout.outputl (Control.Elaborate.document {expert = !expert},
 | |
|                                 Out.standard)
 | |
|               | Show.PathMap =>
 | |
|                 let
 | |
|                    open Layout
 | |
|                 in
 | |
|                    outputl (align
 | |
|                             (List.map (Control.mlbPathMap (),
 | |
|                                        fn {var, path, ...} =>
 | |
|                                        str (concat [var, " ", path]))),
 | |
|                             Out.standard)
 | |
|                 end
 | |
|              ; let open OS.Process in exit success end)
 | |
|    in
 | |
|       case result of
 | |
|       Result.No msg => usage msg
 | |
|     | Result.Yes [] =>
 | |
|          (inputFile := "<none>"
 | |
|           ; if isSome (!showBasis)
 | |
|                then (trace (Top, "Type Check SML")
 | |
|                      Compile.elaborateSML {input = []})
 | |
|             else if !buildConstants
 | |
|                then Compile.outputBasisConstants Out.standard
 | |
|             else if !verbosity = Silent orelse !verbosity = Top
 | |
|                then printVersion Out.standard
 | |
|             else outputHeader' (No, Out.standard))
 | |
|     | Result.Yes (input :: rest) =>
 | |
|          let
 | |
|             val _ = inputFile := File.base (File.fileOf input)
 | |
|             val (start, base) =
 | |
|                let
 | |
|                   val rec loop =
 | |
|                      fn [] => usage (concat ["invalid file suffix on ", input])
 | |
|                       | (suf, start, hasNum) :: sufs =>
 | |
|                            if String.hasSuffix (input, {suffix = suf})
 | |
|                               then (start,
 | |
|                                     let
 | |
|                                        val f = File.base input
 | |
|                                     in
 | |
|                                        if hasNum
 | |
|                                           then File.base f
 | |
|                                        else f
 | |
|                                     end)
 | |
|                            else loop sufs
 | |
|                   datatype z = datatype Place.t
 | |
|                in
 | |
|                   loop [(".mlb", MLB, false),
 | |
|                         (".sml", SML, false),
 | |
|                         (".c", Generated, true),
 | |
|                         (".o", O, true)]
 | |
|                end
 | |
|             val _ =
 | |
|                List.foreach
 | |
|                (rest, fn f =>
 | |
|                 if List.exists ([".c", ".o", ".s", ".S"], fn suffix =>
 | |
|                                 String.hasSuffix (f, {suffix = suffix}))
 | |
|                    then File.withIn (f, fn _ => ())
 | |
|                 else usage (concat ["invalid file suffix: ", f]))
 | |
|             val csoFiles = rest
 | |
|          in
 | |
|             case Place.compare (start, stop) of
 | |
|                GREATER => usage (concat ["cannot go from ", Place.toString start,
 | |
|                                          " to ", Place.toString stop])
 | |
|              | EQUAL => usage "nothing to do"
 | |
|              | LESS =>
 | |
|                   let
 | |
|                      val _ =
 | |
|                         if !verbosity = Top
 | |
|                            then printVersion Out.error
 | |
|                         else ()
 | |
|                      val tempFiles: File.t list ref = ref []
 | |
|                      val tmpDir =
 | |
|                         let
 | |
|                            val (tmpVar, default) =
 | |
|                               case MLton.Platform.OS.host of
 | |
|                                  MinGW => ("TEMP", "C:/WINDOWS/TEMP")
 | |
|                                | _ => ("TMPDIR", "/tmp")
 | |
|                         in
 | |
|                            case Process.getEnv tmpVar of
 | |
|                               NONE => default
 | |
|                             | SOME d => d
 | |
|                         end
 | |
|                      fun temp (suf: string): File.t =
 | |
|                         let
 | |
|                            val (f, out) =
 | |
|                               File.temp {prefix = OS.Path.concat (tmpDir, "file"),
 | |
|                                          suffix = suf}
 | |
|                            val _ = Out.close out
 | |
|                            val _ = List.push (tempFiles, f)
 | |
|                         in
 | |
|                            f
 | |
|                         end
 | |
|                      fun suffix s = concat [base, s]
 | |
|                      fun maybeOut suf =
 | |
|                         case !output of
 | |
|                            NONE => suffix suf
 | |
|                          | SOME f => f
 | |
|                      fun maybeOutBase suf =
 | |
|                         case !output of
 | |
|                            NONE => suffix suf
 | |
|                          | SOME f => if File.extension f = SOME "exe"
 | |
|                                         then concat [File.base f, suf]
 | |
|                                      else concat [f, suf]
 | |
|                      val { base = outputBase, ext=_ } =
 | |
|                         OS.Path.splitBaseExt (maybeOut ".ext")
 | |
|                      val { file = defLibname, dir=_ } =
 | |
|                         OS.Path.splitDirFile outputBase
 | |
|                      val defLibname =
 | |
|                         if String.hasPrefix (defLibname, {prefix = "lib"})
 | |
|                         then String.extract (defLibname, 3, NONE)
 | |
|                         else defLibname
 | |
|                      fun toAlNum c = if Char.isAlphaNum c then c else #"_"
 | |
|                      val () =
 | |
|                         if !libname <> "" then () else
 | |
|                         libname := CharVector.map toAlNum defLibname
 | |
|                      (* Library output includes a header by default *)
 | |
|                      val () =
 | |
|                         case (!format, !exportHeader) of
 | |
|                            (Executable, _) => ()
 | |
|                          | (_, NONE) => exportHeader := SOME (!libname ^ ".h")
 | |
|                          | _ => ()
 | |
|                      val _ =
 | |
|                         atMLtons :=
 | |
|                         Vector.fromList
 | |
|                         (maybeOut "" :: tokenize (rev ("--" :: (!runtimeArgs))))
 | |
|                      (* The -Wa,--gstabs says to pass the --gstabs option to the
 | |
|                       * assembler. This tells the assembler to generate stabs
 | |
|                       * debugging information for each assembler line.
 | |
|                       *)
 | |
|                      val (gccDebug, asDebug) =
 | |
|                         case !debugFormat of
 | |
|                            NONE => (["-g"], "-Wa,-g")
 | |
|                          | SOME Dwarf => (["-gdwarf", "-g2"], "-Wa,--gdwarf2")
 | |
|                          | SOME DwarfPlus => (["-gdwarf+", "-g2"], "-Wa,--gdwarf2")
 | |
|                          | SOME Dwarf2 => (["-gdwarf-2", "-g2"], "-Wa,--gdwarf2")
 | |
|                          | SOME Stabs => (["-gstabs", "-g2"], "-Wa,--gstabs")
 | |
|                          | SOME StabsPlus => (["-gstabs+", "-g2"], "-Wa,--gstabs")
 | |
|                      fun compileO (inputs: File.t list): unit =
 | |
|                         let
 | |
|                            val output =
 | |
|                               case (!format, targetOS) of
 | |
|                                  (Archive, _) => maybeOut ".a"
 | |
|                                | (Executable, _) => maybeOut ""
 | |
|                                | (LibArchive, _) => maybeOut ".a"
 | |
|                                | (Library, Darwin) => maybeOut ".dylib"
 | |
|                                | (Library, Cygwin) => !libname ^ ".dll"
 | |
|                                | (Library, MinGW)  => !libname ^ ".dll"
 | |
|                                | (Library, _) => maybeOut ".so"
 | |
|                            val libOpts =
 | |
|                               case targetOS of
 | |
|                                  Darwin => [ "-dynamiclib" ]
 | |
|                                | Cygwin =>  [ "-shared",
 | |
|                                               "-Wl,--out-implib," ^
 | |
|                                                  maybeOut ".a",
 | |
|                                               "-Wl,--output-def," ^
 | |
|                                                  !libname ^ ".def"]
 | |
|                                | MinGW =>  [ "-shared",
 | |
|                                              "-Wl,--out-implib," ^
 | |
|                                                 maybeOut ".a",
 | |
|                                              "-Wl,--output-def," ^
 | |
|                                                 !libname ^ ".def"]
 | |
|                                | _ =>      [ "-shared" ]
 | |
|                            val _ =
 | |
|                               trace (Top, "Link")
 | |
|                               (fn () =>
 | |
|                                if !format = Archive orelse
 | |
|                                   !format = LibArchive
 | |
|                                then System.system
 | |
|                                     (arScript,
 | |
|                                      List.concat
 | |
|                                       [[targetStr, OSStr, output],
 | |
|                                        inputs,
 | |
|                                        linkArchives])
 | |
|                                else System.system
 | |
|                                     (gcc,
 | |
|                                      List.concat
 | |
|                                       [["-o", output],
 | |
|                                        if !format = Library then libOpts else [],
 | |
|                                        if !debug then gccDebug else [],
 | |
|                                        inputs,
 | |
|                                        linkOpts]))
 | |
|                               ()
 | |
|                            (* gcc on Cygwin appends .exe, which I don't want, so
 | |
|                             * move the output file to it's rightful place.
 | |
|                             * Notice that we do not use targetOS here, since we
 | |
|                             * care about the platform we're running on, not the
 | |
|                             * platform we're generating for.
 | |
|                             *
 | |
|                             * We want to keep the .exe as is for MinGW/Win32.
 | |
|                             *)
 | |
|                            val _ =
 | |
|                               if MLton.Platform.OS.host = Cygwin
 | |
|                                  then
 | |
|                                     if String.contains (output, #".")
 | |
|                                        then ()
 | |
|                                     else
 | |
|                                        File.move {from = concat [output, ".exe"],
 | |
|                                                   to = output}
 | |
|                               else ()
 | |
|                         in
 | |
|                            ()
 | |
|                         end
 | |
|                   fun mkOutputO (c: Counter.t, input: File.t): File.t =
 | |
|                      if stop = Place.O orelse !keepO
 | |
|                         then
 | |
|                            if File.dirOf input = File.dirOf (maybeOutBase ".o")
 | |
|                               then
 | |
|                                  concat [File.base input, ".o"]
 | |
|                               else
 | |
|                                  maybeOutBase
 | |
|                                     (concat [".",
 | |
|                                              Int.toString (Counter.next c),
 | |
|                                              ".o"])
 | |
|                         else temp ".o"
 | |
|                   fun compileC (c: Counter.t, input: File.t): File.t =
 | |
|                      let
 | |
|                         val debugSwitches = gccDebug @ ["-DASSERT=1"]
 | |
|                         val output = mkOutputO (c, input)
 | |
| 
 | |
|                         val _ =
 | |
|                            System.system
 | |
|                             (gcc,
 | |
|                              List.concat
 | |
|                              [[ "-std=gnu99", "-c" ],
 | |
|                               if !format = Executable
 | |
|                               then [] else [ "-DLIBNAME=" ^ !libname ],
 | |
|                               if positionIndependent
 | |
|                               then [ "-fPIC", "-DPIC" ] else [],
 | |
|                               if !debug then debugSwitches else [],
 | |
|                               ccOpts,
 | |
|                               ["-o", output],
 | |
|                               [input]])
 | |
|                      in
 | |
|                         output
 | |
|                      end
 | |
|                   fun compileS (c: Counter.t, input: File.t): File.t =
 | |
|                      let
 | |
|                         val output = mkOutputO (c, input)
 | |
|                         val _ =
 | |
|                            System.system
 | |
|                            (gcc,
 | |
|                             List.concat
 | |
|                             [["-c"],
 | |
|                              if !debug then [asDebug] else [],
 | |
|                              asOpts,
 | |
|                              ["-o", output],
 | |
|                              [input]])
 | |
|                      in
 | |
|                         output
 | |
|                      end
 | |
|                   fun compileCSO (inputs: File.t list): unit =
 | |
|                      if List.forall (inputs, fn f =>
 | |
|                                      SOME "o" = File.extension f)
 | |
|                         then compileO inputs
 | |
|                      else
 | |
|                      let
 | |
|                         val c = Counter.new 0
 | |
|                         val oFiles =
 | |
|                            trace (Top, "Compile and Assemble")
 | |
|                            (fn () =>
 | |
|                             List.fold
 | |
|                             (inputs, [], fn (input, ac) =>
 | |
|                              let
 | |
|                                 val extension = File.extension input
 | |
|                              in
 | |
|                                 if SOME "o" = extension
 | |
|                                    then input :: ac
 | |
|                                 else if SOME "c" = extension
 | |
|                                    then (compileC (c, input)) :: ac
 | |
|                                 else if SOME "s" = extension
 | |
|                                         orelse SOME "S" = extension
 | |
|                                    then (compileS (c, input)) :: ac
 | |
|                                 else Error.bug
 | |
|                                      (concat
 | |
|                                       ["invalid extension: ",
 | |
|                                        Option.toString (fn s => s) extension])
 | |
|                              end))
 | |
|                            ()
 | |
|                      in
 | |
|                         case stop of
 | |
|                            Place.O => ()
 | |
|                          | _ => compileO (rev oFiles)
 | |
|                      end
 | |
|                   fun mkCompileSrc {listFiles, elaborate, compile} input =
 | |
|                      let
 | |
|                         val outputs: File.t list ref = ref []
 | |
|                         val r = ref 0
 | |
|                         fun make (style: style, suf: string) () =
 | |
|                            let
 | |
|                               val suf = concat [".", Int.toString (!r), suf]
 | |
|                               val _ = Int.inc r
 | |
|                               val file = (if !keepGenerated
 | |
|                                              orelse stop = Place.Generated
 | |
|                                              then maybeOutBase
 | |
|                                           else temp) suf
 | |
|                               val _ = List.push (outputs, file)
 | |
|                               val out = Out.openOut file
 | |
|                               fun print s = Out.output (out, s)
 | |
|                               val _ = outputHeader' (style, out)
 | |
|                               fun done () = Out.close out
 | |
|                            in
 | |
|                               {file = file,
 | |
|                                print = print,
 | |
|                                done = done}
 | |
|                            end
 | |
|                         val _ =
 | |
|                            case !verbosity of
 | |
|                               Silent => ()
 | |
|                             | Top => ()
 | |
|                             | _ =>
 | |
|                                  outputHeader
 | |
|                                  (Control.No, fn l =>
 | |
|                                   let val out = Out.error
 | |
|                                   in Layout.output (l, out)
 | |
|                                      ; Out.newline out
 | |
|                                   end)
 | |
|                         val _ =
 | |
|                            case stop of
 | |
|                               Place.Files =>
 | |
|                                  Vector.foreach
 | |
|                                  (listFiles {input = input}, fn f =>
 | |
|                                   (print (String.translate
 | |
|                                           (f, fn #"\\" => "/" | c => str c))
 | |
|                                    ; print "\n"))
 | |
|                             | Place.TypeCheck =>
 | |
|                                  trace (Top, "Type Check SML")
 | |
|                                  elaborate
 | |
|                                  {input = input}
 | |
|                             | _ =>
 | |
|                                  trace (Top, "Compile SML")
 | |
|                                  compile
 | |
|                                  {input = input,
 | |
|                                   outputC = make (Control.C, ".c"),
 | |
|                                   outputS = make (Control.Assembly, ".s")}
 | |
|                      in
 | |
|                         case stop of
 | |
|                            Place.Files => ()
 | |
|                          | Place.TypeCheck => ()
 | |
|                          | Place.Generated => ()
 | |
|                          | _ =>
 | |
|                               (* Shrink the heap before calling gcc. *)
 | |
|                               (MLton.GC.pack ()
 | |
|                                ; compileCSO (List.concat [!outputs, csoFiles]))
 | |
|                      end
 | |
|                   val compileSML =
 | |
|                      mkCompileSrc {listFiles = fn {input} => Vector.fromList input,
 | |
|                                    elaborate = Compile.elaborateSML,
 | |
|                                    compile = Compile.compileSML}
 | |
|                   val compileMLB =
 | |
|                      mkCompileSrc {listFiles = Compile.sourceFilesMLB,
 | |
|                                    elaborate = Compile.elaborateMLB,
 | |
|                                    compile = Compile.compileMLB}
 | |
|                   fun compile () =
 | |
|                      case start of
 | |
|                         Place.SML => compileSML [input]
 | |
|                       | Place.MLB => compileMLB input
 | |
|                       | Place.Generated => compileCSO (input :: csoFiles)
 | |
|                       | Place.O => compileCSO (input :: csoFiles)
 | |
|                       | _ => Error.bug "invalid start"
 | |
|                   val doit
 | |
|                     = trace (Top, "MLton")
 | |
|                       (fn () =>
 | |
|                        Exn.finally
 | |
|                        (compile, fn () =>
 | |
|                         List.foreach (!tempFiles, File.remove)))
 | |
|                in
 | |
|                   doit ()
 | |
|                end
 | |
|          end
 | |
|    end
 | |
| 
 | |
| val commandLine = Process.makeCommandLine commandLine
 | |
| 
 | |
| val main = fn (_, args) => commandLine args
 | |
| 
 | |
| val mainWrapped = fn () => OS.Process.exit (commandLine (CommandLine.arguments ()))
 | |
| 
 | |
| end
 |