1 (* Copyright (C) 2010-2011,2013-2018 Matthew Fluet.
2 * Copyright (C) 1999-2009 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
10 functor Main (S: MAIN_STRUCTS): MAIN =
15 structure Compile = Compile ()
19 datatype t = Files | Generated | MLB | O | OUT | SML | SXML | TypeCheck
40 fun compare (p, p') = Int.compare (toInt p, toInt p')
52 datatype t = Anns | PathMap
55 val cc: string list ref = ref ["cc"]
56 val arScript: string ref = ref "<unset>"
57 val asOpts: {opt: string, pred: OptPred.t} list ref = ref []
58 val ccOpts: {opt: string, pred: OptPred.t} list ref = ref []
59 val linkOpts: {opt: string, pred: OptPred.t} list ref = ref []
60 val llvm_as: string ref = ref "llvm-as"
61 val llvm_asOpts: {opt: string, pred: OptPred.t} list ref = ref []
62 val llvm_llc: string ref = ref "llc"
63 val llvm_llcOpts: {opt: string, pred: OptPred.t} list ref = ref []
64 val llvm_opt: string ref = ref "opt"
65 val llvm_optOpts: {opt: string, pred: OptPred.t} list ref = ref []
67 val buildConstants: bool ref = ref false
68 val debugRuntime: bool ref = ref false
69 val expert: bool ref = ref false
70 val explicitAlign: Control.align option ref = ref NONE
71 val explicitChunk: Control.chunk option ref = ref NONE
72 datatype explicitCodegen = Native | Explicit of Control.Codegen.t
73 val explicitCodegen: explicitCodegen option ref = ref NONE
74 val keepGenerated = ref false
76 val output: string option ref = ref NONE
77 val profileSet: bool ref = ref false
78 val profileTimeSet: bool ref = ref false
79 val runtimeArgs: string list ref = ref ["@MLton"]
80 val show: Show.t option ref = ref NONE
81 val stop = ref Place.OUT
83 fun parseMlbPathVar (line: String.t) =
84 case String.tokens (line, Char.isSpace) of
85 [var, path] => SOME {var = var, path = path}
88 fun readMlbPathMap (file: File.t) =
89 if not (File.canRead file) then
90 Error.bug (concat ["can't read MLB path map file: ", file])
93 (File.lines file, fn line =>
94 if String.forall (line, Char.isSpace)
97 case parseMlbPathVar line of
98 NONE => Error.bug (concat ["strange mlb path mapping: ",
102 val targetMap: unit -> {arch: MLton.Platform.Arch.t,
103 os: MLton.Platform.OS.t,
104 target: string} list =
109 OS.Path.mkAbsolute {path = "targets", relativeTo = !Control.libDir}
110 val potentialTargets = Dir.lsDirs targetsDir
111 fun targetMap target =
114 OS.Path.mkAbsolute {path = target, relativeTo = targetsDir}
116 OS.Path.joinDirFile {dir = targetDir, file = "os"}
118 OS.Path.joinDirFile {dir = targetDir, file = "arch"}
119 val os = File.contents osFile
120 val arch = File.contents archFile
121 val os = List.first (String.tokens (os, Char.isSpace))
122 val arch = List.first (String.tokens (arch, Char.isSpace))
124 case MLton.Platform.OS.fromString os of
125 NONE => Error.bug (concat ["strange os: ", os])
128 case MLton.Platform.Arch.fromString arch of
129 NONE => Error.bug (concat ["strange arch: ", arch])
132 SOME {arch = arch, os = os, target = target}
136 List.keepAllMap (potentialTargets, targetMap)
139 fun setTargetType (target: string, usage): unit =
140 case List.peek (targetMap (), fn {target = t, ...} => target = t) of
141 NONE => usage (concat ["invalid target: ", target])
142 | SOME {arch, os, ...} =>
150 fun hasCodegen (cg) =
152 datatype z = datatype Control.Target.arch
153 datatype z = datatype Control.Target.os
154 datatype z = datatype Control.Format.t
155 datatype z = datatype Control.codegen
157 case !Control.Target.arch of
162 AMD64Codegen => false
164 (* Darwin PIC doesn't work *)
165 !Control.Target.os <> Darwin orelse
166 !Control.format = Executable orelse
167 !Control.format = Archive
170 AMD64Codegen => false
171 | X86Codegen => false
174 fun hasNativeCodegen () =
176 datatype z = datatype Control.codegen
178 hasCodegen AMD64Codegen
179 orelse hasCodegen X86Codegen
183 fun defaultAlignIs8 () =
185 datatype z = datatype Control.Target.arch
187 case !Control.Target.arch of
200 fun makeOptions {usage} =
202 val usage = fn s => (ignore (usage s); raise Fail "unreachable")
203 fun reportAnnotation (s, flag, e) =
205 Control.Elaborate.Bad =>
206 usage (concat ["invalid -", flag, " flag: ", s])
207 | Control.Elaborate.Good _ => ()
208 | Control.Elaborate.Other =>
209 usage (concat ["invalid -", flag, " flag: ", s])
210 | Control.Elaborate.Proxy (ids, {deprecated}) =>
211 if deprecated andalso !Control.warnDeprecated
215 concat ["Warning: ", "deprecated annotation: ", s, ", use ",
216 List.toString Control.Elaborate.Id.name ids, ".\n"])
219 datatype z = datatype MLton.Platform.Arch.t
220 datatype z = datatype MLton.Platform.OS.t
221 fun tokenizeOpt f opts =
222 List.foreach (String.tokens (opts, Char.isSpace),
224 fun tokenizeTargetOpt f (target, opts) =
225 List.foreach (String.tokens (opts, Char.isSpace),
226 fn opt => f (target, opt))
231 (Normal, "align", if defaultAlignIs8 () then " {8|4}" else " {4|8}",
233 (SpaceString (fn s =>
238 | _ => usage (concat ["invalid -align flag: ",
240 (Expert, "ar-script", " <ar>", "path to a script producing archives",
241 SpaceString (fn s => arScript := s)),
242 (Normal, "as-opt", " <opt>", "pass option to assembler",
243 (SpaceString o tokenizeOpt)
244 (fn s => List.push (asOpts, {opt = s, pred = OptPred.Yes}))),
245 (Expert, "as-opt-quote", " <opt>", "pass (quoted) option to assembler",
247 (fn s => List.push (asOpts, {opt = s, pred = OptPred.Yes}))),
248 (Expert, "build-constants", " {false|true}",
249 "output C file that prints basis constants",
250 boolRef buildConstants),
251 (Expert, "cc", " <cc>", "set C compiler",
253 (fn s => cc := String.tokens (s, Char.isSpace))),
254 (Normal, "cc-opt", " <opt>", "pass option to C compiler",
255 (SpaceString o tokenizeOpt)
256 (fn s => List.push (ccOpts, {opt = s, pred = OptPred.Yes}))),
257 (Expert, "cc-opt-quote", " <opt>", "pass (quoted) option to C compiler",
259 (fn s => List.push (ccOpts, {opt = s, pred = OptPred.Yes}))),
260 (Expert, "chunkify", " {coalesce<n>|func|one}", "set chunkify method",
264 "func" => ChunkPerFunc
268 usage (concat ["invalid -chunkify flag: ", s])
270 if String.hasPrefix (s, {prefix = "coalesce"})
272 val s = String.dropPrefix (s, 8)
274 if String.forall (s, Char.isDigit)
275 then (case Int.fromString s of
283 (Expert, "closure-convert-globalize", " {true|false}",
284 "whether to globalize during closure conversion",
285 Bool (fn b => (closureConvertGlobalize := b))),
286 (Expert, "closure-convert-shrink", " {true|false}",
287 "whether to shrink during closure conversion",
288 Bool (fn b => (closureConvertShrink := b))),
293 (Native :: (List.map (Control.Codegen.all, Explicit)),
296 Native => if hasNativeCodegen () then SOME "native" else NONE
297 | Explicit cg => if hasCodegen cg
298 then SOME (Control.Codegen.toString cg)
302 "which code generator to use",
305 := SOME (if s = "native"
308 (Control.Codegen.all, fn cg =>
309 s = Control.Codegen.toString cg) of
310 SOME cg => Explicit cg
311 | NONE => usage (concat ["invalid -codegen flag: ", s]))))),
312 (Normal, "const", " '<name> <value>'", "set compile-time constant",
314 case String.tokens (s, Char.isSpace) of
316 Compile.setCommandLineConstant {name = name,
318 | _ => usage (concat ["invalid -const flag: ", s]))),
319 (Expert, "contify-into-main", " {false|true}",
320 "contify functions into main",
321 boolRef contifyIntoMain),
322 (Expert, "debug", " {false|true}", "produce executable with debug info",
323 Bool (fn b => (debug := b
324 ; debugRuntime := b))),
325 (Expert, "debug-runtime", " {false|true}", "produce executable with debug info",
326 boolRef debugRuntime),
328 val flag = "default-ann"
330 (Normal, flag, " <ann>", "set annotation default for mlb files",
332 (fn s => reportAnnotation (s, flag,
333 Control.Elaborate.processDefault s)))
335 (Normal, "default-type", " '<ty><N>'", "set default type",
338 "char8" => Control.defaultChar := s
339 | "int8" => Control.defaultInt := s
340 | "int16" => Control.defaultInt := s
341 | "int32" => Control.defaultInt := s
342 | "int64" => Control.defaultInt := s
343 | "intinf" => Control.defaultInt := s
344 | "real32" => Control.defaultReal := s
345 | "real64" => Control.defaultReal := s
346 | "widechar16" => Control.defaultWideChar := s
347 | "widechar32" => Control.defaultWideChar := s
348 | "word8" => Control.defaultWord := s
349 | "word16" => Control.defaultWord := s
350 | "word32" => Control.defaultWord := s
351 | "word64" => Control.defaultWord := s
352 | _ => usage (concat ["invalid -default-type flag: ", s])))),
353 (Expert, "diag-pass", " <pass>", "keep diagnostic info for pass",
356 (case Regexp.fromString s of
357 SOME (re,_) => let val re = Regexp.compileDFA re
358 in List.push (diagPasses, re)
360 | NONE => usage (concat ["invalid -diag-pass flag: ", s])))),
362 val flag = "disable-ann"
364 (Normal, flag, " <ann>", "disable annotation in mlb files",
367 reportAnnotation (s, flag,
368 Control.Elaborate.processEnabled (s, false))))
370 (Expert, "disable-pass", " <pass>", "disable optimization pass",
372 (fn s => (case Regexp.fromString s of
373 SOME (re,_) => let val re = Regexp.compileDFA re
374 in List.push (executePasses, (re, false))
376 | NONE => usage (concat ["invalid -disable-pass flag: ", s])))),
377 (Expert, "drop-pass", " <pass>", "disable optimization pass",
379 (fn s => (if !Control.warnDeprecated
382 "Warning: -drop-pass is deprecated. Use -disable-pass.\n")
384 case Regexp.fromString s of
385 SOME (re,_) => let val re = Regexp.compileDFA re
386 in List.push (executePasses, (re, false))
388 | NONE => usage (concat ["invalid -disable-pass flag: ", s])))),
390 val flag = "enable-ann"
392 (Expert, flag, " <ann>", "globally enable annotation",
395 reportAnnotation (s, flag,
396 Control.Elaborate.processEnabled (s, true))))
398 (Expert, "enable-pass", " <pass>", "enable optimization pass",
400 (fn s => (case Regexp.fromString s of
401 SOME (re,_) => let val re = Regexp.compileDFA re
402 in List.push (executePasses, (re, true))
404 | NONE => usage (concat ["invalid -enable-pass flag: ", s])))),
405 (Expert, "error-threshhold", " <n>", "error threshhold (20)",
406 intRef errorThreshhold),
407 (Expert, "emit-main", " {true|false}", "emit main() startup function",
409 (Expert, "expert", " {false|true}", "enable expert status",
411 (Normal, "export-header", " <file>", "write C header file for _export's",
412 SpaceString (fn s => exportHeader := SOME s)),
417 (Control.Format.all, fn cg => SOME (Control.Format.toString cg)),
420 "generated output format",
424 (Control.Format.all, fn cg =>
425 s = Control.Format.toString cg) of
427 | NONE => usage (concat ["invalid -format flag: ", s])))),
428 (Expert, "gc-check", " {limit|first|every}", "force GCs",
435 | _ => usage (concat ["invalid -gc-check flag: ", s])))),
436 (Normal, "ieee-fp", " {false|true}", "use strict IEEE floating-point",
437 boolRef Native.IEEEFP),
438 (Expert, "indentation", " <n>", "indentation level in ILs",
440 (Normal, "inline", " <n>", "set inlining threshold",
441 Int (fn i => inlineNonRec := {small = i,
442 product = #product (!inlineNonRec)})),
443 (Expert, "inline-into-main", " {true|false}",
444 "inline functions into main",
445 boolRef inlineIntoMain),
446 (Expert, "inline-leafa-loops", " {true|false}", "leaf inline loops",
449 {repeat, size, ...} =>
451 {loops = loops, repeat = repeat, size = size})),
452 (Expert, "inline-leafa-repeat", " {true|false}", "leaf inline repeat",
455 {loops, size, ...} =>
457 {loops = loops, repeat = repeat, size = size})),
458 (Expert, "inline-leafa-size", " <n>", "set leaf inlining threshold (20)",
461 {loops, repeat, ...} =>
463 {loops = loops, repeat = repeat,
466 else if String.forall (s, Char.isDigit)
467 then Int.fromString s
468 else (usage o concat)
469 ["invalid -inline-leaf-size flag: ", s])})),
470 (Expert, "inline-leafb-loops", " {true|false}", "leaf inline loops",
473 {repeat, size, ...} =>
475 {loops = loops, repeat = repeat, size = size})),
476 (Expert, "inline-leafb-repeat", " {true|false}", "leaf inline repeat",
479 {loops, size, ...} =>
481 {loops = loops, repeat = repeat, size = size})),
482 (Expert, "inline-leafb-size", " <n>", "set leaf inlining threshold (40)",
485 {loops, repeat, ...} =>
487 {loops = loops, repeat = repeat,
490 else if String.forall (s, Char.isDigit)
491 then Int.fromString s
492 else (usage o concat)
493 ["invalid -inline-leaf-size flag: ", s])})),
494 (Expert, "inline-nonrec-product", " <n>", "set inlining threshold (320)",
496 case !inlineNonRec of
498 inlineNonRec := {small = small, product = product})),
499 (Expert, "inline-nonrec-small", " <n>", "set inlining threshold (60)",
501 case !inlineNonRec of
503 inlineNonRec := {small = small, product = product})),
504 (Normal, "keep", " {g|o}", "save intermediate files",
507 "ast" => keepAST := true
508 | "core-ml" => keepCoreML := true
509 | "dot" => keepDot := true
510 | "g" => keepGenerated := true
511 | "machine" => keepMachine := true
512 | "o" => keepO := true
513 | "rssa" => keepRSSA := true
514 | "ssa" => keepSSA := true
515 | "ssa2" => keepSSA2 := true
516 | "sxml" => keepSXML := true
517 | "xml" => keepXML := true
518 | _ => usage (concat ["invalid -keep flag: ", s]))),
519 (Expert, "keep-pass", " <pass>", "keep the results of pass",
521 (fn s => (case Regexp.fromString s of
522 SOME (re,_) => let val re = Regexp.compileDFA re
523 in List.push (keepPasses, re)
525 | NONE => usage (concat ["invalid -keep-pass flag: ", s])))),
526 (Expert, "layout-width", " <n>", "target width for pretty printer",
529 then Layout.setDefaultWidth n
530 else usage (concat ["invalid -layout-width arg: ", Int.toString n]))),
531 (Expert, "libname", " <basename>", "the name of the generated library",
532 SpaceString (fn s => libname := s)),
533 (Normal, "link-opt", " <opt>", "pass option to linker",
534 (SpaceString o tokenizeOpt)
535 (fn s => List.push (linkOpts, {opt = s, pred = OptPred.Yes}))),
536 (Expert, "link-opt-quote", " <opt>", "pass (quoted) option to linker",
538 (fn s => List.push (linkOpts, {opt = s, pred = OptPred.Yes}))),
539 (Expert, "llvm-as", " <llvm-as>", "path to llvm .ll -> .bc assembler",
540 SpaceString (fn s => llvm_as := s)),
541 (Normal, "llvm-as-opt", " <opt>", "pass option to llvm assembler",
542 (SpaceString o tokenizeOpt)
543 (fn s => List.push (llvm_asOpts, {opt = s, pred = OptPred.Yes}))),
544 (Expert, "llvm-as-opt-quote", " <opt>", "pass (quoted) option to llvm assembler",
546 (fn s => List.push (llvm_asOpts, {opt = s, pred = OptPred.Yes}))),
547 (Expert, "llvm-llc", " <llc>", "path to llvm .bc -> .o compiler",
548 SpaceString (fn s => llvm_llc := s)),
549 (Normal, "llvm-llc-opt", " <opt>", "pass option to llvm compiler",
550 (SpaceString o tokenizeOpt)
551 (fn s => List.push (llvm_llcOpts, {opt = s, pred = OptPred.Yes}))),
552 (Expert, "llvm-llc-opt-quote", " <opt>", "pass (quoted) option to llvm compiler",
554 (fn s => List.push (llvm_llcOpts, {opt = s, pred = OptPred.Yes}))),
555 (Expert, "llvm-opt", " <llvm-as>", "path to llvm .bc -> .bc optimizer",
556 SpaceString (fn s => llvm_opt := s)),
557 (Normal, "llvm-opt-opt", " <opt>", "pass option to llvm optimizer",
558 (SpaceString o tokenizeOpt)
559 (fn s => List.push (llvm_optOpts, {opt = s, pred = OptPred.Yes}))),
560 (Expert, "llvm-opt-opt-quote", " <opt>", "pass (quoted) option to llvm optimizer",
562 (fn s => List.push (llvm_optOpts, {opt = s, pred = OptPred.Yes}))),
563 (Expert, "loop-ssa-passes", " <n>", "loop ssa optimization passes (1)",
567 then loopSsaPasses := i
568 else usage (concat ["invalid -loop-ssa-passes arg: ", Int.toString i]))),
569 (Expert, "loop-ssa2-passes", " <n>", "loop ssa2 optimization passes (1)",
573 then loopSsa2Passes := i
574 else usage (concat ["invalid -loop-ssa2-passes arg: ", Int.toString i]))),
575 (Expert, "loop-unroll-limit", " <n>", "limit code growth by loop unrolling",
579 then loopUnrollLimit := i
580 else usage (concat ["invalid -loop-unroll-limit: ", Int.toString i]))),
581 (Expert, "loop-unswitch-limit", " <n>", "limit code growth by loop unswitching",
585 then loopUnswitchLimit := i
586 else usage (concat ["invalid -loop-unswitch-limit: ", Int.toString i]))),
587 (Expert, "mark-cards", " {true|false}", "mutator marks cards",
589 (Expert, "max-function-size", " <n>", "max function size (blocks)",
590 intRef maxFunctionSize),
591 (Normal, "mlb-path-map", " <file>", "additional MLB path map",
592 SpaceString (fn s => mlbPathVars := !mlbPathVars @ readMlbPathMap s)),
593 (Normal, "mlb-path-var", " '<name> <value>'", "additional MLB path var",
595 (fn s => mlbPathVars := !mlbPathVars @
596 [case parseMlbPathVar s of
597 NONE => Error.bug ("strange mlb path var: " ^ s)
599 (Expert, "native-commented", " <n>", "level of comments (0)",
600 intRef Native.commented),
601 (Expert, "native-copy-prop", " {true|false}",
602 "use copy propagation",
603 boolRef Native.copyProp),
604 (Expert, "native-cutoff", " <n>",
605 "live transfer cutoff distance",
606 intRef Native.cutoff),
607 (Expert, "native-live-transfer", " {0,...,8}",
609 intRef Native.liveTransfer),
610 (Expert, "native-live-stack", " {false|true}",
611 "track liveness of stack slots",
612 boolRef Native.liveStack),
613 (Expert, "native-move-hoist", " {true|false}",
615 boolRef Native.moveHoist),
616 (Expert, "native-optimize", " <n>", "level of optimizations",
617 intRef Native.optimize),
618 (Expert, "native-split", " <n>", "split assembly files at ~n lines",
619 Int (fn i => Native.split := SOME i)),
620 (Expert, "native-shuffle", " {true|false}",
621 "shuffle registers at C-calls",
622 Bool (fn b => Native.shuffle := b)),
623 (Expert, "opt-fuel", " <n>", "optimization 'fuel'",
624 Int (fn n => optFuel := SOME n)),
625 (Expert, "opt-passes", " {default|minimal}", "level of optimizations",
629 usage (concat ["invalid -opt-passes flag: ", s])
632 (!optimizationPasses, fn {il,set,...} =>
635 | Result.No s' => err (concat [s', "(for ", il, ")"]))
637 (Normal, "output", " <file>", "name of output file",
638 SpaceString (fn s => output := SOME s)),
639 (Expert, "polyvariance", " {true|false}", "use polyvariance",
640 Bool (fn b => if b then () else polyvariance := NONE)),
641 (Expert, "polyvariance-hofo", " {true|false}", "duplicate higher-order fns only",
643 case !polyvariance of
644 SOME {product, rounds, small, ...} =>
645 polyvariance := SOME {hofo = hofo,
650 (Expert, "polyvariance-product", " <n>", "set polyvariance threshold (300)",
652 case !polyvariance of
653 SOME {hofo, rounds, small, ...} =>
654 polyvariance := SOME {hofo = hofo,
659 (Expert, "polyvariance-rounds", " <n>", "set polyvariance rounds (2)",
661 case !polyvariance of
662 SOME {hofo, product, small, ...} =>
663 polyvariance := SOME {hofo = hofo,
668 (Expert, "polyvariance-small", " <n>", "set polyvariance threshold (30)",
670 case !polyvariance of
671 SOME {hofo, product, rounds, ...} =>
672 polyvariance := SOME {hofo = hofo,
677 (Expert, "prefer-abs-paths", " {false|true}",
678 "prefer absolute paths when referring to files",
679 boolRef preferAbsPaths),
680 (Expert, "prof-pass", " <pass>", "keep profile info for pass",
682 (case Regexp.fromString s of
683 SOME (re,_) => let val re = Regexp.compileDFA re
685 List.push (profPasses, re)
687 | NONE => usage (concat ["invalid -diag-pass flag: ", s])))),
688 (Normal, "profile", " {no|alloc|count|time}",
689 "produce executable suitable for profiling",
693 then usage "can't have multiple -profile switches"
696 ; profile := (case s of
698 | "alloc" => ProfileAlloc
699 | "call" => ProfileCallStack
700 | "count" => ProfileCount
701 | "drop" => ProfileDrop
702 | "label" => ProfileLabel
703 | "time" => (profileTimeSet := true
705 | "time-field" => ProfileTimeField
706 | "time-label" => ProfileTimeLabel
708 ["invalid -profile arg: ", s]))))),
709 (Normal, "profile-branch", " {false|true}",
710 "profile branches in addition to functions",
711 boolRef profileBranch),
712 (Expert, "profile-c", " <regexp>",
713 "include C-calls in files matching <regexp> in profile",
716 (case Regexp.fromString s of
719 val re = seq [anys, re, anys]
720 val re = compileDFA re
721 in List.push (profileC, re)
723 | NONE => usage (concat ["invalid -profile-c flag: ", s])))),
724 (Expert, "profile-exclude", " <regexp>",
725 "exclude files matching <regexp> from profile",
728 (case Regexp.fromString s of
731 val re = seq [anys, re, anys]
732 val re = compileDFA re
733 in List.push (profileInclExcl, (re, false))
735 | NONE => usage (concat ["invalid -profile-exclude flag: ", s])))),
736 (Expert, "profile-il", " {source}", "where to insert profile exps",
740 "source" => profileIL := ProfileSource
741 | "ssa" => profileIL := ProfileSSA
742 | "ssa2" => profileIL := ProfileSSA2
743 | _ => usage (concat ["invalid -profile-il arg: ", s]))),
744 (Expert, "profile-include", " <regexp>",
745 "include files matching <regexp> from profile",
748 (case Regexp.fromString s of
751 val re = seq [anys, re, anys]
752 val re = compileDFA re
753 in List.push (profileInclExcl, (re, true))
755 | NONE => usage (concat ["invalid -profile-include flag: ", s])))),
756 (Expert, "profile-raise", " {false|true}",
757 "profile raises in addition to functions",
758 boolRef profileRaise),
759 (Normal, "profile-stack", " {false|true}", "profile the stack",
760 boolRef profileStack),
761 (Normal, "profile-val", " {false|true}",
762 "profile val bindings in addition to functions",
764 (Normal, "runtime", " <arg>", "pass arg to runtime via @MLton",
765 SpaceString (fn s => List.push (runtimeArgs, s))),
766 (Expert, "seed-rand", " <w>", "seed the pseudo-random number generator",
768 (Expert, "show", " {anns|path-map}", "print specified data and stop",
771 show := SOME (case s of
773 | "path-map" => Show.PathMap
774 | _ => usage (concat ["invalid -show arg: ", s])))),
775 (Normal, "show-basis", " <file>", "write final basis environment",
776 SpaceString (fn s => showBasis := SOME s)),
777 (Expert, "show-basis-compact", " {false|true}", "show basis environment in compact form",
778 boolRef showBasisCompact),
779 (Expert, "show-basis-def", " {true|false}", "show basis environment with definition source position",
780 boolRef showBasisDef),
781 (Expert, "show-basis-flat", " {true|false}", "show basis environment with long identifier names",
782 boolRef showBasisFlat),
783 (Normal, "show-def-use", " <file>", "write def-use information",
784 SpaceString (fn s => showDefUse := SOME s)),
785 (Expert, "show-types", " {true|false}", "show types in ILs",
787 (Expert, "ssa-passes", " <passes>", "ssa optimization passes",
790 case List.peek (!Control.optimizationPasses,
791 fn {il, ...} => String.equals ("ssa", il)) of
795 | Result.No s' => usage (concat ["invalid -ssa-passes arg: ", s']))
796 | NONE => Error.bug "ssa optimization passes missing")),
797 (Expert, "ssa2-passes", " <passes>", "ssa2 optimization passes",
800 case List.peek (!Control.optimizationPasses,
801 fn {il, ...} => String.equals ("ssa2", il)) of
805 | Result.No s' => usage (concat ["invalid -ssa2-passes arg: ", s']))
806 | NONE => Error.bug "ssa2 optimization passes missing")),
807 (Normal, "stop", " {f|g|o|tc}", "when to stop",
812 | "g" => Place.Generated
814 | "tc" => Place.TypeCheck
815 | _ => usage (concat ["invalid -stop arg: ", s])))),
816 (Expert, "sxml-passes", " <passes>", "sxml optimization passes",
819 case List.peek (!Control.optimizationPasses,
820 fn {il, ...} => String.equals ("sxml", il)) of
824 | Result.No s' => usage (concat ["invalid -sxml-passes arg: ", s']))
825 | NONE => Error.bug "sxml optimization passes missing")),
828 (case targetMap () of
831 | x :: _ => concat [#target x, "|..."]),
833 "platform that executable will run on",
836 (target := (if t = "self" then Self else Cross t);
837 setTargetType (t, usage)))),
838 (Normal, "target-as-opt", " <target> <opt>", "target-dependent assembler option",
839 (SpaceString2 o tokenizeTargetOpt)
841 List.push (asOpts, {opt = opt, pred = OptPred.Target target}))),
842 (Expert, "target-as-opt-quote", " <target> <opt>", "target-dependent assembler option (quoted)",
845 List.push (asOpts, {opt = opt, pred = OptPred.Target target})))),
846 (Normal, "target-cc-opt", " <target> <opt>", "target-dependent C compiler option",
847 (SpaceString2 o tokenizeTargetOpt)
849 List.push (ccOpts, {opt = opt, pred = OptPred.Target target}))),
850 (Expert, "target-cc-opt-quote", " <target> <opt>", "target-dependent C compiler option (quoted)",
853 List.push (ccOpts, {opt = opt, pred = OptPred.Target target})))),
854 (Normal, "target-link-opt", " <target> <opt>", "target-dependent linker option",
855 (SpaceString2 o tokenizeTargetOpt)
857 List.push (linkOpts, {opt = opt, pred = OptPred.Target target}))),
858 (Expert, "target-link-opt-quote", " <target> <opt>", "target-dependent linker option (quoted)",
861 List.push (linkOpts, {opt = opt, pred = OptPred.Target target})))),
862 (Expert, "target-llvm-as-opt", " <target> <opt>", "target-dependent llvm assembler option",
863 (SpaceString2 o tokenizeTargetOpt)
864 (fn (target, opt) => List.push (llvm_asOpts, {opt = opt, pred = OptPred.Target target}))),
865 (Expert, "target-llvm-as-opt-quote", " <target> <opt>", "target-dependent llvm assembler option (quoted)",
867 (fn (target, opt) => List.push (llvm_asOpts, {opt = opt, pred = OptPred.Target target}))),
868 (Expert, "target-llvm-llc-opt", " <target> <opt>", "target-dependent llvm compiler option",
869 (SpaceString2 o tokenizeTargetOpt)
870 (fn (target, opt) => List.push (llvm_llcOpts, {opt = opt, pred = OptPred.Target target}))),
871 (Expert, "target-llvm-llc-opt-quote", " <target> <opt>", "target-dependent llvm compiler option (quoted)",
873 (fn (target, opt) => List.push (llvm_llcOpts, {opt = opt, pred = OptPred.Target target}))),
874 (Expert, "target-llvm-opt-opt", " <target> <opt>", "target-dependent llvm optimizer option",
875 (SpaceString2 o tokenizeTargetOpt)
876 (fn (target, opt) => List.push (llvm_optOpts, {opt = opt, pred = OptPred.Target target}))),
877 (Expert, "target-llvm-opt-opt-quote", " <target> <opt>", "target-dependent llvm optimizer option (quoted)",
879 (fn (target, opt) => List.push (llvm_optOpts, {opt = opt, pred = OptPred.Target target}))),
880 (Expert, #1 trace, " name1,...", "trace compiler internals", #2 trace),
881 (Expert, "type-check", " {false|true}", "type check ILs",
883 (Normal, "verbose", " {0|1|2|3}", "how verbose to be",
886 verbosity := (case s of
891 | _ => usage (concat ["invalid -verbose arg: ", s])))),
892 (Expert, "warn-ann", " {true|false}",
893 "unrecognized annotation warnings",
895 (Expert, "warn-deprecated", " {true|false}",
896 "deprecated feature warnings",
897 boolRef warnDeprecated),
898 (Expert, "xml-passes", " <passes>", "xml optimization passes",
901 case List.peek (!Control.optimizationPasses,
902 fn {il, ...} => String.equals ("xml", il)) of
906 | Result.No s' => usage (concat ["invalid -xml-passes arg: ", s']))
907 | NONE => Error.bug "xml optimization passes missing")),
908 (Expert, "zone-cut-depth", " <n>", "zone cut depth",
911 fn (style, name, arg, desc, opt) =>
912 {arg = arg, desc = desc, name = name, opt = opt, style = style})
916 "mlton [option ...] file.{c|mlb|o|sml} [file.{c|o|s|S} ...]"
919 Popt.makeUsage {mainUsage = mainUsage,
920 makeOptions = makeOptions,
921 showExpert = fn () => !expert}
923 val usage = fn s => (usage s; raise Fail "unreachable")
925 fun commandLine (args: string list): unit =
928 datatype z = datatype MLton.Platform.Arch.t
929 datatype z = datatype MLton.Platform.OS.t
933 (libDir := OS.Path.mkCanonical lib
935 | _ => Error.bug "incorrect args from shell script"
936 val () = setTargetType ("self", usage)
937 val result = parse args
945 OS.Path.mkAbsolute {path = "targets", relativeTo = !libDir}
947 OS.Path.mkAbsolute {path = targetStr, relativeTo = targetsDir}
948 val () = Control.libTargetDir := targetDir
950 OS.Path.mkAbsolute {path = "include", relativeTo = targetDir}
951 val targetLibDir = targetDir
952 val targetArch = !Target.arch
953 val targetArchStr = String.toLower (MLton.Platform.Arch.toString targetArch)
954 val targetOS = !Target.os
955 val targetOSStr = String.toLower (MLton.Platform.OS.toString targetOS)
956 val targetArchOSStr = concat [targetArchStr, "-", targetOSStr]
958 (* Determine whether code should be PIC (position independent) or not.
959 * This decision depends on the platform and output format.
961 val positionIndependent =
962 case (targetOS, targetArch, !format) of
963 (* Windows is never position independent *)
964 (MinGW, _, _) => false
965 | (Cygwin, _, _) => false
966 (* GCC on AMD64 now produces PIC by default in many Linux distros. *)
967 | (Linux, AMD64, _) => true
968 (* Technically, Darwin should always be PIC.
969 * However, PIC on i386/darwin is unimplemented so we avoid it.
970 * PowerPC PIC is bad too, but the C codegen will use PIC behind
971 * our back unless forced, so let's just admit that it's PIC.
973 | (Darwin, X86, Executable) => false
974 | (Darwin, X86, Archive) => false
975 | (Darwin, _, _) => true
976 | (OpenBSD, _, _) => true
977 (* On ELF systems, we only need PIC for LibArchive/Library *)
978 | (_, _, Library) => true
979 | (_, _, LibArchive) => true
981 val () = Control.positionIndependent := positionIndependent
986 align := (case !explicitAlign of
987 NONE => if defaultAlignIs8 () then Align8 else Align4
990 codegen := (case !explicitCodegen of
992 if hasCodegen AMD64Codegen
994 else if hasCodegen X86Codegen
998 if hasCodegen AMD64Codegen
1000 else if hasCodegen X86Codegen
1002 else usage (concat ["can't use native codegen on ",
1003 MLton.Platform.Arch.toString targetArch,
1005 | SOME (Explicit cg) => cg)
1006 val () = MLton.Rusage.measureGC (!verbosity <> Silent)
1007 val () = if !profileTimeSet
1008 then (case !codegen of
1009 X86Codegen => profile := ProfileTimeLabel
1010 | AMD64Codegen => profile := ProfileTimeLabel
1011 | _ => profile := ProfileTimeField)
1013 val () = if !exnHistory
1014 then (case !profile of
1015 ProfileNone => profile := ProfileCallStack
1016 | ProfileCallStack => ()
1017 | _ => usage "can't use -profile with Exn.keepHistory"
1018 ; profileRaise := true)
1022 Compile.setCommandLineConstant
1023 {name = "CallStack.keep",
1024 value = Bool.toString (!Control.profile = Control.ProfileCallStack)}
1030 (File.lines (OS.Path.joinDirFile {dir = targetDir,
1033 case String.tokens (line, Char.isSpace) of
1035 (case Int.fromString size of
1036 NONE => Error.bug (concat ["strange size: ", size])
1038 (ty, Bytes.toBits (Bytes.fromInt size)))
1039 | _ => Error.bug (concat ["strange size mapping: ", line]))
1041 case List.peek (sizeMap, fn (ty, _) => String.equals (ty, ty')) of
1042 NONE => Error.bug (concat ["missing size mapping: ", ty'])
1043 | SOME (_, size) => size
1045 Control.Target.setSizes
1046 {arrayMetaData = lookup "arrayMetaData",
1047 cint = lookup "cint",
1048 cpointer = lookup "cpointer",
1049 cptrdiff = lookup "cptrdiff",
1050 csize = lookup "csize",
1051 header = lookup "header",
1052 mplimb = lookup "mplimb",
1053 normalMetaData = lookup "normalMetaData",
1054 objptr = lookup "objptr",
1055 seqIndex = lookup "seqIndex"}
1059 String.tokens (concat (List.separate (l, " ")), Char.isSpace)
1061 (* When cross-compiling, use the named cross compiler.
1062 * Older gcc versions used -b for multiple targets.
1063 * If this is still needed, a shell script wrapper can hide this.
1069 val {dir = ccDir, file = ccFile} =
1070 OS.Path.splitDirFile (hd (!cc))
1074 file = s ^ "-" ^ ccFile}
1079 val arScript = !arScript
1081 fun addTargetOpts opts =
1083 (!opts, [], fn ({opt, pred}, ac) =>
1087 val s = String.toLower s
1090 orelse s = targetArchStr
1091 orelse s = targetOSStr
1093 | OptPred.Yes => true)
1096 val asOpts = addTargetOpts asOpts
1097 val asOpts = if !debug
1098 then "-Wa,-g" :: asOpts
1100 val ccOpts = addTargetOpts ccOpts
1101 val ccOpts = ("-I" ^ targetIncDir) :: ccOpts
1102 val ccOpts = if !debug
1103 then "-g" :: "-DASSERT=1" :: ccOpts
1105 val linkOpts = addTargetOpts linkOpts
1106 val linkOpts = if !debugRuntime then
1107 "-lmlton-gdb" :: "-lgdtoa-gdb" :: linkOpts
1108 else if positionIndependent then
1109 "-lmlton-pic" :: "-lgdtoa-pic" :: linkOpts
1111 "-lmlton" :: "-lgdtoa" :: linkOpts
1112 val linkOpts = ("-L" ^ targetLibDir) :: linkOpts
1115 if !debugRuntime then
1116 [OS.Path.joinDirFile {dir = targetLibDir, file = "libmlton-gdb.a"},
1117 OS.Path.joinDirFile {dir = targetLibDir, file = "libgdtoa-gdb.a"}]
1118 else if positionIndependent then
1119 [OS.Path.joinDirFile {dir = targetLibDir, file = "libmlton-pic.a"},
1120 OS.Path.joinDirFile {dir = targetLibDir, file = "libgdtoa-pic.a"}]
1122 [OS.Path.joinDirFile {dir = targetLibDir, file = "libmlton.a"},
1123 OS.Path.joinDirFile {dir = targetLibDir, file = "libgdtoa.a"}]
1125 val llvm_as = !llvm_as
1126 val llvm_llc = !llvm_llc
1127 val llvm_opt = !llvm_opt
1128 val llvm_asOpts = addTargetOpts llvm_asOpts
1129 val llvm_llcOpts = addTargetOpts llvm_llcOpts
1130 val llvm_optOpts = addTargetOpts llvm_optOpts
1133 if not (hasCodegen (!codegen))
1134 then usage (concat ["can't use ",
1135 Control.Codegen.toString (!codegen),
1137 MLton.Platform.Arch.toString targetArch,
1141 Control.labelsHaveExtra_ := (case (targetOS, targetArch) of
1142 (Cygwin, X86) => true
1143 | (Darwin, _) => true
1144 | (MinGW, X86) => true
1148 (case !explicitChunk of
1149 NONE => (case !codegen of
1150 AMD64Codegen => ChunkPerFunc
1151 | CCodegen => Coalesce {limit = 4096}
1152 | LLVMCodegen => Coalesce {limit = 4096}
1153 | X86Codegen => ChunkPerFunc
1156 val _ = if not (!Control.codegen = X86Codegen) andalso !Native.IEEEFP
1157 then usage "must use x86 codegen with -ieee-fp true"
1160 if !keepDot andalso List.isEmpty (!keepPasses)
1161 then keepSSA := true
1165 := (isSome (!showDefUse)
1166 orelse (Control.Elaborate.enabled Control.Elaborate.warnUnused)
1167 orelse (Control.Elaborate.default Control.Elaborate.warnUnused))
1169 (Control.Elaborate.enabled Control.Elaborate.nonexhaustiveMatch)
1170 orelse (Control.Elaborate.enabled Control.Elaborate.redundantMatch)
1171 orelse (Control.Elaborate.default Control.Elaborate.nonexhaustiveMatch <>
1172 Control.Elaborate.DiagEIW.Ignore)
1173 orelse (Control.Elaborate.default Control.Elaborate.redundantMatch <>
1174 Control.Elaborate.DiagEIW.Ignore)
1175 val _ = elaborateOnly := (stop = Place.TypeCheck
1176 andalso not (warnMatch)
1177 andalso not (!keepDefUse))
1189 if !profile = ProfileTimeField
1190 orelse !profile = ProfileTimeLabel
1191 then usage (concat ["can't use -profile time on ",
1192 MLton.Platform.OS.toString targetOS])
1194 fun printVersion (out: Out.t): unit =
1195 Out.output (out, concat [Version.banner, "\n"])
1202 Layout.outputl (Control.Elaborate.document {expert = !expert},
1209 (List.revMap (Control.mlbPathMap (),
1210 fn {var, path, ...} =>
1211 str (concat [var, " ", path]))),
1214 ; let open OS.Process in exit success end)
1217 Result.No msg => usage msg
1219 (inputFile := "<none>"
1220 ; if isSome (!showBasis)
1221 then (trace (Top, "Type Check SML")
1222 Compile.elaborateSML {input = []})
1223 else if !buildConstants
1224 then Compile.outputBasisConstants Out.standard
1225 else if !verbosity = Silent orelse !verbosity = Top
1226 then printVersion Out.standard
1227 else outputHeader' (No, Out.standard))
1228 | Result.Yes (input :: rest) =>
1230 val _ = inputFile := File.base (File.fileOf input)
1234 fn [] => usage (concat ["invalid file suffix on ", input])
1235 | (suf, start, hasNum) :: sufs =>
1236 if String.hasSuffix (input, {suffix = suf})
1239 val f = File.base input
1246 datatype z = datatype Place.t
1248 loop [(".mlb", MLB, false),
1249 (".sml", SML, false),
1250 (".sxml", SXML, false),
1251 (".c", Generated, true),
1257 if List.exists ([".c", ".o", ".s", ".S"], fn suffix =>
1258 String.hasSuffix (f, {suffix = suffix}))
1259 then File.withIn (f, fn _ => ())
1260 else usage (concat ["invalid file suffix: ", f]))
1263 case Place.compare (start, stop) of
1264 GREATER => usage (concat ["cannot go from ", Place.toString start,
1265 " to ", Place.toString stop])
1266 | EQUAL => usage "nothing to do"
1271 then printVersion Out.error
1273 val tempFiles: File.t list ref = ref []
1276 val (tmpVar, default) =
1277 case MLton.Platform.OS.host of
1278 MinGW => ("TEMP", "C:/WINDOWS/TEMP")
1279 | _ => ("TMPDIR", "/tmp")
1281 case Process.getEnv tmpVar of
1285 fun temp (suf: string): File.t =
1288 File.temp {prefix = OS.Path.concat (tmpDir, "file"),
1290 val _ = Out.close out
1291 val _ = List.push (tempFiles, f)
1295 fun suffix s = concat [base, s]
1300 fun maybeOutBase suf =
1303 | SOME f => if File.extension f = SOME "exe"
1304 then concat [File.base f, suf]
1305 else concat [f, suf]
1306 val {base = outputBase, ...} =
1307 OS.Path.splitBaseExt (maybeOut ".ext")
1308 val {file = defLibname, ...} =
1309 OS.Path.splitDirFile outputBase
1311 if String.hasPrefix (defLibname, {prefix = "lib"})
1312 then String.extract (defLibname, 3, NONE)
1314 fun toAlNum c = if Char.isAlphaNum c then c else #"_"
1316 if !libname <> "" then () else
1317 libname := CharVector.map toAlNum defLibname
1318 (* Library output includes a header by default *)
1320 case (!format, !exportHeader) of
1321 (Executable, _) => ()
1322 | (_, NONE) => exportHeader := SOME (!libname ^ ".h")
1327 (tokenize (rev ("--" :: (!runtimeArgs))))
1328 val (ccDebug, asDebug) = (["-g", "-DASSERT=1"], "-Wa,-g")
1329 fun compileO (inputs: File.t list): unit =
1332 case (!format, targetOS) of
1333 (Archive, _) => maybeOut ".a"
1334 | (Executable, _) => maybeOut ""
1335 | (LibArchive, _) => maybeOut ".a"
1336 | (Library, Darwin) => maybeOut ".dylib"
1337 | (Library, Cygwin) => !libname ^ ".dll"
1338 | (Library, MinGW) => !libname ^ ".dll"
1339 | (Library, _) => maybeOut ".so"
1342 Darwin => [ "-dynamiclib" ]
1343 | Cygwin => [ "-shared",
1344 "-Wl,--out-implib," ^
1346 "-Wl,--output-def," ^
1348 | MinGW => [ "-shared",
1349 "-Wl,--out-implib," ^
1351 "-Wl,--output-def," ^
1353 | _ => [ "-shared" ]
1357 if !format = Archive orelse
1358 !format = LibArchive
1362 [[targetStr, targetOSStr, output],
1369 if !format = Library then libOpts else [],
1374 (* gcc on Cygwin appends .exe, which I don't want, so
1375 * move the output file to it's rightful place.
1376 * Notice that we do not use targetOS here, since we
1377 * care about the platform we're running on, not the
1378 * platform we're generating for.
1380 * We want to keep the .exe as is for MinGW/Win32.
1383 if MLton.Platform.OS.host = Cygwin
1385 if String.contains (output, #".")
1388 File.move {from = concat [output, ".exe"],
1394 fun mkOutputO (c: Counter.t, input: File.t): File.t =
1395 if stop = Place.O orelse !keepO
1397 if File.dirOf input = File.dirOf (maybeOutBase ".o")
1399 concat [File.base input, ".o"]
1403 Int.toString (Counter.next c),
1406 fun mkOutputBC (c: Counter.t, input: File.t, xsuf): File.t =
1407 if stop = Place.O orelse !keepO
1409 if File.dirOf input = File.dirOf (maybeOutBase (xsuf ^ ".bc"))
1411 concat [File.base input, xsuf, ".bc"]
1415 Int.toString (Counter.next c),
1418 else temp (xsuf ^ ".bc")
1419 fun compileC (c: Counter.t, input: File.t): File.t =
1421 val output = mkOutputO (c, input)
1428 if !format = Executable
1429 then [] else [ "-DLIBNAME=" ^ !libname ],
1430 if positionIndependent
1431 then [ "-fPIC", "-DPIC" ] else [],
1432 if !debug then ccDebug else [],
1439 fun compileS (c: Counter.t, input: File.t): File.t =
1441 val output = mkOutputO (c, input)
1448 if !debug then [asDebug] else [],
1455 fun compileLL (c: Counter.t, input: File.t): File.t =
1457 val asBC = mkOutputBC (c, input, ".as")
1465 val optBC = mkOutputBC (c, input, ".opt")
1473 val output = mkOutputO (c, input)
1485 fun compileCSO (inputs: File.t list): unit =
1486 if List.forall (inputs, fn f =>
1487 SOME "o" = File.extension f)
1488 then compileO inputs
1491 val c = Counter.new 0
1493 trace (Top, "Compile and Assemble")
1496 (inputs, [], fn (input, ac) =>
1498 val extension = File.extension input
1500 if SOME "o" = extension
1502 else if SOME "c" = extension
1503 then (compileC (c, input)) :: ac
1504 else if SOME "ll" = extension
1505 then (compileLL(c, input)) :: ac
1506 else if SOME "s" = extension
1507 orelse SOME "S" = extension
1508 then (compileS (c, input)) :: ac
1511 ["invalid extension: ",
1512 Option.toString (fn s => s) extension])
1518 | _ => compileO (rev oFiles)
1520 fun mkCompileSrc {listFiles, elaborate, compile} input =
1522 val outputs: File.t list ref = ref []
1524 fun make (style: style, suf: string) () =
1526 val suf = concat [".", Int.toString (!r), suf]
1528 val file = (if !keepGenerated
1529 orelse stop = Place.Generated
1532 val _ = List.push (outputs, file)
1533 val out = Out.openOut file
1534 fun print s = Out.output (out, s)
1535 val _ = outputHeader' (style, out)
1536 fun done () = Out.close out
1548 (Control.No, fn l =>
1549 let val out = Out.error
1550 in Layout.output (l, out)
1557 (listFiles {input = input}, fn f =>
1558 (print (String.translate
1559 (f, fn #"\\" => "/" | c => str c))
1561 | Place.TypeCheck =>
1562 trace (Top, "Type Check SML")
1566 trace (Top, "Compile SML")
1569 outputC = make (Control.C, ".c"),
1570 outputLL = make (Control.LLVM, ".ll"),
1571 outputS = make (Control.Assembly, ".s")}
1575 | Place.TypeCheck => ()
1576 | Place.Generated => ()
1578 (* Shrink the heap before calling C compiler. *)
1580 ; compileCSO (List.concat [!outputs, csoFiles]))
1583 mkCompileSrc {listFiles = fn {input} => Vector.fromList input,
1584 elaborate = Compile.elaborateSML,
1585 compile = Compile.compileSML}
1587 mkCompileSrc {listFiles = Compile.sourceFilesMLB,
1588 elaborate = Compile.elaborateMLB,
1589 compile = Compile.compileMLB}
1591 mkCompileSrc {listFiles = fn {input} => Vector.new1 input,
1592 elaborate = fn _ => raise Fail "Unimplemented",
1593 compile = Compile.compileSXML}
1596 Place.SML => compileSML [input]
1597 | Place.MLB => compileMLB input
1598 | Place.Generated => compileCSO (input :: csoFiles)
1599 | Place.O => compileCSO (input :: csoFiles)
1600 | Place.SXML => compileSXML input
1601 | _ => Error.bug "invalid start"
1603 = trace (Top, "MLton")
1607 List.foreach (!tempFiles, File.remove)))
1614 val commandLine = Process.makeCommandLine commandLine
1616 val main = fn (_, args) => commandLine args
1618 val mainWrapped = fn () => OS.Process.exit (commandLine (CommandLine.arguments ()))