mlton(1)

πŸ“¦
version 2025 Β· 3 versions
, in arch rolling, nixos 26.05, nixpkgs unstable

πŸ’… mlton β€” like, the whole-program compiler for Standard ML, bestie

Okay so this is from the General Commands Manual, and it's about mlton, which is the whole-program compiler for the Standard ML (SML) programming language. Buckle up, she's thorough πŸ’–


✨ NAME ✨

mlton β€” whole-program compiler for the Standard ML (SML) programming language. That's it, that's the girl πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

mlton [option ...] file.{c|mlb|o|sml} [file.{c|o|s|S} ...]

🌸 DESCRIPTION 🌸

So here's the vibe: you run MLton from the command line with a whole lil collection of options, and then you follow that up with a file name plus a list of files that you want to compile, assemble, and link with. Yeah, it's a whole ensemble cast 🫢

The absolute simplest case? You just run mlton foo.sml, where foo.sml contains a valid SML program, and boom β€” MLton compiles that program and produces an executable named foo. Iconic, no notes ✨

Lowkey important though: MLton does not support separate compilation, so that program has to be the entire program you wish to compile. Like, the whole thing. No splitting her up πŸ’€

That said β€” and this is the slay part β€” the program totally may refer to signatures and structures defined in the Basis Library. So you're not starting from nothing, bestie πŸ’…

🧚 Bigger programs, bigger energy

Larger programs that span, like, so many files can be compiled with the ML Basis system. In that case, mlton foo.mlb will compile the complete SML program described by the basis foo.mlb, which may specify both SML files and additional bases. See the MLton Guide for the details β€” she's got lore πŸ“–

⭐ What the compile-time options actually do

MLton's compile-time options are the main characters here, and they control:

  • the name of the output file πŸ“
  • the verbosity of compile-time messages πŸ—£οΈ
  • whether or not certain optimizations are performed πŸš€

They can also specify which intermediate files are saved, and they can stop the compilation process early at some intermediate pass β€” in which case, plot twist, compilation can be resumed by passing the generated files to MLton πŸ”₯

πŸ“ File suffixes, iykyk

MLton uses the input file suffix to determine the type of input program. The possibilities are:

  • .c
  • .mlb
  • .o
  • .s
  • .sml

πŸ™ˆ Little quality-of-life notes

With no arguments at all, MLton just prints the version number and exits. Short and sweet, love that for her πŸ’–

Want a usage message? Run MLton with an invalid switch, e.g. mlton -z. Yes, breaking it on purpose is the move 😭

Noted for the explanation below and also in the usage message: for flags that take a number of choices (e.g. {true|false}), the first value listed is the default. Memorize that, it comes up a lot πŸŽ€

...and that's where part 1 wraps, but don't go anywhere β€” more mlton coming right up πŸ¦‹

πŸ› οΈ Compile-time options πŸ› οΈ

Okay bestie, this is the big one β€” all the knobs you can turn while compiling. Buckle up πŸ’…

  • -align n β€” Lines up objects in memory on the alignment you pick (n is 4 or 8). The default? Honestly it depends on the architecture, no cap. πŸ¦‹
  • -as-opt option β€” Sneaks option through to gcc when it's compiling assembler code. Heads up though: if you actually want the option to reach the assembler, you gotta use gcc's -Wa, syntax. That's just how it is 😌
  • -cc cc β€” Tells MLton which executable to use as the C compiler. And like, it does triple duty β€” the C compiler also gets called to compile assembly (.s files) down to object code (.o files), and to link object code into your final executable. Main character energy tbh πŸ‘‘
  • -cc-opt option β€” Passes option to gcc when compiling C code. Slay.
  • -codegen {native|x86|amd64|c|llvm} β€” Picks how you want native object code generated: via amd64 assembly, C code, LLVM code, or x86 assembly. With -codegen native (which is -codegen amd64 or -codegen x86), MLton usually compiles faster and generates better code. Bestie, that's the good stuff ✨
  • -const 'name value' β€” Sets the value of a compile-time constant. Here's the whole list of what you can set, what it defaults to, and what it actually controls:
    • Exn.keepHistory {false|true} β€” Turns on MLton.Exn.history. Fair warning: setting this to true has a performance cost β€” both in the memory that exceptions eat up and in run time β€” because extra work has to happen at every exception construction, raise, and handle. So like, know what you're signing up for πŸ’€
  • -default-ann ann β€” Sets default ML Basis annotations. Like, -default-ann 'warnUnused true' makes unused variable warnings on by default. Any annotation in an ML Basis file can override these defaults though β€” defaults aren't the final word 🫢
  • -default-type type β€” Picks the default binding for a primitive type. For example, -default-type word64 makes the top-level type word and the top-level structure Word in the Basis Library equal to Word64.word and Word64:WORD, respectively. Same vibe with -default-type intinf β€” that makes the top-level type int and the top-level structure Int equal to IntInf.int and IntInf:INTEGER. Icons only 🌟
  • -disable-ann ann β€” Ignores the annotation you name in every single ML Basis File. So say you want to see absolutely all the match and unused warnings β€” you'd compile with -default-ann 'warnUnused true', -disable-ann forceUsed, -disable-ann nonexhaustiveMatch, -disable-ann redundantMatch, and -disable-ann warnUnused. That's the whole combo meal πŸ“
  • -export-header file β€” Writes C prototypes out to file for every function in your program that's exported from SML to C. Very demure, very mindful.
  • -ieee-fp {false|true} β€” Makes the x86 native code generator be all pedantic about following the IEEE floating point standard. By default it is not pedantic, purely because it costs performance. And btw this only does anything with -codegen x86 β€” otherwise it's just sitting there πŸ’…
  • -inline n β€” Sets the inlining threshold the optimizer uses. The threshold is like an approximate measure of how big a procedure's code is. Default is 320. Don't ask me to justify it, that's just the number πŸ”₯
  • -keep {g|o} β€” Saves the intermediate files. If you give no -keep argument at all, only the output file survives.
    • g β€” keeps the generated .c and .s files that get passed to gcc, plus the generated .ll files passed to llvm-as
    • o β€” keeps the object (.o) files
  • -link-opt option β€” Passes option to gcc when linking. This is your moment to specify library search paths, like -link-opt -Lpath, and libraries to link against, like -link-opt -lfoo β€” or honestly both at once, like -link-opt '-Lpath -lfoo'. If you want the option to reach the linker itself, you gotta go through gcc's -Wl, syntax, e.g. -link-opt '-Wl,--export-dynamic'. iykyk 🧚
  • -llvm-as llvm-as β€” Sets the executable for the LLVM .ll to .bc assembler. You can use this to pick a specific version of the tool (e.g., -llvm-as llvm-as-14). Obsessed with tool versioning fr fr πŸŽ€
  • -llvm-as-opt option β€” Passes option to llvm-as when it's assembling (.ll to .bc) LLVM code.
  • -llvm-llc llc β€” Sets the executable for the LLVM .bc to .o system compiler. Again, pick your specific tool version if you want (e.g., -llvm-llc llc-14). 🌈
  • -llvm-llc-opt option β€” Passes option to llc when it's compiling (.bc to .o) LLVM code.
  • -llvm-opt opt β€” Sets the executable for the LLVM .bc to .bc optimizer. Same deal β€” specific version if you need it (e.g., -llvm-opt opt-14). ⭐
  • -llvm-opt-opt option β€” Passes option to opt when it's optimizing (.bc to .bc) LLVM code.
  • -mlb-path-map file β€” Uses file as an ML Basis path map so you can define extra MLB path variables. And here's the cute part: you can use -mlb-path-map and -mlb-path-var as many times as you want, and variable definitions in later path maps take precedence over earlier ones. Last one wins, periodt πŸ‘‘
  • -mlb-path-var 'name value' β€” Defines one more MLB path variable. Same rules as above: multiple uses of -mlb-path-map and -mlb-path-var are allowed, and later definitions beat out earlier ones. πŸ’œ
  • -output file β€” Names your final output file. The default name is the input file name with its suffix chopped off and an appropriate, possibly empty, suffix added on. So like, it just does its thing ✨
  • -profile {no|alloc|count|time} β€” Builds an executable that gathers profiling data. When you run that executable, it spits out an mlmon.out file. The man page on mlprof explains how to get the actual info out of that file β€” go read it, bestie πŸ¦‹
  • -profile-branch {false|true} β€” If true, the profiler separately gathers profiling data for each branch of a function definition, case expression, and if expression. Every branch gets its own little spotlight 🌸
  • -profile-stack {false|true} β€” If true, the profiler gathers profiling data for all functions on the stack, not just the one currently executing. The whole squad, not just the main character 😭
  • -profile-val {false|true} β€” If true, the profiler separately gathers profiling data for each (expansive) val declaration. πŸŽ€
  • -runtime arg β€” Passes arg to the runtime system via @MLton. The argument gets processed before the other @MLton command line switches. You can use -runtime multiple times and all the arguments get passed in order. If the same runtime switch shows up more than once, the last setting is the one that takes effect. Oh and β€” you don't need to write the leading @MLton or the trailing -- yourself, those get supplied automatically 🫢

An argument to -runtime can totally contain spaces, and that makes the runtime treat it as a whole sequence of words. Like, this command line:

mlton -runtime 'ram-slop 0.4' foo.sml

will make foo run as if it had been called like:

foo @MLton ram-slop 0.4 --

Also! An executable created with -runtime stop doesn't process any @MLton arguments at all. Super useful when you're making an executable, e.g. echo, that has to treat @MLton like literally any other command-line argument:

% mlton -runtime stop echo.sml
% echo @MLton --
@MLton --

See? It just hands it right back. Iconic behavior πŸ’…

  • -show-basis file β€” Pretty prints to file the basis that your input program defines. So you can actually see it πŸ’–
  • -show-def-use file β€” Writes def-use info out to file. Every identifier that's defined shows up on a line, and then subsequent lines give you the position of each place it's used. It's giving detective work πŸ”
  • -stop {f|g|o|tc} β€” Tells MLton exactly when to stop doing things.
    • f β€” list of files on stdout (only makes sense when the input is foo.mlb)
    • g β€” the generated .c and .s files
    • o β€” the object (.o) files
    • tc β€” stop after type checking

And here's the good part: if you compile with -stop g or -stop o, you can pick compilation right back up by running MLton on the generated .c and .s files or the .o files. Resume-able, we love to see it 🌟

  • -target {self|...} β€” Generates an executable that runs on the platform you name. The default is self, which means "compile for the exact machine MLton is running on." If you wanna use any other target, you gotta install a cross compiler first β€” check the MLton Guide for the details, bestie πŸ“–
  • -target-as-opt target option β€” Just like -as-opt, it passes option to gcc when assembling β€” except it only passes it when the target architecture or operating system is target. Conditional vibes ✨
  • -target-cc-opt target option β€” Just like -cc-opt, it passes option to gcc when compiling C code β€” except it only passes it when the target architecture or operating system is target. πŸ¦‹
  • -target-link-opt target option β€” Just like -link-opt, it passes option to gcc when linking β€” except it only passes it when the target architecture or operating system is target. πŸ’œ
  • -verbose {0|1|2|3} β€” Controls how chatty MLton is about which passes are running. Default is 0.
    • 0 β€” silent 🀫
    • 1 β€” calls to compiler, assembler, and linker
    • 2 β€” everything in 1, plus intermediate compiler passes
    • 3 β€” everything in 2, plus some data structure sizes πŸ”₯

πŸ› οΈ Runtime system options πŸ› οΈ

Okay bestie, plot twist: executables produced by MLton also take command line arguments that control the runtime system. These arguments are optional, and they occur before the executable's usual arguments. To use these options, the first argument to the executable must literally be @MLton. The optional arguments then follow, must be terminated by --, and are followed by any arguments to the program. The optional arguments are not made available to the SML program via CommandLine.arguments. For example, a valid call to hello-world is:

hello-world @MLton gc-summary fixed-heap 10k -- a b c

In the above example, CommandLine.arguments () = ["a", "b", "c"]. 🫢

It is allowed to have a sequence of @MLton arguments, as in:

hello-world @MLton gc-summary -- @MLton fixed-heap 10k -- a b c

Run-time options can also control MLton, as in:

mlton @MLton fixed-heap 0.5g -- foo.sml
  • fixed-heap x{k|K|m|M|g|G} β€” Use a fixed size heap of size x, where x is a real number and the trailing letter indicates its units:
    • k or K β€” 1024
    • m or M β€” 1,048,576
    • g or G β€” 1,073,741,824

A value of 0 means to use almost all the RAM present on the machine. The heap size used by fixed-heap includes all memory allocated by SML code, including memory for the stack (or stacks, if there are multiple threads). It does not, however, include any memory used for code itself or memory used by C globals, the C stack, or malloc. πŸ’–

  • gc-messages β€” Print a message at the start and end of every garbage collection. πŸ—‘οΈβœ¨
  • gc-summary β€” Print a summary of garbage collection statistics upon program termination. πŸ“Š
  • gc-summary-file file β€” Print a summary of garbage collection statistics upon program termination to the file specified by file. πŸ“
  • load-world world β€” Restart the computation with the file specified by world, which must have been created by a call to MLton.World.save by the same executable. See the MLton Guide for details. 🌍
  • max-heap x{k|K|m|M|g|G} β€” Run the computation with an automatically resized heap that is never larger than x, where x is a real number and the trailing letter indicates the units as with fixed-heap. The heap size for max-heap is accounted for as with fixed-heap. πŸ“ˆ
  • may-page-heap {false|true} β€” Enable paging the heap to disk when unable to grow the heap to a desired size. πŸ’Ύ
  • no-load-world β€” Disable load-world. This can be used as an argument to the compiler via -runtime no-load-world to create executables that will not load a world. This may be useful to ensure that set-uid executables do not load some strange world. 🚫🌍
  • ram-slop x β€” Multiply x by the amount of RAM on the machine to obtain what the runtime views as the amount of RAM it can use. Typically x is less than 1, and is used to account for space used by other programs running on the same machine. 🧠
  • stop β€” Causes the runtime to stop processing @MLton arguments once the next -- is reached. This can be used as an argument to the compiler via -runtime stop to create executables that don't process any @MLton arguments. πŸ›‘

🩺 DIAGNOSTICS 🩺

MLton's type error messages are not in a form suitable for processing by Emacs. For details on how to fix this, see http://mlton.org/Emacs. 😭

πŸ‘€ SEE ALSO πŸ‘€

mlprof(1) and the MLton Guide. 🫢


This page is serving mlton(1) manual-page energy, dated December 30, 2024. Periodt. πŸ’…

πŸ“¦ where this page comes from