1 (* Copyright (C
) 2009,2017 Matthew Fluet
.
2 * Copyright (C
) 1999-2008 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 structure Control
: CONTROL
=
17 datatype t
= datatype verbosity
20 fn (Silent
, _
) => true
21 |
(Top
, Silent
) => false
23 |
(Pass
, Pass
) => true
28 datatype style
= No | Assembly | C | Dot | LLVM | ML
32 | Assembly
=> ("/* ", " */")
36 | ML
=> ("(* ", " *)")
38 fun outputHeader (style
: style
, output
: Layout
.t
-> unit
) =
40 val (pre
, suf
) = preSuf style
43 :: concat
[" created this file on ", Date
.toString (Date
.now ()), "."]
44 :: "Do not edit this file."
46 :: (List.map (all (), fn {name
, value
} =>
47 concat
[" ", name
, ": ", value
]))
48 in List.foreach (lines
, fn l
=> output (Layout
.str (concat
[pre
, l
, suf
])))
51 fun outputHeader
' (style
, out
: Out
.t
) =
52 outputHeader (style
, fn l
=>
53 (Layout
.output (l
, out
);
56 val depth
: int ref
= ref
0
57 fun getDepth () = !depth
58 fun indent () = depth
:= !depth
+ 3
59 fun unindent () = depth
:= !depth
- 3
61 fun message (verb
: Verbosity
.t
, th
: unit
-> Layout
.t
): unit
=
62 if Verbosity
.<= (verb
, !verbosity
)
69 else (Layout
.output (Layout
.indent (lay
, !depth
), out
)
74 fun messageStr (verb
, s
: string): unit
=
75 message (verb
, fn () => Layout
.str s
)
80 val {children
, self
, gc
, ...} = times ()
81 fun add
{utime
, stime
} = utime
+ stime
83 (add self
+ add children
, add gc
)
86 fun timeToString
{total
, gc
} =
88 fun fmt (x
, n
) = Real.format (x
, Real.Format
.fix (SOME n
))
89 val toReal
= Real.fromIntInf
o Time
.toMilliseconds
91 if Time
.equals (total
, Time
.zero
)
93 else fmt (100.0 * (toReal gc
/ toReal total
), 0)
95 fmt (Real./ (toReal t
, 1000.0), 2)
96 in concat
[t2s (Time
.- (total
, gc
)), " + ", t2s gc
, " (", per
, "% GC)"]
99 fun trace (verb
, name
: string) (f
: 'a
-> 'b
) (a
: 'a
): 'b
=
100 if Verbosity
.<= (verb
, !verbosity
)
103 val _
= messageStr (verb
, concat
[name
, " starting"])
104 val (t
, gc
) = time ()
109 val (t
', gc
') = time ()
111 timeToString
{total
= Time
.- (t
', t
),
112 gc
= Time
.- (gc
', gc
)}
115 before messageStr (verb
, concat
[name
, " finished in ", done ()]))
117 (messageStr (verb
, concat
[name
, " raised in ", done ()])
118 ; messageStr (verb
, concat
[name
, " raised: ", Exn
.toString e
])
119 ; (case Exn
.history e
of
122 (messageStr (verb
, concat
[name
, " raised with history: "])
126 messageStr (verb
, s
)))
133 type traceAccum
= {verb
: verbosity
,
137 val traceAccum
: (verbosity
* string) -> (traceAccum
* (unit
-> unit
)) =
140 val total
= ref Time
.zero
141 val totalGC
= ref Time
.zero
143 ({verb
= verb
, total
= total
, totalGC
= totalGC
},
144 fn () => messageStr (verb
,
152 val ('a
, 'b
) traceAdd
: (traceAccum
* string) -> ('a
-> 'b
) -> 'a
-> 'b
=
153 fn ({verb
, total
, totalGC
}, name
) =>
156 if Verbosity
.<= (verb
, !verbosity
)
158 val (t
, gc
) = time ()
161 val (t
', gc
') = time ()
163 total
:= Time
.+ (!total
, Time
.- (t
', t
))
164 ; totalGC
:= Time
.+ (!totalGC
, Time
.- (gc
', gc
))
170 (messageStr (verb
, concat
[name
, " raised"])
171 ; (case Exn
.history e
of
174 (messageStr (verb
, concat
[name
, " raised with history: "])
178 messageStr (verb
, s
)))
184 val ('a
, 'b
) traceBatch
: (verbosity
* string) -> ('a
-> 'b
) ->
185 (('a
-> 'b
) * (unit
-> unit
)) =
188 val (ta
,taMsg
) = traceAccum (verb
, name
)
191 (traceAdd (ta
,name
) f
, taMsg
)
194 (*------------------------------------*)
196 (*------------------------------------*)
198 val numErrors
: int ref
= ref
0
200 val errorThreshhold
: int ref
= ref
20
202 val die
= Process
.fail
205 fun msg (kind
: string, r
: Region
.t
, msg
: Layout
.t
, extra
: Layout
.t
): unit
=
208 val r
= Region
.toString r
209 val msg
= Layout
.toString msg
212 (concat
[String.fromChar (Char.toUpper (String.sub (msg
, 0))),
213 String.dropPrefix (msg
, 1),
216 outputl (align
[seq
[str (concat
[kind
, ": "]), str r
, str
"."],
223 fun warning (r
, m
, e
) = msg ("Warning", r
, m
, e
)
224 fun error (r
, m
, e
) =
226 val _
= Int.inc numErrors
227 val _
= msg ("Error", r
, m
, e
)
229 if !numErrors
= !errorThreshhold
230 then die
"compilation aborted: too many errors"
235 fun errorStr (r
, msg
) = error (r
, Layout
.str msg
, Layout
.empty
)
237 fun checkForErrors (name
: string) =
239 then die (concat
["compilation aborted: ", name
, " reported errors"])
242 fun checkFile (f
: File
.t
, {fail
: string -> 'a
, name
, ok
: unit
-> 'a
}): 'a
= let
243 fun check (test
, msg
, k
) =
247 fail (concat
["File ", name
, " ", msg
])
249 check (File
.doesExist
, "does not exist", fn () =>
250 check (File
.canRead
, "cannot be read", ok
))
253 (*---------------------------------------------------*)
254 (* Compiler Passes
*)
255 (*---------------------------------------------------*)
257 datatype 'a display
=
259 | Layout
of 'a
-> Layout
.t
260 | Layouts
of 'a
* (Layout
.t
-> unit
) -> unit
262 fun 'a
sizeMessage (name
: string, a
: 'a
): Layout
.t
=
264 in str (concat
[name
, " size = ",
265 Int.toCommaString (MLton
.size a
), " bytes"])
268 val diagnosticWriter
: (Layout
.t
-> unit
) option ref
= ref NONE
271 case !diagnosticWriter
of
275 fun diagnostic f
= diagnostics (fn disp
=> disp (f ()))
277 fun saveToFile ({suffix
: string},
280 d
: 'a display
): unit
=
283 trace (Pass
, "display")
285 (inputFile
, concat
[!inputFile
, ".", suffix
], fn () =>
286 File
.withOut (!inputFile
, fn out
=>
287 f (fn l
=> (Layout
.outputl (l
, out
)))))
293 (outputHeader (style
, output
)
294 ; output (layout a
)))
297 (outputHeader (style
, output
)
298 ; layout (a
, output
)))
301 fun maybeSaveToFile ({name
: string, suffix
: string},
304 d
: 'a display
): unit
=
305 if not (List.exists (!keepPasses
, fn re
=>
306 Regexp
.Compiled
.matchesAll (re
, name
)))
308 else saveToFile ({suffix
= concat
[name
, ".", suffix
]}, style
, a
, d
)
310 (* Code for diagnosing a pass
. *)
313 thunk
: unit
-> 'a
} =>
314 if not (List.exists (!diagPasses
, fn re
=>
315 Regexp
.Compiled
.matchesAll (re
, name
)))
319 val result
= ref NONE
322 ({suffix
= concat
[name
, ".diagnostic"]}, No
, (),
323 Layouts (fn ((), disp
) =>
324 (diagnosticWriter
:= SOME disp
325 ; result
:= SOME (thunk ())
326 ; diagnosticWriter
:= NONE
)))
331 (* Code for profiling a pass
. *)
334 thunk
: unit
-> 'a
} =>
335 if MLton
.Profile
.isOn
336 then if not (List.exists (!profPasses
, fn re
=>
337 Regexp
.Compiled
.matchesAll (re
, name
)))
342 val d
= Data
.malloc ()
345 (fn () => withData (d
, thunk
),
346 fn () => (Data
.write (d
, concat
[!inputFile
, ".", name
, ".mlmon"])
351 fun pass
{display
: 'a display
,
354 stats
: 'a
-> Layout
.t
,
356 thunk
: unit
-> 'a
}: 'a
=
358 val thunk
= wrapDiagnosing
{name
= name
, thunk
= thunk
}
359 val thunk
= wrapProfiling
{name
= name
, thunk
= thunk
}
360 val result
= trace (Pass
, name
) thunk ()
362 val _
= message (verb
, fn () => Layout
.str (concat
[name
, " stats"]))
364 val _
= message (verb
, fn () => sizeMessage (suffix
, result
))
365 val _
= message (verb
, fn () => stats result
)
366 val _
= message (verb
, PropertyList
.stats
)
367 val _
= message (verb
, HashSet
.stats
)
369 val _
= checkForErrors name
370 val _
= maybeSaveToFile ({name
= name
, suffix
= suffix
},
371 style
, result
, display
)
376 fun passTypeCheck
{display
: 'a display
,
378 stats
: 'a
-> Layout
.t
,
382 typeCheck
= tc
: 'a
-> unit
}: 'a
=
384 val result
= pass
{display
= display
,
392 then trace (Pass
, "typeCheck") tc result