Files
linguist/samples/Standard ML/main.fun

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